Author: Bang, senior wireless development expert of Ant Financial

Ali sister introduction: More and more APP business using H5 way to achieve, how to make H5 page start faster is a lot of people in the exploration of technical points, this article combs the various points in the startup process, respectively from the front-end and client perspective to discuss what optimization plan, for everyone’s reference.

With the increasing performance of mobile devices, the performance experience of Web pages is becoming more and more acceptable, and because of the many benefits of web development mode (cross-platform, dynamic update, reduced size, unlimited expansion), more and more embedded Web pages appear in APP clients (to match the current popular saying, I will refer to all pages as H5 pages, although it may not be related to H5.) Many apps have changed some function modules to H5 implementation.

Although the performance of H5 pages is better, the experience is still terrible without some targeted optimization. There are two main parts of the experience:

1. Page startup white screen time: opening an H5 page requires a series of processing, and there will be a period of white screen time, and the experience will be terrible.

2. Response fluency: Due to webKit’s rendering mechanism, single thread, historical baggage and other reasons, page refresh/interaction performance experience is not as good as native.

This article will not discuss the second point, but the first point, how to reduce the white screen time. How to speed up the startup speed of some functional modules in APP implemented by H5, so that their startup experience is close to original.

process

Why does an H5 page have a long blank screen? Because it does a lot of things, like:

Initialize webView -> request page -> Download data -> parse HTML -> request JS/CSS resources -> DOM render -> parse JS execution -> JS request data -> parse render -> download render image

Some simple pages may not have JS request data this step, but most functional modules should have, according to the current user information, JS to the background request relevant data and then render, is a conventional development way.

Generally, the prototype of the page can be displayed after DOM rendering. Before this, the user sees a white screen, and the whole page is displayed only after downloading the rendered image. The optimization of the first screen is to reduce the time consuming of this process.

The front-end optimization

The above process of opening a page has many optimization points, including front-end and client side. General front-end and back-end performance optimization has been best practices in the PC era. The main ones are:

1. Reduce request volume: merge resources, reduce HTTP requests, Minify/gzip compression, webP, lazyLoad.

2. Speed up the request: pre-resolve DNS, reduce the number of domain names, parallel loading, CDN distribution.

3. Cache: HTTP protocol cache request, offline cache MANIFEST, offline data cache localStorage.

4. Rendering: JS/CSS optimization, loading sequence, server-side rendering, pipeline.

Among them, network requests have the greatest impact on the speed of the first screen startup, so the focus of optimization is caching, and here we focus on the caching strategy of the front-end for requests. Let’s break it down into HTML caches, JS/CSS/ Image caches, and JSON data caches.

HTML and JS/CSS/image resources are static files. HTTP provides caching protocols and browsers implement these protocols to cache static files.

In general, there are two kinds of caches:

If there is no update, 304 is returned. If there is no update, the browser uses local cache.

2. Use the local Cache directly: Use the cache-Control/Expires field in the protocol to determine how long you can use the local Cache instead of sending a request for an update.

The maximum cache strategy that the front-end can do is: HTML files ask the server for updates every time, JS/CSS/Image resource files do not request updates, and directly use the local cache. How are JS/CSS resource files updated? A common practice is to give each resource file a version number or hash value during the build process. If the resource file is updated, the version number and hash value change, the URL of the resource request changes, and the corresponding HTML page is updated to request the new resource URL, and the resource is updated.

Json data cache can use localStorage to cache the requested data, you can use local data for the first display, and then request updates, which are controlled by front-end JS.

These cache policies can achieve full cache of resource files such as JS and CSS and user data. Local cache data can be directly used every time without waiting for network requests. If you set Expires/max-age to a long time and only use the local cache for a long time, then the update won’t be timely. If you set Expires/max-age to a long time, you’ll have to send a network request every time you open a page to ask if there’s been an update and then decide whether to use local resources. The general front end strategy here is to request every time, which is still a long time for the user to experience the white screen in a weak network situation. So there is a contradiction between “caching” and “updating” HTML files.

Client optimization

Then it was the turn of the client. In the desktop era, limited by the browser, the H5 page could not be optimized more. Now the H5 page is embedded in the client APP, and the client has more permissions, so the client can go beyond the scope of the browser and do more optimization.

The HTML cache

