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 bychromethedevtools performancetheStart profiling and reload pageButton down to test the page performance. The specific performance statistics screenshot is as follows:

Performance interpretation

  • At the top of the graph isFPSInformation, representing the number of frames rendered by the browser per second. The higher the green bar,FPSThe higher the height, the smoother the user feels. Red representativeFPSBelow 60, the redder it isFPSThe lower.
  • inFPSBelow is theCPUInformation that represents the current pageCPUThe state of being consumed. When you haveCPUWhen consumed, the corresponding bottom panel appearsSummaryThe color in the TAB.CPUThe higher the bar isCPUThe greater the consumption. As you can see from the figure, after the page loadsCPUConsumption is soon to beScriptingFull, andScriptingOn behalf of theExecute JavaScript.
  • inCPUBelow is theNETInformation, which represents the network activity of the current page.NETIf the bar exists, it indicates that there is network activity.
  • inNETBelow is theHEAPInformation that represents the current pageJS HEAP, i.e.,JavaScript heapThe use of. You can see that in the pictureJS HEAPOn the page3500msStarting from the21MBQuickly climb up to52MBHave been inMemory ballooningState, exceeding the memory required for optimal page speed.
  • MainInformation on behalf ofPage main threadThe page main thread is responsible forJS calculation and execution,CSS style calculation and layout calculation,Predraw and submitAnd so on. As can be seen from the figure on the page3500msAt the beginning, the main thread is executing logic repeatedly, in time and with high consumptionCPUAnd high consumptionJS HEAPCorresponding. Further down we find the synthesizer threadCompositorAnd child threads and some other thread pool tasks.
  • inThe Summary TABA new memory chart is shown at the top because it is checkedMemoryThen perform performance recording. There are five colored check boxes on the chart, respectivelyJS Heap,Documents``(documentThe number of)Nodes(of the operationdom nodeThe number of)Listeners(JavaScriptNumber of listeners)GPU Memory(GPUMemory consumption).

Preliminary Problem location

It can be seen from the above that the following indicators remain high during the performance record time:

  • JSMemory usage;
  • Operation of thedom nodeThe number of;
  • GPUMemory usage;

See above that the main thread repeats some logic, trying to start from the event call stack, as shown belowwebpack, itsdevtoolSet 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.xThe 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 performancetheRecordButton 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

  • toElemeng - UI componentsOptimization requirements;
  • useVirtual scrolling technologyRewrite 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:

  1. rightElement cascader componentsTo eliminateoptionsProperty and addlazyandlazyLoad;
  2. 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
  1. 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