Mini Program Loading Performance Optimization Practice

Posted May 27, 202010 min read

Foreword

For Internet products, the first impression is the startup speed of the application. Although the user will not have a great sense when starting fast enough, if it is slow, it will be found and will be challenged. In summary, fast is the way it should be.

The application startup speed optimization can be divided into first startup speed optimization and second startup speed optimization. For different types, the corresponding optimization schemes are also completely different. How to determine the optimization direction and priority, it is necessary to start from a specific business scenario.

Business scene

But it is not professional enough to talk about optimization directly without talking about business scenarios.

This optimization practice is mainly based on the auto insurance business of Micro Insurance.

As we all know, auto insurance is generally a one-year insurance. Therefore, the frequency of visits by auto insurance users is very low, once a year. Secondly, due to regulatory requirements, users must complete real-name certification before insuring car insurance; only after the certification is passed can they add vehicles to be insured. Therefore, if a user wants to complete the insurance, he must go through the steps of real-name authentication, adding a vehicle, quotation, payment, and completing the insurance.

In addition, in order to optimize the problem of low user access frequency, the platform usually provides some essential services for car owners. To increase user activity.

It can be seen that the business matrix of Micro Insurance is roughly as follows:

Compared with the car owner service, the performance of the main process is more important. After all, the process involving the transaction is the key. Therefore, this summary is also based on main process optimization practice.

Optimization direction

The vast majority of car owner users own only one car. Therefore, there is only one opportunity to purchase auto insurance once a year. For such a low-frequency product, every time the user comes, it is a brand new UI and brand new code. It is equivalent to starting for the first time every time you come, and at this time first start speed is extremely important. This is also the main direction of this optimization.

We refer to the RAIL performance model goals proposed by Google.

  • User-centric; the ultimate goal is not to make your website run fast on any particular device, but to satisfy most users.
  • Respond to users immediately; confirm user input within 100 milliseconds.
  • When setting up animation or scrolling, frames are generated within 10 milliseconds.
  • Maximize the idle time of the main thread.
  • Continue to attract users; present interactive content within 1000 milliseconds.

To sum up, our first optimization principle is to ensure that the vast majority of users can access our applications faster.

Ready to work

Before embarking on performance optimization, there is an important preparation. That is to collect the benchmark data in advance. With benchmark data, you can measure your own optimization results.

Although the WeChat public platform provides charts for time-consuming startup and time-consuming downloads for applets, it cannot provide original data and is not conducive to detailed analysis. Therefore, this performance optimization uses the data provided by the self-built log reporting system.

It is not enough to determine the performance indicators and obtain relevant data.

Because the actual network environment is very complicated, the original data collected is very rough, the same code, the same configuration, in different network environments and mobile phones, the loading time will vary greatly. Therefore, for the collection of various extreme data, certain processing must be done before it can be analyzed:

The common processing treatments(index calculation caliber) are mainly as follows:

  • Average: The most straightforward idea is to calculate the average of all data. However, the average will be too large due to some extreme values, and the data will fluctuate greatly, so the average is not suitable as a reference indicator.
  • 95 quantile: Sort all data from small to large, take the value at the 95th position, and use this value as our performance indicator.
  • Censored average: All data is also arranged in sequence, part of the data at the tail is removed, and then the average value is taken. This value will be relatively stable and is more suitable as a reference indicator.

Combined with the first optimization principle mentioned above, it is obvious that censored average is the best choice for calculating the caliber of this indicator.

For example, suppose we promise to ensure that 90%of users complete the loading of applets within 1000ms, then we only need to pay attention to whether the loading time of the first 90%of users is within 1000ms.

Obtain the average loading time-consuming data of the first 50%, 80%, and 90%users with less time-consuming through truncation(subsequent horizontal analysis of the optimization effect of loading time-consuming of these three types of users)

After processing the data, when performing performance analysis, you can perform classification analysis as follows:

  • Classified by mobile phone system(Android, iOS)
  • Classified by network environment(WIFI and 4G)

