Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image assets cache purge question #3959

Closed
mkoczorowski opened this issue Jan 11, 2017 · 17 comments
Closed

image assets cache purge question #3959

mkoczorowski opened this issue Jan 11, 2017 · 17 comments

Comments

@mkoczorowski
Copy link

Hi there,

I noticed that all assets are copied over to the dist directory. However even if we change the image in src/assets directory. The image wont be pulled in after release as name is the same.
It would be nice if during a release we update urls in css/html simply appending version number of sort in css such as:

url(assets/logo.png) -> url(assets/logo.png?123)

this would force the browser to download new image. Is this something we can do with cli ?

Thanks for the help

@Meligy
Copy link
Contributor

Meligy commented Jan 13, 2017

There's something in master not yet released.

A new flag for ng build that's called output-hashing. It takes one of the following values: none, all, media, bundles.

I suspect you can try setting it to all whenever the next version is released.

P.S.

Reference: #3885

@hansl hansl closed this as completed Jan 13, 2017
@un33k
Copy link

un33k commented Jan 24, 2017

@Meligy what you have found in master is --output-hashing which is available as of beta.26 and apparently doesn't touch the assets directory and its content as per this.

@adamdport
Copy link

adamdport commented Jan 26, 2018

@hansl @Meligy A year later, it looks like --output-hashing has no effect on anything in assets. The entire directory is just copied into the build folder without any means of cache invalidation. Should this be reopened?

@macleanpinto
Copy link

@hansl @Meligy

ng build --env=prod --output-hashing=all

The entire directory is copied into the build folder. According to the build tests https://github.com/angular/angular-cli/blob/master/tests/e2e/tests/build/output-hashing.ts

export default function() {
return Promise.resolve()
.then(() => writeMultipleFiles({
'src/styles.css': 'body { background-image: url("./assets/image.png"); }'
}))
// use image with file size >10KB to prevent inlining
.then(() => copyProjectAsset('images/spectrum.png', './src/assets/image.png'))
.then(() => ng('build', '--output-hashing=all'))
.then(() => expectFileToMatch('dist/test-project/index.html', /runtime.[0-9a-f]{20}.js/))
.then(() => expectFileToMatch('dist/test-project/index.html', /main.[0-9a-f]{20}.js/))
.then(() => expectFileToMatch('dist/test-project/index.html', /styles.[0-9a-f]{20}.(css|js)/))
.then(() => verifyMedia(/styles.[0-9a-f]{20}.(css|js)/, /image.[0-9a-f]{20}.png/))

The defect needs to be reopened.

@clydin
Copy link
Member

clydin commented Apr 10, 2018

Assets are intended to be copied verbatim with no additional processing. That is their defining characteristic.
The test above is also turning the ./assets/image.png file into a resource (used by a global stylesheet). Resources, in contrast to assets, are processed by the system and will be potentially inlined, optimized, and/or hashed depending on the options provided. In practice, an application would generally not have an file be both an asset and a resource as this would cause the file to be present twice within the build output. Once as a directly copied asset and once as a processed resource.

@macleanpinto
Copy link

macleanpinto commented Apr 10, 2018

"assets": [
"assets",
"favicon.ico",
"resources",
"i18n"
]

are the files mentioned as assets in angular-cli.json

I18n contains 2 files

  1. en.json
  2. fr.json

I am using them for translations.

These files are cached. As per the documention

output-hashing=all

must add hashing to all files, irrespective of contents in assets.

@adamdport
Copy link

My team ended up appending a timestamp of the release onto our assets, eg. en.json?v=123456.

@clydin If assets were intentionally not hashed, what is the suggested approach for invalidating cache of assets?

@jeffora
Copy link

jeffora commented Nov 19, 2018

@clydin I would also love to know what the recommended practice is for this

@clydin
Copy link
Member

clydin commented Nov 21, 2018

Please see the following for recommended practices. Note that server configuration is a critical element to an effective caching strategy.
https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

@jeffora
Copy link

jeffora commented Nov 21, 2018

Thanks for the article @clydin. I totally agree that correct cache configuration on the server is important for ensuring browsers treat cached assets correctly. However, a common strategy to ensure the browser immediately requests a new version when an asset has changed is to use unique names for assets based on their contents. The article you linked specifically calls this out:

How do you get the best of both worlds: client-side caching and quick updates? You change the URL of the resource and force the user to download the new response whenever its content changes. Typically, you do this by embedding a fingerprint of the file, or a version number, in its filename—for example, style.x234dff.css.

With the behaviour of angular just copying the assets with no processing, no hash is generated for these assets, making it not possible to achieve this, and it was this I was hoping you had advice on.

@clydin
Copy link
Member

clydin commented Nov 21, 2018

@jeffora Further in that same section, the example specifically using an image (as a token asset example) without a unique hash in the file name. max-age combined with ETags are highly effective for image/video/etc. assets.

@jeffora
Copy link

jeffora commented Nov 22, 2018

Sure, for a photo that rarely or never changes, where the user experience will not change with an out of date image. However, if the asset is an SVG for a menu item, or something that should be considered part of a single "deployment", there is no way to make sure a new version of the app will reference the latest version of the asset.

Another issue is that as assets are not processed at all, they do not follow the --deploy-url semantics, so if all assets are uploaded to a CDN at a different domain name, the image assets maintain their relative paths to the host domain.

@clydin
Copy link
Member

clydin commented Nov 22, 2018

In that case, you have several options. Three of which are to either set the image to no-cache (like the index) and ensure Etags are being used; with HTTP/2 there is no reconnection overhead. Or if the SVG is small, inline it in the template HTML. Or use a CSS background image; which has numerous advantages over an HTML img element (e.g., transformations, animations, etc.).

As to the second aspect, base HREF would be the preferred option in that scenario. There is rarely a reason to use deploy URL over base HREF.

@jeffora
Copy link

jeffora commented Nov 22, 2018

Ok thanks for the discussion. It sounds like there is no out of the box support via the angular cli, so I'll look into 6+ builder pack options to customise it ourselves.

Base href is not really appropriate for this use case, as that tells the browser that all relative URLs on the page should be interpreted as relative to the base href, which is not the behaviour we're after. We serve the site (index.html) from one domain, e.g. site.example.com, and all relative URLs should be relative to that domain (e.g. relative AJAX calls to site.example.com/someapi). All JS/CSS/image assets are uploaded to a CDN hosted at a different domain, e.g. cdn.example-static.net, for a variety of reasons - one of these being that this prevents the browser from sending unnecessary cookies to the server when retrieving static assets.

The other options suggested might work for specific cases, but they all feel like workarounds - surely there is a valid (and possibly common) use case where static assets that are not JS / CSS should be processed through the same content pipeline as JS / CSS. This is fully supported by webpack, so it would be nice to have an easy way to opt in to this functionality in angular.

@clydin
Copy link
Member

clydin commented Nov 22, 2018

With HTTP/2’s header compression, the entire cookie can be encoded in 1 to 2 bytes for repeat requests. There’s no need for a complex setup if cookie request elimination is the primary goal.

@edertone
Copy link

edertone commented Jan 4, 2019

I really thought that an absolutely amazing framework like angular would take care of such a basic thing. A bit disappointing to see that assets are not hashed to prevent them from being stuck on cache... but I hope to see this improvement some time.

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 9, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants