preface

For static resources on a page (HTML /js/ CSS /img/webfont), the ideal look is:

  1. Page at the fastest speed to get all the necessary static resources, rendering fast;
  2. The server is not requested when static resources on the server are not updated.
  3. When static resource update on the server requests the latest resource on the server, the load is fast.
In summary, there are two indicators:

  • Static resource loading speed
  • Page rendering speed
Static resource loading speeds lead us to today’s topic, because the most straightforward way is to cache static resources. Page rendering speed is based on resource loading speed, but the loading order and timing of different resource types also affect this, leaving more room for optimization.

Of course, in addition to speed, caching has two other benefits, reducing the bandwidth of user requests and reducing server stress.



Let’s start with a diagram that summarizes what we’ll be covering in this article.



Common cache types

1. Browser cache

For the front end, this is probably the most overlooked type of cache because most of the setup is done at the server operations level, not the maintenance side of front-end development. However, the content update timing of static resources is the most clear in the front end, if it can be properly configured based on the understanding of the browser cache policy.

Browser cache policies are usually defined by Response headers for resources, and HTML files were defined by http-equiv Meta tags in earlier specifications.



An example Response Header:



You can see all HTTP Response Header field definitions in the OFFICIAL W3C documentation. The main ones that are relevant to caching are the ones circled above:

  • Cache-control:
    • Public: The response is cached and shared among multiple users.
    • Private: the default, the response can only be used as a private cache (e.g., within a browser) and cannot be shared between users;
    • No-cache: The response is not cached, but requests resources to the server in real time.
    • Max-age: indicates the number of seconds between the request time and the expiration time, in seconds. Relative time interval based on request time (Date field) rather than absolute expiration time;
Note: HTTP/1.0 does not implement cache-Control, so Pragma fields appear for compatibility with HTTP/1.0.

  • Pragma: there is only one Pragma: no-cache, which works exactly the same as cache-control :no-cache. (Cache-control: no-cache was only provided by HTTP 1.1, so Pragma: no-cache can make no-cache applied to HTTP 1.0 and HTTP 1.1.)
  • Expires: Specifies how long a cached page Expires in the browser, equivalent to a max-age in cache-Control, or overwritten by a max-age in cache-Control if both exist. If set to 0, the page expires immediately. And if this property is set multiple times on the page, take its minimum value.
Note: This rule allows the source server to provide HTTP/1.1 (or later) caches with a longer expiration time than HTTP/1.0 for a given response.

  • Date: indicates the time and Date when the message is generated.
  • Last-modified/if-modified-since: indicates the time when the local file was Last Modified on the server. When the cache expires, the browser sends the last modified time of the cached page to the server. The server compares this time with the last modified time of the actual file on the server. If the time is consistent, 304 is returned and the client directly uses the local cached file.
  • Etag/ if-none-match :(EntityTags) is a URL tag that indicates whether the URL object has changed. It is usually the hash value of the resource entity. Like last-Modified, if the server verifies that the resource’s ETag has not changed (the resource has not been updated), it returns a 304 status telling the client to use the local cache file. Etag takes precedence over Last-Modified, and Etag is designed to solve problems that last-Modified cannot solve.
    • The file may change periodically, but its contents do not change and the client is not expected to get again.
    • If-modified-since The granularity that can be checked is s-grade;
    • Some servers do not know exactly when a file was last modified.


Cache policy execution process

After the local cache expires, the browser sends a request to the server. The request contains the following two fields:

  • If-modified-since: last-modified in response;
  • If-none-match: Etag in response (If present);
“File Modified?” on the right. The server reads the request headers to determine whether the resource cached by the client is up to date. If so, the server returns an HTTP/304 Not Modified response header with no response body. When the client receives the 304 response, it reads the corresponding resource from the cache. Otherwise HTTP/200 and the response body are returned.



Html Meta

Meta is an auxiliary tag in the HEAD section of THE HTML language, where the HTTP-equiv field defines some behavior of the server and user agent. In the previous specification, the http-equiv field of the meta had the following values similar to the HTTP Header caches related fields.

  • Cache-Control
  • Pragma
  • Expires