The following is the collection of 4G network, which does not distinguish the time-consuming loading of auto insurance subcontracting of mobile phone system(as baseline data before optimization):

practice

Since applets provide many basic APIs and UI libraries, this part of the code is a dependency of each applet. Therefore, when the code is initialized, this is the first to be initialized, and the rendering process is roughly as follows:

Compared with the traditional web, we can optimize in many aspects. For example, the resource loading order(lazy loading of images, pre-loading of CSS, post-loading of JS, etc.), using the build tool to achieve on-demand loading, etc. However, the underlying logic of the applet is relatively closed, and we cannot participate deeply. Therefore, to optimize the loading performance of the applet, the most effective solution that can be done is to optimize the size of the applet code package.

There are mainly the following schemes to optimize the code package size:

  • Remove unused code
  • Realize the code is loaded on demand(in the small program, it is to use the sub-package loading scheme)
  • Remove the picture resource in the mini-package(because the mini-package is downloaded, gzip compression is used by default, and the compression efficiency of non-text is low)
  • Simplify JavaScript and try to avoid complex calculations on the front end(migrate JS logic to the back end as much as possible or use Nodejs mid-stage to complete this part of the calculation, which will also facilitate subsequent expansion to multiple platforms)

Remove unused code

Since auto insurance is the first product launched by Micro Insurance, it has undergone countless multiple iterations, and it is inevitable that there will be offline services or functions. This part of the code is generally in units of pages, so you can find this part of the code by combining PV data.

When it comes to PV data, by the way, how to build a log reporting system by yourself. Each page of the applet has a complete life cycle, so when a new page is entered, the onLoad method of the Page will be triggered. At this time, by reporting the information of the entered page to the background service, the page PV data statistics can be completed.

There are two ideas for how to implement error path redirection:

  1. The routing capability provided by the applet(such as wx.navigateTo), when the incoming URL does not exist, the fail method will be triggered, and then it can be processed under fail.
  2. Use wx.onPageNotFound method, but there are certain compatibility issues.

After removing unused codes, auto insurance subcontracting was reduced from 1100 + KB to 900 + KB(18%reduction)

Code is loaded on demand

Ideally, accessing users should only download resources that have been accessed, and other resources should only be downloaded when needed. In this on-demand loading scheme, applets are handled in code subcontracting. However, since sub-contracting and sub-contracting cannot refer to each other's resources and code cannot be reused, splitting sub-contracting will inevitably lead to a decrease in development efficiency. Therefore, when splitting subcontracting, we must grasp the balance between performance and efficiency.

Therefore, we have proposed the principle of subcontracting and splitting based on UV and access path **.

Combined with the log system, we found that the real-name authentication page of auto insurance is a large UV, and the logic of real-name authentication is similar to the logic of adding a vehicle and editing a vehicle. Both include vehicle management logic and components. Therefore, the code similarity of this part of the page is high, and it is more suitable to be split into a subcontract. Finally, the real name authentication, vehicle management and other pages are split into:new user subcontract A.

Secondly, by observing the page funnel data, it is found that most users only visit the pages on the main process:auto insurance homepage, quotation page, payment page, policy details page. As for the branch processes such as the adjustment plan page and the vehicle list page, only a small number of users will access it. Therefore, the page of the main process is split into the main subcontract B, and the pages of all other branch processes are split into another subcontract:other subcontract C.

Finally, the subcontracting of auto insurance is split into the following:

  • New user subcontract A(200 + KB) including real-name authentication and vehicle management process
  • Main subcontract B(400 + KB) including main process
  • Other subcontracting C(100 + KB) including branch processes

For new users(without real-name authentication), pre-detect the real-name situation and distribute different paths, so that new users only load the new user subcontract A first, and at the same time preload the main subcontract B. Therefore, for new users, 200 + KB of new user subcontract A is loaded for the first time. Compared with the 900 + KB full subcontract loaded before optimization, the size of the loaded small program subcontract is reduced by 78%.