The client can intercept all requests of the H5 page and manage the cache by itself. For the contradiction between “cache” and “update” of HTML files, we can use this strategy to solve the problem:

1. The client intercepts the request and caches the data after the first REQUEST for an HTML file. The client does not send the request for the second time and directly uses the cached data.

2. When to ask for updates? This update request can be the client free control policy, you can use the local cache to open the local page and then in the background to initiate a request to update the cache, the next time to open the effect; You can also initiate a request for pre-update in the background when the APP starts or at a certain time to improve the probability of users accessing the latest code.

HTML files are cached by client strategy, and other resources and data are cached by the above front-end. In this way, when an H5 page accesses HTML, JS/CSS/Image resources and data for the second time, it can be read directly from the local without waiting for network requests. At the same time, it can maintain real-time update as much as possible, solve the cache problem, and greatly improve the speed of the first screen of the H5 page startup.

The problem

The above solution seems to solve the caching problem completely, but there are many problems:

1. No preloading: The first open experience was poor and all data had to be requested from the network.

2. Uncontrollable cache: The access of the cache is controlled by the webview of the system, and its cache logic cannot be controlled, which brings the following problems:

  • The cleanup logic is out of control and the cache space is limited. After a few large images are cached, the important HTML/JS/CSS cache is cleared.

  • Disk I/O cannot be controlled and data cannot be preloaded from disk to memory.

  • Poor update experience: The background HTML/JS/CSS updates are downloaded in full, resulting in a large amount of data and a long time to download from the weak network.

  • Unable to prevent hijacking: If the HTML page is hijacked by a carrier or a third party, the hijacked page will be cached for a long time.

All of these problems can be solved on the client side, but it is a bit troublesome.

1. A pre-loaded list can be configured to be requested in advance when the APP is started or at some time. This pre-loaded list should contain the pages and resources of the REQUIRED H5 module.

2. The client can take over the cache of all requests, do not follow the default cache logic of WebView, and realize the cache mechanism by itself, which can be divided into cache priority and cache preloading.

3. Incremental updates can be made for each HTML and resource file, but implementation and administration are cumbersome.

4. On the client, use HTTPDNS + HTTPS to prevent hijacking.

The above solution is very cumbersome to implement, the reason is that many HTML and resource files are scattered, difficult to manage, there is a better solution to solve these problems, is the offline package.

The offline package

Since many problems are caused by the difficulty of decentralized file management, and our use scenario here is to use H5 to develop functional modules, it is easy to think of all the relevant pages and resources of each functional module packaged and delivered, this compressed package can be called the offline package of functional modules. Using the offline package solution, these problems can be solved relatively easily:

1. You can download the entire offline package by service module, not by file. The offline package contains all pages related to service modules and can be preloaded at one time.

2. The offline package core file is separated from the dynamic image resource file cache, facilitating cache management. The offline package can also be loaded into the memory in advance, reducing disk I/O time.

3. Offline packages make it easy to make incremental updates based on version.

4. Offline packages are delivered as compressed packages, encrypted and verified, and cannot be hijacked or tampered with by carriers or third parties.

So far, the offline package is a good solution for developing functional modules using H5. Briefly recap the offline package solution:

1. The back-end uses the build tool to package the pages and resources related to the same service module into a file, and encrypts/signs the file.

2. According to the configuration table, the client pulls down the offline package to decompress, decrypt, and verify the package.

3. According to the configuration table, a service is forwarded to the entry page for opening the offline package.

4. Intercept network requests and directly read files existing in offline packages

5. The offline package data is returned. Otherwise, HTTP cache logic is used.

During offline package update, diFF data between two versions is delivered in the background according to the version number, and clients are merged and incremental updated.

More optimization

The offline package scheme is pretty much done with caching, but it can also be optimized with some details:

Common Resource pack

Each package will use the same JS framework and CSS global style. It is too wasteful to repeat these resources in each offline package. You can create a common resource pack to provide these global files.

Preload webview

Whether iOS or Android, local webView initialization takes a lot of time, can be pre-initialized webView. There are two types of preloading:

1. First preload: Initializing a WebView for the first time in a process is not the same as initializing it for the second time, which is much slower than the second time. The reason is expected to be that after the initial initialization of the WebView, even though the WebView has been released, some global service or resource objects shared by multiple WebViews are still not released, so the second initialization does not need to regenerate these objects to make them faster. We can pre-initialize a WebView at APP startup and release it so that when the user actually goes to the H5 module to load the WebView it will be faster.

