Shangzhi C store H5 performance optimization practice

Preface

Shangzhi C-store is an application built based on mobile low-coding capabilities, and its products are targeted at B-end merchants. As the size of the application continues to increase, considering product positioning and user experience, we made an optimization for pages with poor performance and achieved good results. The user experience value (UEI) increased from average to good . This article records the optimization ideas and process in detail, hoping to provide some reference for partners who are currently working on or planning to improve user experience.

1. Overview of performance optimization

From a macro perspective, front-end performance optimization mainly includes two levels:

Fast first-screen response

It refers to the process from the user inputting the URL to the complete rendering of the page, by reducing the resource request time, rationalizing the page rendering, etc., so that the page content can be presented to the user as soon as possible to achieve a comfortable display effect.

Smooth interactive experience

This means that after the resources are loaded and the page rendering is completed, the interactive feedback between the user and the page is timely and the animation is smooth, with no lags, frame drops, etc.

This article focuses on the former. The performance monitoring tool relies on the Zhulong platform (performance indicators come from the LightHouse performance tool).

2. Performance analysis

1. Lighthouse tool

Lighthouse is an open source automation tool launched by Google Chrome. It can collect multiple modern web page performance indicators, analyze the performance of web applications and generate reports, providing developers with reference directions for performance optimization.

1.1 Performance indicators

FCP

First Contentful Paint refers to measuring the time it takes for the browser to render the first piece of DOM content after the user navigates to the page. DOM content includes images on the page, non-white <canvas> elements, SVG, etc., and does not include any content within the iframe.

LCP

Largest Contentful Paint: Measures when the largest content element in the viewport is rendered to the screen, which approximates the time when the main content of the page is visible to the user.

SI

Speed ​​Index ( Speed ​​Index ) reflects the speed at which web content is filled. During the page parsing and rendering process, the loading of resources and the tasks performed by the main thread will affect the result of the speed index.

CLS

Cumulative Layout Shift ( Cumulative Layout Shift ) mainly measures the movement of visible elements within the visible area. This indicator needs to be focused on. With the version iteration of Lighthouse, the proportion of this indicator has been on the rise. It is also an aspect that is easily ignored in page performance optimization. The following is the proportion of CLS indicators in different versions of Lighthouse:





TBT

Total Blocking Time measures the total time a page is blocked from responding to user input (such as mouse clicks, screen clicks, or keyboard presses). It is calculated as a sum by adding the blocking portion of all long tasks between FCP and TTI. Any task that takes more than 50 milliseconds to execute is a long task.

Performance evaluation criteria for each indicator:





1.2 UEI calculation rules

UEI refers to user experience value. The bottom layer of the Zhulong platform uses the Lighthouse performance statistics tool. Among them, the score statistical rules of a single page are consistent with it (weighted average of performance indicator scores). For multi-page applications, the overall score calculation rule is the weighted average of each page's score: (page A * A visits + page B * B visits...) / (A visits + B visits + .. .).

2. Chrome debugging tools

Chrome's Performance panel can record all activities when the page is loading\running, making it easier for us to find the performance bottlenecks of the page.





The above is an overview of the performance panel. By recording the page loading process and viewing the main thread working process of the view panel, we can find long tasks blocked by the page, thereby optimizing the TBT indicator; problems such as animation freezes during the interaction phase can also be found. Analysis and optimization can be done through this panel.

3. Actual combat

1. Project introduction

Shangzhi C-store is an application built for B-end merchants based on mobile low-code. According to Zhulong statistics, before optimization, the user experience value was around 58 points. We look forward to improving the Zhulong statistical score to a good experience level (75 points and above) through this optimization. As can be seen from the figure below, the CLS and TBT scores of the page are very low, and the LCP indicator score is between good performance and poor performance. These three indicators are the focus of our next optimization.





2. Process records

2.1 Reduce resource size

2.1.1 Extract public dependencies

Shangzhi C-store adopts an architecture of main application plus three micro-applications. The package sizes of these three micro-applications are:





It can be found that the size of each package is very large (more than 5000k), which will inevitably affect the resource download speed and slow down the code execution efficiency. The reason why the micro application package is too large is that there are duplicate packages between the main application and multiple micro applications, such as component libraries (self-developed), public library dependencies (react, react-dom, echarts), etc.

To solve this problem, we can deal with it in the production phase of the product: generate two packages, one is a complete package to ensure that it can be used independently; the other is an optimized package that eliminates public dependencies and component library styles. After adjustment, the package size is reduced by more than 1300k , and the total package size is reduced to about 4000k .





