当前位置:网站首页>Web resource preloading - production environment practice

Web resource preloading - production environment practice

2022-06-26 18:53:00 Flying bear blablabla

This article records the practice of resource preloading in our project , The technical difficulty is not high , Focus on introducing the birth and implementation of a set of technical solutions , What are the thoughts in it , Based on what to make decisions , How to evaluate the effect , wait . It provides some enlightenment for readers when making technical plans .

background

The resource preloading mechanism is well understood , That is, before the user visits the page , Load the corresponding resources in advance . So when users visit the page , Saves time to load resources , achieve “ Second on ” The effect of .

There are many resource preloading schemes , What this article describes is pure web Resource preloading under , It is different from using containers to preset resources . So the technology used is pure web programme .

There is also a background : The project is SPA framework ,webpack+vue Family bucket , What we do is after loading the home page , That is, the preload when jumping to other pages . coordination SPA Advantages of application , It can achieve zero delay jump comparable to native applications .

Let's introduce the technical details .

Which resources are preloaded

There are many resources that can be preloaded , Page asynchrony chunk、vue Components 、js modular , Even interface data can be pre requested . So it depends on what your purpose is .

Here we aim to jump to the page with zero delay , So the page to be preloaded is asynchronous chunk. The so-called page asynchrony chunk, Refers to the use of vue-router The asynchronously loaded file corresponding to the defined page route , In fact, it's also a vue Components , In order to communicate with others vue Component differentiation , We call it page asynchrony chunk Well . This is what the code usually says :

const routes = [
    name: 'home',
    path: '/home',
    children: [
        {
            path: 'A',
            component: () => import('pageA.vue'),
        },
        {
            path: 'B',
            component: () => import('pageB.vue'),
        }
    ]
]

pageA.vue and pageB.vue Packaged asynchronously chunk It will only be loaded under the corresponding route , Bring a sense of delay to page Jump , This is the part of resources we need to preload .

Resource loading time

Specify the resources to be loaded , The next thing to consider is , When to load these resources ? If we preload pageB And the user doesn't jump B What shall I do? ?

Resource loading time , It's a technical job , Two key points need to be grasped :

1. Preloading resources should try not to affect the normal operation of users

2. Preloaded resources should be used by users as much as possible

The first 1 spot , It's easy to think , We can preload the page when it is idle . And as soon as possible , If you load late , Users will not use your preloaded resources . To sum up , The time to load resources is “ As early as possible when the page is idle ”.

The first 2 spot , It's not easy , Whether the user jumps or not B Pages are user behavior , How can we guarantee that it will use the preloaded resources . So this can only be a strategy , Maintain an acceptable proportion , stay “ Waste traffic ” And “ User experience ” Find a trade-off point between .

Let's talk about these two points .

Page idle detection

How to detect whether a page is idle ? Unfortunately, there is no such api You can use , There is one requestIdleFrameCallback, It supports the execution of a callback function during the idle period of each frame , But there is no guarantee that .

So we can only judge by ourselves , In fact, the idea is relatively simple , The browser's rendering process has js The engine and UI Engine these two threads , As long as these two threads are idle , We think the page is idle , This should work .

that , How to detect the idleness of these two threads ? It's very simple , Just look at the code :

//  testing js Thread idle 
const d1 = new Date();
setTimeout(()=>{ 
    const offset = new Date() - d1;
    if (offset < 25 ) { 
      // JS Thread idle  
    }
}), 20);

Start a delay function , Check the delay of actual execution , If the delay is very large , That means the current situation js The engine is busy . If it is less than a certain threshold , That means js Engine idle .

How should this threshold be determined ? Statistics on real users are certainly the most accurate , So we use an idle page to calculate the average delay of users , It was finally determined to be 5ms.

Same idea ,UI Idleness of the engine can also be detected :

//  testing UI Thread idle 
const d1 = new Date();
requestAnimationFrame(()=>{
    const offset = new Date() - d1;
    if ( offset < 30 ) { 
      // UI Thread idle  
    }
})

The determination of threshold value is also the statistical mean value of real users , It was finally determined to be 30ms.

With these two tests , We got the page “ Spare time ”. How about “ As soon as possible ” Take it ? The method is relatively simple , We use... After the page is loaded setInterval Start polling , every other 1 Check once per second , Once idle is detected , Resource preloading .

Resource inventory policy

Next, let's look at No 2 spot , How to be in “ Waste traffic ” And “ User experience ” Find a trade-off between .

Consider a question , If only 5% Of users will jump to the page from the home page B, that B Is it necessary to preload the resources of ? The answer is obviously No . In other words, the resources that need to be preloaded should be determined manually , What do we use to determine the resource list ?

There are two ways :

1. Page access funnel . According to the funnel chart, we can get the loss rate of each page Jump , For pages with high churn rate , We can avoid preloading . What is the value of ” more “, This also needs to be weighed , For example, we think 60% Even if the loss is large . A typical funnel diagram is as follows :

2. Dynamic adjustment according to statistical indicators . How effective is our resource preloading scheme , What is the cost-benefit ratio , It needs clear indicators to measure . Pages that have a negative effect on indicators , Don't preload it .

that , How to design statistical indicators ? Let's continue .

Index design

Indicators are used to evaluate and guide our work in a data-based way 、 Decision making . Since it is a production environment practice , There must be a rigorous set of indicators to measure the merits of this scheme .

As mentioned above, we have counted the user execution setTimeout and requestAnimationFrame Average delay of , It is used to guide us to set the idle detection threshold , This is an example of an application .

For the whole scheme , We also designed the following indicators :

Page Jump time

