background
The original address
Baby knows that miniprograms have gone through dozens of iterations since their first release. With the development of services and the increasing number of page functions, related performance data deteriorates. FMP of core performance data is above 2000ms for a long time.
Before this project, our team also did some performance tuning work on the small program, including:
- Package volume optimization: removed a lot of picture material files referenced in the project, and optimized the package volume to less than 500KB;
- The combined backend optimizes the time-consuming service interfaces, and the return speed of a single interface should be controlled at about 100ms.
- Optimized part of the business logic, small program startup to reduce some unnecessary operation logic;
- Using the latest life cycle onInit provided by the small program framework, it can initiate business network requests about 100ms in advance;
- Prelink is used to preconnect the network, improving the data interface request efficiency.
After the above means, THE FMP was reduced to about 1900ms, and the optimization effect was no longer possible.
The above optimization methods basically exclude the poor performance caused by network connection and insufficient packet volume optimization. There is only one issue that needs to be scrutinized — rendering efficiency of content.
The problem found
At present, the biggest entry page for opening baby mini program from Mobile is the question and answer page, which accounts for more than 60% of the total PV. Therefore, we give priority to optimizing this page to maximize performance benefits.
Read through the code of the q&A page, from top to bottom, and the function points of the whole page are as follows:
- Live broadcast information bar
- The problem area
- Answer area
- Advertising component area
- Recommend feedList for you
There are many types of content to be displayed, and the amount of content is huge. Part of the content requires separate interface acquisition, coupled with the introduction of advertising components, the presentation efficiency cannot be optimized at all.
Because the above business content needs to be displayed, using setData to trigger content rendering during loading will cause major problems, such as:
- SetData is called too often during loading, for example, onLoad will be set, onShow will be set, and asynchronous data launched at different stages will also be set after loading. Simultaneous setData in the current thread is easy to cause congestion of the small program rendering thread and affect the efficiency of content rendering.
- The amount of setData ata time is too much. After the interface data is returned, all the data required in the page are submitted to the rendering thread for rendering at one time, resulting in a long waiting time for the thread and affecting the final presentation of effective content. Although reducing the number of setData calls is officially advocated, submitting too many data renders ata time is not an optimal strategy.
The above two problems in the use of setData will not be reflected on mobile devices with better configurations. However, for mobile devices with medium and low configurations, the loss of user experience is still great due to operation congestion or rendering delay caused by a large number of data rendering operations.
Data rendering schematic of q&A page before optimization
Before optimization, after page load data rendering, for the first time will submit questions, answer at a time, advertising components area three part of the rendering task, because of these three areas involved in capacity is large, the basic will be more than one screen, even more than two screen, the other regions also contains some graphic content, combined with itself takes higher advertising components. Overall page content rendering speed is poor. In addition, due to the data content rendering of separate asynchronous request loading such as the horizontal bar of live broadcast information, it is easy to cause the congestion phenomenon of setData operation in the small program rendering thread.
Therefore, according to the statistical rules of small program FMP, the current data rendering logic is obviously not optimal.
Since FMP mainly counts the location content of the first screen that users can see at first sight, can we change our thinking to complete our content rendering work?
While ensuring that the data interface performance is already up to standard, we can use smarter rendering strategies.
Optimization scheme
In order to solve the above problems, we design a set of split screen type content rendering strategy, intended to allow users to the fastest speed to see part of the key content, to render the rest need to be rendered by stages, and those who do not need to be automatically rendered data, can change to some kind of behaviors by the user (such as sliding page) trigger loading and rendering.
Optimized q&A page rendering schematic
PS: The advertising component itself is an asynchronous component. The second setData will trigger the rendering of the advertising component, and the advertising component itself initiates the loading of asynchronous content.
The optimized q&A page rendering logic is divided into four stages as a whole:
- Core content quick render stage. This stage is the main data rendering time detected by FMP, so in this stage, we need to make the content and elements of the page, enough to fill a screen.
- Core content completion rendering stage. In this stage, time-consuming contents in the core content, such as pictures, videos and native components of small programs, are rendered on the screen (Note: As for time-consuming components for rendering, it is known that video, video and all native components of small programs are not suitable for direct rendering in the first stage. If conditions permit, images should not be placed on the first screen as far as possible.
- Subsequent content rendering phases. In this stage, all the data returned by the interface to be rendered will be displayed on the screen.
- Other non-primary asynchronous data rendering phases (live message bar in legend). Render another interface’s data onto the screen.
PS: If the core content is still unable to fill a screen after rendering, you can consider setting the min-height of the whole page to 100vh, or placing a placeholder element at the bottom of the page to achieve the effect of filling a screen.
Optimization results
The optimized version was launched at around 11 a.m. on August 4, 2020 in full quantity, and gradually increased volume in Baizhong. FMP index decreased rapidly on August 5 and 6, and gradually stabilized on August 7. A total of 540ms FMP index was optimized.
From the data performance, the optimization effect is very obvious.
In addition, the question and answer page, as the biggest landing page for baby to know pv of small program, accounts for about 60% of the total PV. In addition, we need to continue to optimize 40% of other pages, and there is still a lot of room for optimization of future data performance.
Ps. We need people like you in tech Exchange
Tool construction
He that will do his work well must first sharpen his tools. Subsequently, we also need to optimize the performance of other entry pages, as well as to make continuous technical reserves for the subsequent development of high-performance pages, so we will encounter performance-related problems in the development of some abstractions, through the creation of the basic operation of the tool class library, from the bottom up to solve or avoid the problem.
As mentioned above, launching multiple setData operations at the same time is easy to cause congestion of small program rendering threads, which will affect the rendering efficiency and reduce the screen efficiency of small program content. In actual development, if we want to avoid launching multiple setData at the same time, it will inevitably bring additional logical thinking cost and code structure adjustment cost, and it is also easy to reduce the readability and maintainability of the code because of adjustment. In order to balance the need of rendering performance with the readability of code structure and the look and feel of code, we specially designed a content rendering task manager.
DataSetter
DataSetter is now integrated within the team’s applets engineering scaffolding, and applets Page instances created through AdvancedPage support submitting data rendering tasks to applets’ rendering threads through the API open to the manager.
DataSetter encapsulates small program setData operations as a queue rendering task manager. Using DataSetter to perform set operations allows only one setData operation to be performed per unit of time, while other data is being set at the same time. Will queue the execution in sequence in the queue.
Legend: setData before optimization will cause congestion in the applet rendering thread
Legend: After optimization, set and DataSetter will manage data rendering tasks as a whole without causing congestion of rendering threads
To support the writing of split-screen rendering strategies, DataSetter’s API is designed as a chain-call design. The n-stage content rendering logic can be written in a non-nested manner, and the code is clear and understandable.
This.$datasetter. set({// first render data status:'success', aaa:111}). Done (e => {// first render complete console.log(' first render complete '); }).set({// BBB :222}).set({// BBB :333}).done(e => {// BBB :222}); }); Copy the codeCopy the code
DataSetter source
/** * @name DataSetter * @description setData syntax is improved to support the chain call and queue set data, after the next setData */ class DataSetter {queue = []; context = null; index = 0; constructor(context) { this.context = context; } set(dataset = {}) { this.queue.push({dataset, callback: null}); if (this.queue.length === 1) { this.exec(); } return this; } done(callback) { this.queue[this.queue.length - 1].callback = callback; return this; } exec() { let task = this.queue[this.index]; if (! task) { // console.log('all task done! '); this.refresh(); return; } const next = () => { // console.log(set data ${this.index} ok!) ; task.callback && task.callback(); this.index++; this.exec(); }; // If the current task dataset is empty, no native setData is called and the callback if (! task.dataset || Object.keys(task.dataset).length < 1) { next(); return; } // console.log(set data ${this.index}); this.context.setData(task.dataset, next); } refresh() { this.queue = []; this.index = 0; } } // Page Demo Page({ $dataSetter: null, onLoad() { this.$dataSetter = new DataSetter(this); }}); Copy the codeCopy the code
Afterword.
There are many situations where the performance of a small program is not ideal, but the resolution and optimization of rendering problems can bring the greatest benefits, and the flexibility to design the rendering strategy of a view based on the actual business scenario can often bring wonders. Rendering problem optimization is a very delicate thing, especially in the face of increasingly complex business code, dare to try to transform, is the final success of the starting point.