It should be noted that the self-developed component library currently does not provide UMD format packaging, so the public dependencies mentioned above do not include packages related to the self-developed component library (will be processed later).

2.1.2 tree shaking optimization

tree shaking (tree shaking), used to describe the removal of unreferenced code (dead-code) in JavaScript context. Originally originated from the ES2015 module packaging tool rollup.

The self-developed component library mainly includes three packages, PC component library jmtd, mobile component library jmtm, and jmtd-hooks library. The hooks library is a secondary encapsulation of the component libraries at both ends, helping the low-code platform handle the logical arrangement between components. All three libraries are introduced through npm packages.





After excluding public dependencies and styles, the volume of micro-application products is about 4000k, which is obviously not an ideal value. The packaging and building tool relies on webpack5. According to the webpack performance analysis plug-in analysis, it is determined that the size of the jmtd-hooks package is too large. According to the inherent thinking, webpack5 supports tree shaking by default in the production environment, so the generated products will only package the used code and will not cause the product to be too large. So what's the problem?





The reason is that there is a problem with the packaging method of the jmtd-hooks library. Due to the strong business attributes of the hooks package, we packaged the original files into one file.

There are two ways of webpack's tree shaking:

usedExports: After turning on this configuration, unused functions with no side effects can be found in the packaging process and will not be packaged. It only takes effect on a single file and cannot be analyzed across files. JS is a dynamic language. Due to its flexible and changeable characteristics, it is difficult for compilation tools to accurately analyze whether a certain function has side effects, so its optimization capabilities are weak.
sideEffects: It has cross-file analysis capability. If sideEffects is configured as true in the package.json of a library, it means that all files in the library have no side effects. If a method in a file is not used, the compilation tool can safely perform tree shaking, and its optimization capabilities are stronger.

After analysis, it can be concluded that webpack only uses its static analysis ability to shake the tree very weakly on a single file, causing the micro-application product to contain a large amount of unused code. The solution is also very simple, adjust the packaging method of jmtd-hooks and retain the original file structure. After adjustment, the structure of the hooks package is:





It can be seen that the volume of the product has been greatly reduced, and is finally controlled below 2000k . After two steps of extracting common dependencies and tree-shaking optimization, the overall package size is reduced by about 65% .





2.1.3 Delete project redundant code

As requirements continue to iterate, it is easy for there to be some invalid scripts and style references in the project, or some non-standard writing methods, such as unused global variables, meaningless consoles, etc., which need to be deleted to keep the code streamlined.



After completing the above steps, we conducted an online launch and continuously monitored the data for a week. The user experience value has been significantly improved, from the previous 58 points to 65 points .

Before optimization:





Optimized:





 

The following is the comparative data of each indicator before and after optimization (unit: ms):





2.2 Optimize resource loading speed

The process of a page from inputting the URL to the final rendering in the browser is roughly as follows:

URL resolution => DNS resolution => Establish TCP connection => Client sends request => Server processes and responds to request => Browser parses and renders response content => Disconnect TCP link



The process from URL parsing to server response to the request belongs to the network level. Commonly used optimization methods include:

1. Caching technology: Reasonable use of cache, such as CDN cache, browser cache, etc., to speed up resource downloading.

2. File compression and merging: Reduce resource size through compression tools, merge multiple files into one file, and reduce network request overhead.

3. Turn on gzip compression: During the file transfer stage, turn on gzip compression to reduce the amount of data transmission and save server network bandwidth.

4. http2 protocol: This protocol brings many new features, such as binary framing, header compression, etc., making the transmission of certificate files more secure and efficient.

5. DNS pre-resolution: When requesting a cross-domain domain name for the first time, the domain name needs to be resolved first. The resolution time is easily overlooked. DNS pre-resolution can reduce the user's waiting time.

6. Domain name fragmentation: Domain name fragmentation refers to distributing static resources under the same site under different domain names. Its biggest advantage is that it can break through the concurrency limit of browser download resources.

7. Code splitting: Reasonably dividing the entire application code into multiple parts can effectively reduce the request size of first-screen resources and improve resource download speed.

2.2.1 Code Splitting

Reasonable code splitting can effectively speed up the first screen resource request. For this reason, we divided the project structure into a main application plus three micro-applications. The main application serves as a base to handle navigation and communication between micro-applications. The three micro-applications (business analysis, industry competition, and my concerns) ) as an independent module to undertake segmented business functions. Dividing the code into modules generally solves the problem, but there is still room for further optimization for some frequently used detail pages.