Usage:

<meta http-equiv="Cache-Control" content="no-cache"/ > <! --> <meta http-equiv="Pragma" content="no-cache"/ > <! -- Compatible with HTTP1.0 --> <meta http-equiv="Expires" content="0"/ > <! Set resource expiration time to 0 -->Copy the code
But these values have now been removed from the W3C specification field for a good reason:

Putting caching instructions into meta tags is not a good idea, because although browsers may read them, proxies won’t. For that reason, they are invalid and you should send caching instructions as real HTTP headers.
In fact, it is easy to understand, written in the meta tag means that the content of the HTML must be parsed, but the proxy server will not read. Most browsers don’t support this anymore and ignore it, so caching is set via HTTP headers.

HTTP Headers cache Settings have a higher priority than http-equiv in meta.





HTML5 Application Cache

Application Cache is one of the local storage solutions introduced in HTML5 that can be used to build offline caches. Currently supported by all browsers except Internet Explorer 10-.

Method of use



A. Add the manifest file

The application cache is managed through a mannifest file. The manifest file is a simple text file containing a list of files that need to be cached for offline use and file control that does not need to be cached or read cache failure.

  • The first line of the file must be CACHE MANIFEST
  • The line starting with # is the comment statement
  • The site cache cannot exceed 5M
  • File resource paths can be absolute or relative
  • Failure of any cache in the file list invalidates the entire cache
  • You can use the same Minifest file for your site or one per page
The file contains three instructions

  • CACHE: Resource files that need to be cached. The browser automatically caches the HTML page with the manifest attribute.
  • NETWORK: wildcard characters can be used for files that do not need to be cached.
  • FALLBACK: An alternative file that cannot access the cache file. Wildcard characters can be used.


B. Server configuration

Mannifest files can use any extension, but you need to add MIME type matches to the server. Using Apache is easier. If you use.manifest as an extension, add it to the Apache configuration file.

AddType text/cache-manifest .appcacheCopy the code


C. References in HTML

<html lang="zh" manifest="main.manifest">Copy the code
Note: Do not place the manifest file itself in the cache file list, otherwise the browser cannot update the manifest file. It is best to set the MANIFEST file to expire immediately in the HTTP headers section of the manifest file.



Cache loading and update process



1, events,

  • cached/checking/downloading/error/noupdate/obsolete/progress/updateready

2. Execution process

First load:

  • Creating Application Cache with manifest (Creating Application Cache with manifest); Creating Application Cache with manifest (Creating Application Cache with manifest);
  • Application Cache Checking Event
  • Application Cache Downloading Event (Downloading Cache files)
  • Application Cache Progress Event (0 of 4)
  • Application Cache Progress event (4 of 4)
  • Application Cache Cached event
Second load:

  • Document was loaded from Application Cache with manifest (read HTML files and other static resource files from the Cache for display)
  • Application Cache Checking Event Checking Event
    • If yes, download the cached file again for next access (does not affect the current browser display).
      • Application Cache Downloading Event (Downloading Cache files)
      • Application Cache Progress Event (0 of 4)
      • Application Cache Progress event (4 of 4)
      • Application Cache UpdateReady Event
    • no
      • Application Cache NoUpdate Event
Remove the MANIFEST file reference in HTML

  • Document was loaded from Application Cache with manifest (read HTML files and other static resource files from the Cache for display)
  • Application Cache Checking Event Checking Event
  • Application Cache Obsolete Event (delete all files from local Cache, no longer use Cache)