For old users(whose real-name authentication has been completed), the new user subcontract A is no longer loaded, and at the same time other subcontracts C are preloaded, so the size of the loaded subprogram subcontract is reduced by 56%.

Simplify JavaScript

This part of optimization is similar to the optimization principle of traditional web development.

After the code package is downloaded, the business code injection must be completed. At this time, the JS engine will parse/compile JavaScript, which is also the most time-consuming operation of the JS engine. As you can see from the Chrome Developer Tools, the yellow part is the time-consuming part of parsing/compiling:

Due to problems left over by history, some complex data require front-end patchwork calculation processing(such as the calculation of renewable vehicles, etc.). After migrating this part of the logic to the backend, the JavaScript code was further simplified, and only the basic data was transformed.

Since this practice does not go deep into this aspect of optimization, I will not elaborate on the effects of this optimization. I will have the opportunity to open another article in detail later.

Problems encountered

In the course of this optimization practice, I encountered many problems, and share with you our approach.

The first problem encountered is the component reference.

Because of the limitations of applets, components cannot refer to each other across sub-packages. To reuse across sub-packages, the component must be placed in the main package of the applet. The code of the main package is shared across services and cannot be added at will. Therefore, this problem needs to be considered when splitting subcontracting.

Secondly, it is the reference problem of JS public library.

Originally in the same subcontract, it can be introduced through a relative path. But after being split into multiple sub-packages, JS cannot be directly referenced. At this point, you need to change your thinking, such as:mounting the JS public library to the global variable global; or copying the code to other subcontracting(of course not direct copying, packaging through gulp or webpack); or the public library Put it on the main bag.

What is even more vexing is the problem of routing jumps caused by directory changes.

Since the applet subcontracting mechanism requires each subcontract to be in a directory. Therefore, it is inevitable to split the subcontracting to migrate the files, and the jump of the applet is strongly coupled with the file directory, and the change of the file path causes the change of the jump path. Therefore, the jump paths of the pages involved must be changed.

At this point, we must also consider forward compatibility, such as template notifications that have been pushed. Therefore, the routing jump encapsulation is extremely necessary.

effect

After the above cocoon extraction and stripping, I finally waited for the time to go online. I can see that the optimization effect is very obvious:

By comparing users in different categories horizontally, you can see the improvement of various users more clearly:

For the faster first 50%of users, it also increased the access speed by nearly 50%.

This optimization ensures that 90%of users can complete the loading of applets within 2000ms.

Subsequent optimization

For the next optimization work, we still have several plans:

Make full use of the pre-download subcontracting function provided by the applet. If no pre-download is set, the user has to wait for the download of the sub-contract when jumping to other sub-contract pages. For the more irritable users, they will think that the card is broken, which eventually causes the user to jump out.(The only limitation of the pre-download of the applet is that the sum of the current sub-package and the pre-downloaded sub-package size cannot exceed 2M, if it exceeds, you can consider splitting the sub-package again)

Simplify the WXML class. The class selector is the most common style selector. When the page becomes huge, the length of the class tends to become longer and longer. However, in small programs that do not support DOM manipulation, the main role of class is mainly style injection. Therefore, the same classes of WXML and WXSS can be unified into short naming through scripts.

Use Node.js to transfer part of the calculation. The JavaScript code is further simplified, which is conducive to further optimizing the loading performance of small programs. Secondly, this has also been extended to other platforms, such as H5.

to sum up

Although it seems that the development and release of WeChat applets is very different from the traditional web. But the underlying operation is similar, so you can find optimization ideas from traditional web optimization practices.

Some of the optimization practices mentioned in this article are very customized and are handled in a special way according to special business scenarios. There is no doubt that the business is developing and the code will change with it, which requires continuous optimization to make the user experience better and better.

Thank you for reading, if there are errors, please correct me, thank you.