h5 open optimization

With the continuous enhancement of mobile device performance, the performance experience of web pages has gradually become acceptable, and because of the many benefits of the web development model (cross-platform, dynamic update, volume reduction, infinite expansion), more and more APP clients appear. Embedded web pages (in order to match the current popular saying, all web pages are called H5 pages below, although they may not be related to H5), many APPs have changed some functional modules to use H5 implementation.


Although it is said that the performance of H5 pages has improved, if some optimizations are not made in a targeted manner, the experience is still very bad. There are two main parts of the experience:


1. White screen time when the page starts: Opening an H5 page requires a series of processing, there will be a period of white screen time, and the experience is bad.


2. Fluency of response: Due to webkit's rendering mechanism, single thread, historical burden and other reasons, the performance experience of page refresh/interaction is not as good as native.


This article will not discuss the second point, only the first point, how to reduce the white screen time. For some functional modules in the APP implemented using H5, how to speed up their startup speed and make their startup experience close to the original.


Process


Why is there a long white screen time when opening an H5 page? Because it does a lot of things, presumably:


Initialize webview -> request page -> download data -> parse HTML -> request js/css resources -> dom rendering -> parse JS execution -> JS request data -> parse rendering -> download rendering images


Some simple pages may not have the step of JS requesting data, but most functional modules should have. According to the current user information, JS requests relevant data from the background and then renders it, which is a conventional development method.


Generally, the page can display the prototype after dom rendering. Before that, the user sees a white screen. After downloading the rendered image, the entire page is displayed completely. The optimization of opening the first screen in seconds is to reduce the time-consuming of this process.


Front-end optimization


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


1. Reduce the amount of requests: merge resources, reduce the number of HTTP requests, minify/gzip compression, webP, lazyLoad.

2. Speed ​​up requests: 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 order, server-side rendering, pipeline.


Among them, the network request has the greatest impact on the startup speed of the first screen, so the focus of optimization is caching. Here we focus on the front-end caching strategy for requests. Let's subdivide it into HTML cache, JS/CSS/image resource cache, and json data cache.


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


In general, there are two caches:


1. Ask if there is an update: According to protocols such as If-Modified-Since / ETag, request the backend to ask if there is an update, if there is no update, 304 is returned, and the browser uses the local cache.


2. Directly use the local cache: According to the Cache-Control / Expires field in the protocol to determine how long it is possible to not send a request for an update, and use the local cache directly.


The maximum caching strategy that the front end can do is: HTML files always ask the server whether there is an update, and JS/CSS/Image resource files do not request updates, and use the local cache directly. How to update JS/CSS resource files? A common practice is to give each resource file a version number or hash value during the construction process. If the resource file is updated and the version number and hash value change, the URL of the resource request will change, and the corresponding HTML page will be updated and changed. When a new resource URL is requested, the resource is updated.


The cache of json data can use localStorage to cache the requested data. You can use the local data first, and then request the update, which is controlled by the front-end JS.


These caching strategies can realize full caching of resource files such as JS/CSS and cache of user data, and can directly use local cached data every time without waiting for network requests. But the cache of HTML files cannot be done. For HTML files, if the Expires / max-age time is set to a long time, and only the local cache is used for a long time, the update will not be timely. The network request asks whether there is an update, and then determines whether to use local resources. Generally, the front-end strategy here is to request every time. In the case of a weak network, the white screen time felt by the user will still be very long. So there is a contradiction between "caching" and "updating" HTML files.


Client-side optimization


Then it's the client's turn. The desktop era is limited by the browser, so the H5 page cannot be optimized. Now the H5 page is embedded in the client APP, and the client has more permissions, so the client can Go beyond the browser and do more optimization.


HTML caching


Let’s talk about caching first. There is a more free caching strategy on the client side. The client side can intercept all requests of H5 pages and manage the cache by itself. For the contradiction between the “cache” and “update” of the above HTML file, we can use This strategy solves:


1. Intercept the request on the client side, cache the data after requesting the HTML file for the first time, and use the cached data directly without sending the request for the second time.


