Yesterday, when I was about to leave work, the product suddenly told me that a page on the operation background line was blocked when I entered it, and there was no response after some operation. Later, the browser pop-up prompted”The page is not responding“, as shown below:
Performance statistics
There are many reasons for the browser page to be blocked. For example, the PC configuration is too low (CPU and memory), too much software is running at the current time, the main process of the browser processes too many tasks, and the page rendering process processes too many tasks. Now that you know when it happened, go back and see if it was real and why. First of all bychrome
thedevtools performance
theStart profiling and reload page
Button down to test the page performance. The specific performance statistics screenshot is as follows:
Performance interpretation
- At the top of the graph is
FPS
Information, representing the number of frames rendered by the browser per second. The higher the green bar,FPS
The higher the height, the smoother the user feels. Red representativeFPS
Below 60, the redder it isFPS
The lower. - in
FPS
Below is theCPU
Information that represents the current pageCPU
The state of being consumed. When you haveCPU
When consumed, the corresponding bottom panel appearsSummary
The color in the TAB.CPU
The higher the bar isCPU
The greater the consumption. As you can see from the figure, after the page loadsCPU
Consumption is soon to beScripting
Full, andScripting
On behalf of theExecute JavaScript
. - in
CPU
Below is theNET
Information, which represents the network activity of the current page.NET
If the bar exists, it indicates that there is network activity. - in
NET
Below is theHEAP
Information that represents the current pageJS HEAP
, i.e.,JavaScript heap
The use of. You can see that in the pictureJS HEAP
On the page3500ms
Starting from the21MB
Quickly climb up to52MB
Have been inMemory ballooning
State, exceeding the memory required for optimal page speed. Main
Information on behalf ofPage main thread
The page main thread is responsible forJS calculation and execution
,CSS style calculation and layout calculation
,Predraw and submit
And so on. As can be seen from the figure on the page3500ms
At the beginning, the main thread is executing logic repeatedly, in time and with high consumptionCPU
And high consumptionJS HEAP
Corresponding. Further down we find the synthesizer threadCompositor
And child threads and some other thread pool tasks.- in
The Summary TAB
A new memory chart is shown at the top because it is checkedMemory
Then perform performance recording. There are five colored check boxes on the chart, respectivelyJS Heap
,Documents``(document
The number of)Nodes
(of the operationdom node
The number of)Listeners
(JavaScript
Number of listeners)GPU Memory
(GPU
Memory consumption).
Preliminary Problem location
It can be seen from the above that the following indicators remain high during the performance record time:
JS
Memory usage;- Operation of the
dom node
The number of; GPU
Memory usage;
See above that the main thread repeats some logic, trying to start from the event call stack, as shown belowwebpack
, itsdevtool
Set tofalse
, but you can still determine the key information based on experience, please remember not to hardcode the key business data in the front-end code) :
element-ui 2.x
The source code for the cascading component (panel) is shown below. The call stack order in the main thread matches exactly the order of the event calls in the component source code. The events of these calls are the initialization of the internal store, the rendering of the cascading elements, the updating of the checkbox state, the multiple node paths, and so on when the component loads.
The logic that the main thread repeats is the loading and initialization of cascading components, etc.!! How can repeated initialization occur (listening options update only once)? And the FPS has no value, right? This is not a Performance record for Carden! But why is this happening again? B: Not yet.
Recollecting Performance Statistics
To usedevtools performance
theRecord
Button to test the performance,Click the button to refresh the page and stop the statistics when the page becomes operational
. The screenshot is as follows:
As you can see from the image above, shortly after the page loads (the red line indicates that the onload event is triggered), the FPS bar turns red and the Frames bar indicates that there is a 21341ms free segment, indicating that the page is stuck but not rendered. At the same time, the CPU consumption was also fully occupied by the Scripting, and the JS HEAP was consumed to 62MB. After observing the thread usage, it was found that the main thread had a long task of 20 seconds for the cascade component during this period. It can be basically determined that the long-term execution of the initial configuration logic of the cascade component occupied the main thread and the running memory. As a result, the browser has no time to process the user experience, which is the problem screenshot at the top of this article.
Problem orientation
After comparing the data amount, it is found that the reason why the initial configuration of the cascading component is executed for a long time is that there is too much data on the directly subordinate nodes of a single node (the data amount of the problem is 5000+).
Project research
- to
Elemeng - UI components
Optimization requirements; - use
Virtual scrolling technology
Rewrite the cascade assembly; - Optimize the loading method of data;
In fact, in front of a large amount of data, even virtual scrolling is difficult to ensure the normal loading of data. The most elegant solution is to get the data in chunks and then render the data using virtual scrolling in the view component.
- Block data acquisition, that is, the common limit on the number of query data;
- Virtual scrolling technology, that is, large quantities of data display can be viewed in the form of scrolling, but it emphasizes the use of the browser’s native scrolling to dynamically render a reasonable amount of data near the viewport of functional components, and the rendered elements or components are properly discarded when they leave the viewport.
Plan implementation
Since the data in the current problem scenario is employees in the organizational architecture, lazy loading of data (data size: 1.5MB) can be used based on the existing function implementation. As the data in the interface is returned once, lazy loading of data is required at the component rendering level. The specific modifications are as follows:
- right
Element cascader components
To eliminateoptions
Property and addlazy
andlazyLoad
; - The format of the returned tree structure data is converted, and all its nodes are converted into a one-dimensional array, which is convenient to reduce the algorithm complexity when the data is loaded lazily.
// Vue SFC
{
data() {
return {
/* others */
optionsArray: [].// Organizational data in the form of a one-dimensional array}},methods: {
/** * Get organizational structure data *@returns {Promise<true>}* /
getData() {
return new Promise((resolve, reject) = > {
/ *... Asynchronously get organizational architecture data and call formatData2Array for formatting conversion... * /}},/** * Convert tree organizational structure data to one-dimensional array format data to facilitate lazy data loading to reduce algorithm complexity. *@param {Array} Data Indicates the data list of the current node@param {string | number} Pid Indicates the ID * of the parent node in the current node data list@returns {undefined}* /
formatData2Array(data, pid) {
for (const item of data) {
// Reassemble all node data, temporarily excluding the children field due to lazy loading
this.optionsArray.push({leaf:! item.children, pid, ... omi(item,'children')})// Process the list of child nodes recursively
if (item.children) {
this.formatData2Array(item.children, item.value); }}},/* others */}}Copy the code
- When a node is first loaded or clicked/slid,
lazyLoad()
Be triggered, inlazyload()
To dynamically obtain the direct child node under the node;
// Vue SFC
{
methods: {
/** * Data lazy load, get son instead of grandson. *@param {CascaderNode} Node The current operation node or root node *@param {Function} Resolve Callback to complete data loading (must be called) *@returns {undefined}* /
async lazyLoad(node, resolve) {
try {
// lazyLoad() may be executed before data fetching when the component is initialized to ensure that organizational schema data fetching is complete and formatting is completed.
if (!this.optionsArray.length) {
await this.getData();
}
// Note the precedence of the operator, === is 10, and the precedence of the conditional operator is 4.
const data = this.optionsArray.filter(
item= > item.pid === (node.root ? "root" : node.value)
);
resolve(data);
} catch (error) {
this.$message(error? .msg ??'Default error message'); }}/* others */
}
Copy the code