Some of the problems

  1. The Application Cache caches HTML documents that reference the manifest file by default, which is a pitfall for dynamically updated HTML pages (which can be avoided using tricky iframe embedding).
  2. If one resource in the cache list fails to load, all files will fail to be cached.
  3. If the resource is not cached and NETWORK is not set, it cannot be loaded. Therefore, wildcard configuration must be used in NETWORK.
  4. After the cache is updated, only the manifest file can be loaded for the first time. Other static resources need to be loaded for the second time to see the latest effect.
  5. The files in the cache manifest are not recached by the browser when they are updated.
    • Update the manifest file: Change the version number or date of the comment.
    • Through the Application Cache provided interfaces (Windows. ApplicationCache. SwapCache) to check for updates.
One last question, the standard has been removed from the Web standard…

This feature has been removed from the Web standards, and while some browsers still support it, it may be discontinued at some point in the future, so try not to use it. Using the application caching capabilities described here is highly discouraged at this point; It is in the process of being removed from the Web platform. Please use Service Workers instead.


3, PWA (Service Worker)

PWA is called Progressive Web Apps, and Service Worker is one of its core technologies.

Service worker is a programmable network proxy, allowing you to control how network requests from your page are handled.
Yes, this is the official proposal to replace the Application Cache. Back in 2014, the W3C published a draft of the Service Worker. It is a script running in the background as a separate thread. Its emergence enables Web App to have the ability of offline use, message push, background automatic update and so on similar to Native App.