According to the statistics of hidden data, there are two main pages that users frequently use: the business analysis page and the indicator details page, which account for more than 90% of the PV value visited. The performance of the business analysis page is acceptable, with a user experience value of around 70 points, so optimization is not needed for the time being; the performance of the indicator details page is a disaster, with only 58 points, which is significantly lower than the current average of 65 points.





Analysis reason: The indicator details page is a secondary routing page. You can drill down to this page through the core indicator page, or you can directly view the details page of an indicator through the client homepage. This will result in the business analysis module needing to be loaded before viewing this page.

Solution: separate the indicator details page into a micro-application and maintain it separately. When a user directly accesses the details of an indicator through the Jingmai client (users use this scenario very frequently), only the micro-application code is loaded to avoid the impact of the first-level routing page code.

After the optimization, the effect is obvious. The score of the indicator details page soars from 58 points to 80 points . The indicators in each dimension have been improved significantly.





2.2.2 Resource preloading

Resource preloading is a common method for performance optimization, especially effective for applications with many modules. Previously, we have divided Shangzhi C store into the code structure of the main application plus three micro applications. The homepage loads the main application plus the business analysis module. When the user switches to other modules (competition analysis, my attention, etc.), he needs to first Download the product of this module (about 2000k), and then render the content.

Using the resource preloading solution, after the homepage content is loaded, the browser's spare resources can be used to preload the files of other modules. When the user switches to other modules, the resources have been loaded in advance, saving the time of resource downloading.

The specific implementation logic is:

// 通过动态创建script的方式,预加载文件
// 脚本加载并解析完成后,会将执行逻辑挂载到全局变量上
function loadScript (url) {
    if (!cache || cache[url]) return;
    cache[url] = new Promise(resolve => {
        const script = document.createElement('script');
        script.src = url;
        script.onload = () => {
            document.body.removeChild(script);
            resolve(window.__microApp__);
        };
        document.body.appendChild(script);
    });
}

// 当页面加载完成后,通过requestIdleCallback方法,利用浏览器空余资源来预加载文件
window.onload = function () {
    requestIdleCallback(() => {
        microAppArr.forEach(({ src }) => loadScript(src));
    });
};

Before resource preloading, the score of the industry competition module was 60 points. After optimization, it increased to 68 points :





Before optimization, the score of my attention module was 65 points, and after optimization it increased to 72 points :





2.2.3 DNS pre-resolution

When your website requests a cross-domain domain name for the first time, it needs to resolve the domain name first (for example, when a page accesses CDN resources, the CDN needs to be resolved for the first time), and requests after this request do not have this time expenditure. . A typical DNS resolution takes 20-120 milliseconds, and the resolution time is easily overlooked. DNS Prefetching is a domain name with this attribute. It is resolved in the background without the user clicking on the link. This method can reduce the user's waiting time and improve the user experience.

<!-- 用meta信息来告知浏览器, 当前页面要做DNS预解析 -->
<meta http-equiv="x-dns-prefetch-control" content="on" />
<!-- 在页面header中使用link标签来强制对DNS预解析 -->
<link rel="dns-prefetch" href="xxx.com"> 

Specific application cases of Shangzhi C store:





2.2.4 Domain name fragmentation

Domain name sharding refers to distributing static resources under the same site under different domain names. For example: the main website domain name www.a.com; the image domain name www.a-img.com; the domain name of scripts, styles and other files www.a-link.com. Its biggest benefit is that it can break through the concurrency limit of browser download resources. Please refer to the article for details .

Also in Shangzhi C store, we have also applied this idea, such as placing public libraries such as React and Echarts, business packaging code, pictures and other static resources in different CDN domain names, thereby breaking through the browser's concurrent request limit.

2.2.4 Sprite image

The bottom of the main application is a menu navigation, and its icon contains 6 pictures (different pictures are used for the selected and unselected states). A reasonable way is to use Sprite to merge multiple consecutive pictures into one. Zhang, control the image display by changing the position of the background image.





2.3 Optimize user perception

2.3.1 Rolling loading

The display form of the micro-application module is a long list of showcase cards (about 10, and the number will increase in subsequent demand iterations). As a basic layout component, the showcase card takes over the filtering area, drill-down function, and display functions of visual components. If too many are loaded on the first screen at once, it will cause greater rendering pressure.





Scroll loading is a good solution to improve first-screen rendering performance. Its core is to determine whether the target element (window card) is within the visible area. When it is not visible, you only need to render an element with a default height in the card content area. Placeholder elements, and there is no need to load the filter area component. When the user scrolls to this area, business-related content will be rendered.





As shown in the figure, the first screen only needs to render the contents of the two window cards of core indicators and target monitoring, and the other cards only render a placeholder element, effectively relieving the browser's rendering pressure. So how is the specific logic implemented?