2. When to request an update? This update request can be freely controlled by the client. You can use the local cache to open the local page and then initiate a request in the background to ask to update the cache, which will take effect the next time you open it; you can also initiate a request in the background when the APP starts or at a certain time. Updates to increase the chances of users accessing the latest code.


This looks perfect. The HTML file is cached using the client-side strategy, and the rest of the resources and data follow the above-mentioned front-end caching method. Such an H5 page accesses HTML to JS/CSS/Image resources for the second time, and then to data. All can be read directly from the local, no need to wait for network requests, and at the same time can maintain real-time updates as much as possible, solve the cache problem, and greatly improve the startup speed of the first screen of H5 pages.


question


The above solution seems to have solved the cache problem completely, but there are actually many more problems:


1. No preloading : The experience of opening it for the first time is poor, and all data is requested from the network.

2. Uncontrollable cache : The access to the cache is controlled by the system webview, and its cache logic cannot be controlled. The problems include:


  • The cleaning logic is uncontrollable, and the cache space is limited. After caching a few large images, important HTML/JS/CSS caches may be cleared.

  • Disk IO cannot be controlled and cannot be preloaded from disk to memory.

  • Poor update experience: When the background HTML/JS/CSS is updated, the full amount of data is downloaded, the amount of data is large, and the download on weak network takes a long time.

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


These problems can be solved on the client side, but they are a little troublesome. Briefly describe:


1. You can configure a preload list to request in advance when the APP starts or at certain times. This preload list needs to contain the pages and resources of the required H5 module, and also needs to consider the situation that an H5 module has multiple pages. This list can be quite large and also requires tools to generate and manage this preloaded list.


2. The client can take over the cache of all requests, without the default cache logic of the webview, and implement the cache mechanism by itself, which can divide the cache priority and cache preload.


3. Incremental updates can be done for each HTML and resource file, but it is more troublesome to implement and manage.

4. Use httpdns + https on the client to prevent hijacking.


The above solution is very cumbersome to implement. The reason is that each HTML and resource file is scattered and difficult to manage. There is a better solution to solve these problems, which is offline package.


offline package


Since many problems are caused by the difficulty of decentralized management of files, and our use scenario here is to use H5 to develop function modules, it is easy to think of packaging and releasing all relevant pages and resources of each function module. This compressed package can be called a function Offline package of modules. Using the offline package solution, the above problems can be solved relatively simply:


1. The entire offline package can be downloaded in advance. It only needs to be configured according to the business module, and does not need to be configured according to the file. The offline package contains all pages related to the business module and can be preloaded at one time.


2. The offline package core file and the page dynamic image resource file cache are separated, which can manage the cache more conveniently, and the offline package can also be loaded into the memory as a whole in advance, reducing the time-consuming of disk IO.


3. The offline package can be easily updated incrementally according to the version.


4. The offline package is delivered as a compressed package, and will be encrypted and verified at the same time, so operators and third parties cannot hijack and tamper with it.


So far, the offline package is a pretty good solution for using H5 to develop functional modules. Let’s briefly recap the offline package solution:


1. The backend uses the construction tool to package the pages and resources related to the same business module into a file, and encrypt/sign the file at the same time.


2. According to the configuration table, the client pulls down the offline package at a custom time, and performs decompression/decryption/verification work.


3. According to the configuration table, when opening a service, it will be transferred to the entry page of opening the offline package.


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


5. The offline packet data is returned, otherwise the HTTP protocol cache logic is used.

When the offline package is updated, the diff data between the two versions is delivered in the background according to the version number, and the client merges and incrementally updates.


more optimizations


The offline package solution has already done almost the same in caching, and it can also be equipped with some detailed optimizations:


public resource pack


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


preload webview


Whether it is iOS or Android, it takes a lot of time to initialize the local webview, and the webview can be initialized in advance. There are two types of preloading:


1. Preloading for the first time: Initializing the webview for the first time in a process is different from the second initialization. The first time will be much slower than the second time. The reason is expected to be that after the webview is initialized for the first time, even if the webview has been released, some global service or resource objects shared by multiple webviews have not been released, and these objects do not need to be regenerated during the second initialization to make it faster. We can pre-initialize a webview when the APP starts and then release it, so that it will be faster when the user actually goes to the H5 module to load the webview.


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