However, it has the following limitations:

  • Unable to access DOM
  • The synchronization API cannot be used
  • HTTPS protocol required (http://localhost or http://127.0.0.1 is also available)
Browser support is not widespread right now, but it should be in the future. This article gives a brief introduction. For specific usage methods, please refer to The official document The Offline Cookbook.



Simple to use

1. First, to use the Service Worker, we need to add a Service Worker JS file and register the reference to this file in our HTML page.

index.html

<script>
navigator.serviceWorker
    .register('./sw.js')
   .then(function(registration) {// Registration successful}); </script>Copy the code


2. Second, we add the Service Worker’s life cycle events to the JS file. The Service Worker lifecycle consists of three parts: registration, installation, and activation.

There are generally three events we need to register:

self.addEventListener('install'.function(event) {/* After installation... */ // cache. AddAll: add cache files to the file, such as a.css,b.js}); self.addEventListener('activate'.function(event) {/* After activation... */ // caches. Delete: update cache file}); self.addEventListener('fetch'.function(event) {/* After requesting the resource... */ // cache. Put Intercepts the request directly returns the cached data});Copy the code
For retrieving files and caching files, the Service worker relies on two apis: Fetch (a standard way to retrieve content over the network) and Cache (a content store of applied data that is independent of browser Cache and network state).

Create-react-app has PWA built into the create-react-app folder.



Static /js/main.js is referenced in the index.html file, and service-worker.js is registered in main.js. In service-worker.js, we can see precacheConfig (cache list) and cacheName (version number) variables. Disconnect from the network and we see that the files in the precacheConfig list can still be loaded locally.



Update mechanism

Take the registration file service-worker.js as an example. Every time you visit a page controlled by ServiceWorker, the browser will load the latest service-worker.js file and compare it with the current service-worker.js file. The browser retrieves and installs the new file. This does not take effect immediately. The existing ServiceWorker is still running. A new ServiceWorker is activated only after all ServiceWorker pages are closed.





4, LocalStorage

LocalStorage is a browser-side cache, but how many people will use it to cache files? First of all, cache reading needs to rely on THE execution of JS, so the prerequisite is to be able to read HTML and JS code segments; Second, file version control brings more maintenance costs at the code level, so LocalStorage is better suited for critical business data rather than static resources.





5. CDN cache

This is a space for time scheme, reducing the user access delay, but also reduce the load of the source station.

The client browser first checks whether the local cache is expired. If so, it sends a request to the CDN edge node. The CDN edge node will detect whether the cache of user request data is expired. If the data is out of date, the CDN also needs to issue a back to source request to the source station.



Update mechanism

CDN edge node Cache policies vary with different service providers, but generally follow the HTTP standard protocol and set the CDN edge node data Cache time through the cache-Control: max-age field in the HTTP response header. In addition, the cache can be updated through the “refresh cache” interface provided by CDN service provider.

prebrowsing

Preloading is a browser indication of resources that may be used in the future, some of which may be used in the current page and some of which may be used in some future pages. As developers, we know more about our applications than browsers do, so we can use this technology for our core resources.
Prebrowsing can cache some files in advance, which can be used as a means of optimizing static resource loading. Prebrowsing has the following types:

  • Dns-prefetch: DNS prefetch, which tells the browser that we may fetch resources from a specific URL in the future. When the browser actually uses a resource in the domain, the DNS prefetch can be completed as soon as possible. This parameter is used when using third-party resources.
  • Preconnect: preconnect. DNS preresolution is completed, TCP handshake is performed, and transport layer protocols are established.
  • Prerender: Prerenders, preloads all the resources of a document, similar to opening a link in a hidden TAB page — downloads all the resources, creates DOM structures, completes the page layout, applies CSS styles, executes JavaScript scripts, and so on.
  • Prefetch: Prefetch. A resource declared using prefetch is a reminder to the browser, indicating that the resource may be used “in the future”. It is suitable for caching resources from other routing pages that may jump to. The loading time of a prefetch resource is determined by the browser, and generally has a low priority and is downloaded when the browser is “idle”.
  • Preload: indicates that the browser is proactively notified of the acquisition of key resources on the page.

prefetch & preload

Many of the first three browsers are already optimized internally by default, while Prefetch & Preload needs to be set manually by developers depending on the situation.



compatibility

from
prefetchand
preloadIn terms of browser support, prefetch is supported by all basic browsers except Safari, but Preload, as a new specification, is less compatible, but Safari is slowly supporting this standard, such as safari advanced options in iOS
Experimental Webkit functionalityThe Link Preload option is already available in.



priority

Preload is a declarative fetch that forces the browser to request resources without blocking the document onload event and instructs the browser to pre-request resources (key scripts, fonts, main images) for the current page.

Prefetch informs the browser that the resource may be needed in the future, but leaves the decision of whether and when to load the resource up to the browser. Prefetch is used in a slightly different scenario — resources that the user may use in other parts of the application, such as views or pages.

As you can see from the above description, preload is significantly higher than preFETCH for preload and preFETCH declarations.



Note: Prebrowsing is easy to use, but it is important not to use it too easily unless you know that you want to load a file that is browsing easily.



application

For those of you who have touched next.js, you know that next.js provides a module with prefetch function: Next/Prefetch, which appears to have similar functions to Prefetch but has a similar priority to Preload.

<Link prefetch href='/'><a>Home</a></Link>

<Link prefetch href='/features'> <a>Features</a></Link>

{ /* we imperatively prefetch on hover */ }
<Link href='/about'>
  <a onMouseEnter={() => { Router.prefetch('/about'); console.log('prefetching /about! ') }}>About</a>
</Link>

<Link href='/contact'><a>Contact (<small>NO-PREFETCHING</small>)</a> </Link>Copy the code

As prefetch is set for the Features link, when accessing the Index page, the browser will fetch the feature.js file from the server after the page is loaded. When the Index page accesses the Features page, the browser will not request the feature.js file from the server, but directly reads it from the local cache. Contact is not handled. The concact.js file is requested from the server when contact is accessed from index.

We can also find that index.js/error.js/app.js files will be preloaded in the header of the HTML file packaged by next.js, because these three files are necessary resources in this page.

Optimization of trying

Different file types



1. HTML files

Although most HTML changes only when a post is published, such as updating the reference address of js/ CSS resources, it is generally recommended to set HTTP Headers to a short max-age value, such as cache-control: max-age=300. Otherwise, Etag is recommended for the server.

However, in order to open the page faster, websites with real-time content (such as finance) will adopt the way of background service production, and all the home page data will be generated into HTML, saving the waiting time of background interface request when users load the first time. Cache-control: no-cache is usually set.



2, js/ CSS /img file

Versioning is now generally done by filename. Webpack naming generates hash values for file names based on file contents, and regenerates hash values only when the contents are changed. In this case, you can set the HTTP Headers to a larger cache time, such as max-age=2592000, to avoid the connection between 304 requests and the server.

// js
output: {
    path: config.build.assetsRoot,
    filename: utils.assetsPath('js/[name].[chunkhash].js'),
    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js'),
}
// css
new ExtractTextPlugin({
    filename: utils.assetsPath('css/[name].[contenthash].css'),}),Copy the code


3, webfont

Webfont files are special, as explained in this article:

  • The web fonts file will be downloaded only when the browser finds @font-face in the CSS selector of DOMNode. At this point, the browser has already downloaded the HTML/CSS/JS file.
  • Telling the browser to download the font file before the browser knows it needs to load it will speed up the file download and page load.
Font files are loaded at different times in different browsers. Some are loaded when the CSS declaration is encountered, while others wait until the DOM node matches the CSS declaration.



To optimize the practice

Optimize a current mobile project based on the cache recommendations listed above. The project background is as follows:

  • React + + Mobx + Webpack
  • React-router single-page/bundle-loader dynamically loads/uses large WebFONT files
1. Cache configuration

  • Do the HTTP Headers cache configuration above for static resource files;
  • All static resource files are controlled by Service Worker cache and loaded offline.
2. Other optimizations

Taking a single page as an example, the page looks like this:




Dynamically loaded JS

The single-page page opens several smaller pages (circled in red) that look something like this when packed with Webpack:

  • index.ef15ea073fbcadd2d690.js
  • The static/js / 0.1280 b2229fe8e5582ec5. Js
  • static/js/1.f3077ec7560cd38684db.js
  • The static/js / 2.39 ecea8ad91ddda09dd0. Js
  • static/js/3.d7ecc3abc72a136e8dc1.js
The first index.js will be loaded on the page for the first time, and the other four js will be loaded dynamically during route switching. Consider the business scenario for this page. Once you enter this page, several other routes are bound to be accessed. So if after the page loading is completed, while the user is thinking about the initiative to load the remaining several JS, is not perfect.

We chose the preload-webpack-plugin, which can be packaged to preload dynamic routes.

webpackConfig.plugins.push(new PreloadWebpackPlugin({
    rel: 'prefetch',}));Copy the code


The REL property can also select the preload/Prefetch mode. It comes out like this:

When visiting the page, you can see that, without affecting dom loading, the browser preloads several other JS that will be used later. When switching to the corresponding route, it will also directly access from cache, rather than request resources from the server.



The CSS file

CSS that are not dynamically loaded (routed) pages are packaged separately and referenced in HTML files. In addition to the use of some packaging plug-ins to optimize the code volume, the CSS can be more granular separation, such as the home page CSS + pop-up CSS + page label switching CSS. In addition to the home page CSS preloading, then dynamic acquisition. But generally speaking, the CSS size of a page is not too large after gzip compression in the case of reasonable code, so the optimization effect is not too obvious.

Dynamic load routing CSS is not split separately but in the js of the route, so it can only be optimized with JS.



Webfont file

For font files, in addition to reducing the file size and setting the cache time, the browser can also be preloaded in advance to improve the first screen rendering speed. Preloading webFont needs to be combined with webpack’s HTMl-webpack-plugin, which inserts the specified font into THE HTML when packaging. After searching the Internet for a ready-made plug-in, I wrote one myself.



1. Write plug-ins

fontpreload-webpack-plugin

2. Use plug-ins

  • Installing a plug-in
npm install fontpreload-webpack-plugin --save-devCopy the code
  • Add the following HtmlWebpackPlugin to webpack’s config file:
const FontPreloadWebpackPlugin = require('fontpreload-webpack-plugin');Copy the code
webpackConfig.plugins.push(new FontPreloadWebpackPlugin({
    rel: 'prefetch',
    fontNameList: ['fontawesome-webfont'],
    crossorigin: true,}));Copy the code


3. Packaging effect





This is the end of this article, if there are mistakes welcome to correct.