There are usually two ways to determine whether an element is within the visible range:

1. Listen to the scroll event, obtain the coordinates of the target element (window card) relative to the viewport, and then determine whether it is within the visible area. The disadvantage of this method is that scroll events are triggered intensively, which can easily cause performance problems.

2. IntersectionObserver API , translated as "intersection observer", can automatically "observe" whether the element is visible, and is supported by Chrome 51+.

Here we adopt the second solution. In the initial state, the content area is set to be invisible. After the element is mounted, an io listener is created to detect that the card is within the visible range, and then the corresponding component is loaded. For specific logic, refer to the following code.

function WindowCard ({ title, filter, children }) {
    const wcRef = useRef(null);
    // 初始状态,默认设置内容区域不可见
    const [visible, setVisible] = useState(!useObserver);
    useEffect(() => {
        // 元素挂载后,创建io监听器
        const io = new IntersectionObserver(entries => {
            if (entries[0].intersectionRatio <= 0) return;
            // 当元素位于可视区域范围内后,加载业务组件
            setVisible(true);
            // 业务组件加载后,断开监听
            io.unobserve(wcRef.current);
        });
        io.observe(wcRef.current);

        return () => {
            io.disconnect();
        };
    }, []);

    return (
        <div className='window-card' ref={wcRef}>
            // 省略head部分逻辑
            <div className='window-card-content'>
                {visible ? children : <div style={{ height: 200 }} />}
            </div>
        </div>
    );
}

2.3.2 Optimizing CLS indicators

According to LightHouse statistics, the CLS indicator score is very low, which reflects the large layout offset when the first screen page is loaded.

Similar to the core indicator card, before the indicator card is loaded, the content area height is 0, and after loading, there is a placeholder height of 200px. After the real indicator card is rendered, the height becomes 322px, and the user can feel the obvious Jitter.





There are also scenes in the indicator details page. During the loading stage of the indicator card, the height of the occupied area is 200px, while the height of the actual rendered indicator card is 142px. If the user clicks on a clickable area at this time, it is easy to cause an accidental touch and bring a poor user experience.





In scenarios such as the above, we can determine the real height rendered by the indicator card in advance and fix it in advance. Follow a similar method to check other pages to avoid jittering on the first page of the page.

3. Practical summary

By extracting public dependencies and tree-shaking optimization, the size of the micro-application was reduced from more than 5,000k to less than 2,000k, and the application experience value was increased from 58 points to 65 points . By adapting scrolling loading to long list pages, the rendering efficiency of the page is improved; the height of the first screen indicator card and other elements is fixed, which reduces the page layout offset, thereby ensuring a stable display effect of the page; the page (indicator) used by users frequently is Details page) is an independent micro-application, which directly turns the lagging page into a page with excellent performance; resource preloading makes full use of the browser's spare resources and effectively improves the page loading speed. The experience value of the application has been increased from 65 points to 75 points:

Before optimization:





 

Optimized:





 



The performance of segmentation indicators has been significantly improved. The following is the comparison data before and after optimization:





 

The focus of this optimization is to improve the first-screen display effect of Shangzhi C store. The goal is to increase Zhulong's statistical score to a good experience level (75 points and above), which has basically completed the predetermined goal. Because the optimization process is in the gap between demand iterations, there is no plan to make in-depth adjustments to the system architecture and business code, so some remaining work has been put into the follow-up: For example, there is still room for further optimization of the volume of micro applications, and although the TBT indicator score has improved, However, the performance is still poor, and the interaction effects of some components are stuck.

Finally, I hope that the experience of this article can give some reference to friends who are also doing performance optimization. The shortcomings of the article can be discussed in the comment area. Stay tuned for subsequent articles on mobile interaction optimization.



Author: JD Retail Qie Pengfei

Source: JD Cloud Developer Community Please indicate the source when reprinting

Broadcom announced the termination of the existing VMware partner program . Site B crashed twice, Tencent's "3.29" level one incident... Taking stock of the top ten downtime incidents in 2023, Vue 3.4 "Slam Dunk" released, Yakult confirmed 95G data Leaked MySQL 5.7, Moqu, Li Tiaotiao... Taking stock of the (open source) projects and websites that will be "stopped" in 2023 "2023 China Open Source Developer Report" is officially released Looking back at the IDE 30 years ago: only TUI, bright background color …… Julia 1.10 officially released Rust 1.75.0 released NVIDIA launched GeForce RTX 4090 D specially for sale in China
{{o.name}}
{{m.name}}

Guess you like

Origin my.oschina.net/u/4090830/blog/10569844