That is, the average time it takes a user to jump to a page . This is the core index we should pay attention to , The goal of the whole scheme is to reduce the page Jump time . Because it is SPA application , This time is easy to count .

Trigger rate

That is, the proportion that triggers resource preloading , Calculation formula : Trigger rate = Number of resource preloads / Page load times . To measure our loading strategy ( Idle detection ) Is it reasonable , Ideally, this value should be close to 100%, In other words, the vast majority of users have preloaded resources . If the trigger rate is found to be lower than expected , Then we should adjust the loading strategy .

shooting

That is, the proportion of preloaded resources used by users , Calculation formula : Number of times preloaded resources were used / Number of resource preloads . This is used to measure the effectiveness of preloaded resources , For example, the hit rate of a resource is 80%, explain 80% Of users use preloaded resources , The effect is very good . But the actual hit rate is often not so high , So our evaluation criteria are : The closer to the scale of the funnel diagram , The better the effect .

Page dwell time

That is, the average time a user stays on a page . This indicator is also collected to guide the loading strategy , For example, the average stay time of the home page is 3 second , Then we can set the polling interval to check whether the page is idle 600ms once , A total of 5 Time .

besides , We also collected the network speed of users , To analyze the impact of this scheme on users in different network speed segments .

With the above indicators , We will be able to formulate strategies scientifically , For example, the hit rate of a resource is lower than 10%, That means it's a low frequency page , Simply remove from the list , No preloading .

How to load resources

The whole idea has become clear , The next step is to load resources . There are many ways to preload resources , Which one shall we choose ?

You might as well look at them one by one .

1. Handwriting script label

The project uses webpack packaged , adopt manifest Documents can be obtained from the resource list , Create one manually when preloading is required script label , Users can use the resources in the cache when they actually jump .

The advantage of this method is that it is less invasive , Just one extra resource load , Minor changes to the original code .

But the disadvantages are obvious : Bad statistical indicators . To count the completion of loading, you can do it in each script Labeled onload In the incident , It's acceptable . But there is no way to count the hit rate , It is impossible to determine whether the user is using the resources in the cache or newly loaded .

2. browser prefetch

That is to use <link rel="prefetch" href="xx.js">, This is the method of preloading resources provided by the browser , It will automatically load resources when the browser is idle .

This method is a little weak , Because no js API For calling , Yes, we have a black box , There is no way to make statistics on relevant indicators .

3.webpack Provided import()

webpack Provides a method for dynamically loading resources , Although it is written in essence script label , but webpack It is well encapsulated , We go through .then() You can know that the resource is loaded . And the resources are stored in memory , On the one hand, it is convenient to count various indicators , On the other hand, you don't even have to leave the cache , Faster .

Sum up , We finally chose the method 3, Can meet our needs .

Effect evaluation

The above is the technical content of the whole scheme , But it's not over yet . Track and evaluate the effectiveness of the scheme , And continue to optimize , This is the correct posture for production environment practice .

The core indicators we care about , Page Jump time , With 50% The above reduction . This is expected , Before the page Jump, such as time-consuming 200ms, After hitting the preloaded resource , Maybe it will be in a moment 10ms Within . The impact on the market is very significant .

Trigger rate , It should be at least 90% The above is qualified . No preload is triggered , How can we talk about hit in the future . Of course, this index can be optimized by adjusting the loading strategy , The back can speak .

As for hit rate , It's hard to say . The first is the one mentioned above , It is related to user behavior , It's an unstable factor . secondly , If the resource preload is triggered too late , The user has already gone to jump , It will also affect this indicator . So the whole is from 15%~35% Don't wait . What is the level of this value ? Qualified or unqualified ? We read the information about preloading schemes in the industry , See that their hit rate is about 20%, So we can think that our effect is good .

Continue to optimize

You can see that , Our index has optimization space , Direction is : Increase trigger rate 、 shooting , Reduce page Jump time .

It is also mentioned above , In order to work out the most accurate loading strategy , Many auxiliary indicators have been collected . An interesting optimization is , We used to go to the page 1 Seconds later, it starts to detect idle , Later, it was changed to detect immediately . Unexpectedly, this change has significantly improved the trigger rate , The reason is that some users are 1 Jump to another page within seconds , There is no time to trigger the preload .

Similar optimizations include , We adjust the idle detection interval , It is found that the trigger rate is also affected .

in addition , We also calculate the hit rate according to the statistics , Removed some low frequency pages , Avoid these resources pulling down the hit rate .

To make a long story short , The reference value of each index is a trade-off process , It needs to be negotiated according to their own business conditions .

Think more

As a performance optimization scheme , We're just building on a specific project 、 Some explorations have been made under specific scenarios . In fact, this subject can do more than that .

For example, the resource loading policy , Can it only be loaded when the page is idle ? stay PC Time , Some schemes guess the next action of the user according to the trajectory of the mouse , Then decide which resources to preload . In mobile terminal , Can we make predictions based on the sliding motion , Or, , Whether the user behavior can be counted ( Business ) As a reference , These are all directions that can be explored .

A friend might ask ,SPA The application loads resources that are not needed on the home page , As a result, you add it back with preload , Isn't that an unnecessary move ? Just package it into a file ? Sounds like a lot of sense , But think carefully , Package into a file and SPA+ Preloading , Compare the advantages and disadvantages of these two methods by drawing out a list , I knew there was a difference . This requires the ability to think .

So the purpose of this article is , Describe the design and implementation process of a technical scheme , To provide you with technical or methodological reference .

原网站

版权声明
本文为[Flying bear blablabla]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202161739044413.html