2. Webview pool: You can reuse two or more WebViews instead of creating a new webview every time you open H5. However, this way to solve the problem of clearing the previous page when the page jumps, in addition, if there is a MEMORY leak in JS on an H5 page, it will affect other pages and cannot be released during the running of the APP.

Preloaded data

Ideally, when the offline package is first opened, all HTML/JS/CSS will be cached locally without waiting for network requests, but the user data on the page still needs to be pulled in real time. We can make an optimization here, and request the data in parallel while the WebView initialization, which takes some time. There are no network requests at this time, so parallel requests at this time can save a lot of time.

The client initiates a request when the WebView is initialized, the request is managed by a manager, and the result is cached when the request is completed. Then the WebView requests the URL that has just been preloaded after the initialization, and the client intercepts the request. Go to the request manager mentioned earlier and return the content directly if the preload is complete, or wait if it is not.

Fallback

What if a user accesses an offline package module and the offline package has not been downloaded, or if the configuration table detects a new version but the local version is an old one? Several options:

1. A simple solution is that if the local offline package is not up to date or not available, block and wait for downloading the latest offline package. This user opening experience is even worse because of the larger size of the offline package.

2. If there is an old package on the local PC, the user directly uses the old package this time. If there is no resynchronization blocking, the update will not be timely and the user cannot use the latest version.

3. Can also do to offline package an online version of the offline package files on the server has a one-to-one access address, no offline package in local, direct access to the corresponding online address, just like traditional opens a page online, this experience relatively better waiting for download the offline package, also can ensure the user to access to the latest.

The third Fallback method also brings benefits to the bottom. In some unexpected situations, when the offline package goes wrong, the online version can be directly accessed without affecting the function. In addition, when the version does not correspond due to the delayed update of the public resource package, the online version can also be directly accessed, which is a good bottom solution.

The above solution strategies can also be mixed, depending on the business requirements.

Use the client interface

Network and storage interface if the use of WebKit ajax and localStorage will have many limitations, it is difficult to optimize, you can provide these interfaces to JS in the client, the client can do on the network request like DNS pre-resolution /IP direct connection/long connection/parallel request and other more detailed optimization, Storage also uses client interfaces to optimize read/write concurrency/user isolation.

Server side rendering

In early Web pages, JS was only responsible for interaction, and all content was directly in HTML. To modern H5 pages, many contents already rely on JS logic to decide what to render, such as waiting for JSON data requested by JS, and then stitching it into HTML to generate DOM rendering on the page. So the page rendering display has to wait for the whole process, there is a time here, reduce the time here is also within the scope of white screen optimization.

Optimization can be done by artificially reducing THE JS rendering logic, or more radically, going back to the original, where everything is determined by the HTML returned by the server without waiting for the JS logic, called server rendering. Whether or not to do this optimization depends on the business situation, since it can lead to changes in development patterns/increased traffic/increased server-side overhead. Some of the pages in Hand Q are rendered using a server-side method called dynamic straight out.

The last

From front-end optimizations, to client caching, to offline packages, to more detailed optimizations, H5 pages start up almost as good as the native experience.

To sum up, the general optimization idea is: cache/preload/parallel, cache all network requests, try to load everything before the user opens it, and do not serial things that can be done in parallel. Some of these optimizations require a complete set of tools and processes to be supported, balanced against development efficiency and optimized according to actual needs.

In addition, what is discussed above is the optimization scheme for the second opening of H5 pages of functional modules. In addition to functional modules, some optimization points of other H5 pages, such as marketing activities or external access, may not be applicable on the client APP, which also needs to be determined by the actual situation and demand. In addition, wechat small program belongs to the category of functional modules, almost this routine.

Here discussed the H5 page first screen start time optimization, after the optimization, basically time-consuming only webView itself start/render mechanism problem, this problem with the follow-up response fluency problem belongs to another optimization scope, is class RN/Weex such a scheme, have the opportunity to discuss again.

About the author: Bang, currently working for Ant Financial, focuses on iOS and front-end field, mainly responsible for iOS infrastructure, including small program exploration. JSPatch, personal blog, blog.cnbang.net