preload data


Ideally, when the offline package solution is opened for the first time, all HTML/JS/CSS use local cache, without waiting for network requests, but the user data on the page still needs to be pulled in real time. Here, an optimization can be done, while the webview is initialized. To request data in parallel, it takes some time to initialize the webview. There is no network request during this time. Parallel requests at this time can save a lot of time.


In terms of specific implementation, you can first indicate the URL that an offline package needs to be preloaded in the configuration table. The client initiates a request at the same time as the webview is initialized. The request is managed by a manager, and the result is cached when the request is completed, and then the webview starts after initialization. When requesting the preloaded URL just now, the client intercepts the request and forwards it to the request manager just mentioned. If the preloading has been completed, the content will be returned directly, and if not, it will wait.


Fallback


If the user accesses an offline package module, the offline package has not been downloaded, or the configuration table detects that there is a new version but the local version is an old version? Several options:


1. The simple solution is to block synchronously and wait for the latest offline package to be downloaded if the local offline package is not available or not up-to-date. This kind of user opening experience is even worse, because the offline package is relatively large.


2. It can also be that if there is an old package locally, the user will directly use the old package this time. If there is no re-synchronization blocking and waiting, this will lead to untimely updates and cannot ensure that users use the latest version.


3. You can also make an online version of the offline package. The files in the offline package have a one-to-one corresponding access address on the server. When there is no offline package locally, you can directly access the corresponding online address and open an online page with the traditional one. In the same way, this experience is better than waiting for the entire offline package to be downloaded, and it can also ensure that users have access to the latest.


The third method of Fallback also brings the benefit of the bottom line. In some unexpected situations, when the offline package is wrong, you can directly access the online version, and the function is not affected. In addition, if the public resource package is not updated in time, it can also be used when the version does not correspond. Direct access to the online version is a good bottom-line solution.


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


Using the client interface


If you use webkit's ajax and localStorage for network and storage interfaces, there will be many limitations and it is difficult to optimize. You can provide these interfaces to JS on the client side, and the client can do things like DNS pre-resolution/IP direct connection/long connection on network requests / Parallel requests and other more detailed optimizations, and storage can also use the client interface to perform targeted optimizations such as 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. In modern H5 pages, a lot of content already relies on JS logic to decide what to render, such as waiting for JS to request JSON data, and then splicing it into HTML to generate DOM rendering to On the page, so the rendering of the page has to wait for this whole process, there is a time-consuming, and reducing the time-consuming here is also within the scope of white screen optimization.


The optimization method can be to artificially reduce the JS rendering logic, or it can be more thorough, returning to the original, all content is determined by the HTML returned by the server, without waiting for the JS logic, which is called server-side rendering. Whether to do this kind of optimization depends on the business situation. After all, this will bring about the negative effects of changes in development mode/increased traffic/increased server overhead. Some pages of mobile QQ use server-side rendering, which is called dynamic straight-out.


finally


From front-end optimization, to client-side caching, to offline packages, to more detailed optimizations, to achieve the above points, H5 pages are almost comparable to the native experience in startup.


To sum up, the general optimization idea is: cache/preload/parallel, cache all network requests, try to load all the content before the user opens it, and do not serially do things that can be done in parallel. Some optimization methods here require a complete set of tools and process support, which need to be weighed against development efficiency and optimized according to actual needs.


In addition, the above discussion is an optimization scheme for H5 pages of functional modules to open in seconds. In addition to functional modules on the client APP, other H5 pages such as marketing activities/external access may have some optimization points that are not applicable. Depends on the situation and needs. In addition, WeChat mini-programs belong to the category of functional modules, which is almost the same routine.


The optimization of the startup time of the first screen of the H5 page is discussed here. After the above optimization, basically only the startup/rendering mechanism of the webview itself is time-consuming. This problem, together with the subsequent problem of response fluency, belongs to another optimization scope, that is Schemes like RN / Weex, have the opportunity to discuss again

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325955624&siteId=291194637