The background,

At present, the electronic contract of the company adopts the form designer and contract business cooperation to achieve, and finally goes online after more than half a year of work, but the staff below generally report that the contract is stuck, even stuck, and burst the stack. Especially the new and modified contract page, because this part of the data volume, logic complex, easy to crash, so I decided to optimize the performance.

2. Introduction to business scenarios

Let’s take a look at how we did it:

1. Because our company contracts change frequently and there is logic between terms, we made a basic service (namely component library in plain English) to provide templates for contracts

2. The form designer, as a basic service, is packaged into a component library embedded in the contract project, including the contract generation component (drag and drop to generate contract templates) and the contract preview component (load contract template data from the database).

3. The contract project has a module management page, which can maintain multiple templates, for example, you can choose which template to enable.

4. The administrator of the contract is responsible for maintaining the template. You can use form designer to drag and drop the contract template and submit it to the database.

5. Finally the following employees use the enabled template (especially this department is stuck)

Below is the macro swimlane map of the electronic contract:

3. Page introduction

  1. Contract Template Management page

2. Add a template page3. Create a contract page4. Contract filling page

Ok, so much for the basic business logic and pages, the special stuck page is the fourth page, let’s analyze the reasons for the stuck page.

4. Caton analysis

1. The first is the most serious problem form designer, because each component needs a lot of configuration items to support the component rendering, and a contract is made up of thousands of components that are tested, a contract template need to 5 MB of storage space (database with mongo, storage format for string, almost does not affect the), The following is the configuration of an input box

2. The form designer implementation uses a lot of closures to manage business, and closures are, as we all know, memory intensive.

3. The contract template is extremely complex, consisting of tens of thousands of components. I have a look at the template data down, and it is about more than 16,000 components with a size of 3.4MB.

4. Because the form designer includes ID, Model and event ID, which are randomly generated by the front end, in the form of random string + timestamp, with a total of 46 characters.

5. The contract project is a large-scale project with extremely complex business scenarios, including contract management, attachment management, contract list, new pages, approval pages and so on. I have calculated that there are more than 30 pages of optical routing, and there are so many pages, components, styles and businesses

Fifth, performance optimization

1. First try

Here is my optimization idea: First of all, the electronic contract by the form designer and business two projects done, contract template to load slow browser rendering is the cause of a large amount of template data and the template data is composed of multiple groups (about 12), I first think of is grouping rendering, load a group first, let the user sees the page, and then continues to load, One by one, the load is finished. This is the accepted solution.

Then I started to implement this group rendering and did it for about twenty days without any results.

Let’s take a look at the render code:

<template v-show="itemManage==='group'">
  <preview-item-template v-for="(item) in domainNodeList"
                        :key="item.id"
                        :formNode="item"
                        :parent="domainNodeList">
  </preview-item-template>
</template>
Copy the code

Loading is above all group code, this is a v – for, make group rendering, I thought of using the vue asynchronous component implementation, but it’s a cycle, all components registered will have the same name, this is obviously cannot use asynchronous components, unless registered is a component of different names, but I think for a long time to make to effect, So for 20 days, it was a failure.

2. Second try

As mentioned above, template loading is slow because browsers render a lot of data. As we know, JS is single-threaded, which means that all tasks can only be done on one thread, and only one thing can be done at a time. The first task is not finished, the next task has to wait. Therefore, JS has limited ability to process data, so I investigated webworkers under the advice of my friends.

The function of Webworker is to create a multi-threaded environment for JS, allowing the main thread to create Worker threads and assign some tasks to the latter to run. While the main thread is running, the Worker thread is running in the background without interfering with each other.

After reading a handful of documents, I immediately felt that this scheme was not feasible. At the end of the day, we just want webworkers to open up counties for us to process a large amount of data. However, the big data processed by webworkers is not because of the large amount of index data, but from the perspective of the amount of calculation. Usually, operations that cannot be controlled within milliseconds can be considered to be executed in Web workers. However, our contract template data is precisely a large amount of data, and does not need to do a particularly large operation.

The second attempt failed.

3. Third try

At the suggestion of my colleagues, I decided to use SSR, also known as server-side pre-rendering. The VUE project we usually write will generate DIST after packaging, and the operation and maintenance will put this folder in the server. The page we see is actually the render function generated and executed, which is quite time-consuming.

Server-side rendering means generating static pages on the server and handing them over to the client to render.

Building a server-side rendering application from scratch was quite complicated, so I ended up using the NUXT framework. I won’t say much about the NUXT framework, but you can read the documentation for yourself (portal). This frame comes with its own scaffolding, which is also officially recommended by VUE.

After a week, the migration from VUE to NUXT has been completed, and the page speed of large departments has been significantly improved.

Except for the new contract page that we want to optimize.

After analysis, the component library used by the contract project includes Element-UI and the form designer that I asked myself. Element only supports SSR for its department components. For example, tables and trees do not support SSR, so there is no server-side rendering.

I also tried to get a form designer to support SSR, but it didn’t work, if anyone knows, please contact me.

Apparently, the third time also failed.

4. Fourth try

Fate always makes fun of people. After optimizing the contract for more than a month, the speed has not been significantly improved. The leader is very anxious, and I am also very anxious.

Suddenly one day, I was on the way home, remember that day, the wind and rain, thunder, a huge thunder, my good idea are split out. The implementation of group loading immediately occurred to me.

Let’s take a look at the code implementation (only part of the code is shown) :

<template>
  <div class="dialog-preview" v-show="!formLoading">
      <el-form  ref="previewForm" onsubmit="return false"
                :size="formSettingState.componentSize"
                @hook:mounted="formMounted"
                :model="formModels">

        <template v-show="itemManage==='group'">
          <preview-item-template v-for="(item) in cutDomainNodeList.one"
                                :key="item.id"
                                :formNode="item"
                                :parent="cutDomainNodeList.one">
          </preview-item-template>
        </template>
        <template v-if="itemManage==='group' && formLoadingTwo">
          <preview-item-template v-for="(item) in cutDomainNodeList.two"
                                :key="item.id"
                                :formNode="item"
                                :parent="cutDomainNodeList.two">
          </preview-item-template>
        </template>
         <template v-if="itemManage==='group' && formLoadingThree">
          <preview-item-template v-for="(item) in cutDomainNodeList.three"
                                :key="item.id"
                                :formNode="item"
                                :parent="cutDomainNodeList.three">
          </preview-item-template>
        </template>
        </template>
      </el-form>
  </div>
</template>
<script>
export default {
    data() {
        return {
          formLoading: true,
          formLoadingTwo: false,
          formLoadingThree: false
        }
    },
    computed: {
        cutDomainNodeList () {
          let { domainNodeList } = this;
          let length = domainNodeList.length;
          if ( length <= 4 ) {
            return {
              one: domainNodeList
            }
          }else {
            return {
              one: domainNodeList.filter((el, index) => index <=2 ),
              two: domainNodeList.filter((el, index) => index>2 && index <=5 ),
              three: domainNodeList.filter((el, index ) => index > 5)
            }
        }
    },
    methods: {
        formMounted () {
          setTimeout(() => { this.formLoading = false },  500);
          setTimeout(() => { this.formLoadingTwo = true },  700);
          setTimeout(() => { this.formLoadingThree = true},  900);
        }
    }
}
Copy the code

Block loading implementation idea:

1. First of all, I made a judgment on the list of template data by using calculation attributes. If the array length is less than 4, it proves that the data volume is small and does not need to be loaded in blocks; if the array length is larger than 4, it proves that the data volume is large and needs to be loaded in blocks

2. Block loading is filtered by array index, the first block is 0-2 groups, the second block is 2-5 groups, the third block is index greater than 5 (can also be split with smaller), and then the page is traversed separately

@hook:mounted=”formMounted” @hook:mounted=”formMounted” @hook:mounted=”formMounted” Load the second block 700mm, load the third block 900ms, and the effect of the block loading is out.

Sixth, other aspects of optimization

First, a skeleton screen component was added to allow users to see transitions while waiting.

As mentioned above, the contract template is about 3.4MB, which is pure JSON, so it’s hard for the browser to load such large data all at once, so I was wondering if I could optimize the template size to speed up the loading.

In the form designer, id, Model and event ID are randomly generated by the front end in the form of random string + timestamp, with a total of 46 bytes. An English character is a byte, which is 46 bytes. Therefore, we can shorten the length of random number to reduce the size of the template.

I ended up with 26 random digits, which I calculated was about half the size.

Sure enough, the new template was 1.44MB in size, more than double the size.

On the other hand, we knew that the form designer wasn’t configured properly, so the administrator had to drag and drop the template in a different way, so we added some configuration items so that the administrator could drag and drop fewer components. This optimization reduced the template size by more than 300 KB.

We can also optimize the form designer code, change the closure implementation, which should also improve the loading speed. We’ll do that later.

The contract business project also optimized some interfaces, code, front and back end interactions, and page interactions to improve performance and visual effects.

Seven,

This is the first time that I made such efforts to optimize the performance of VUE project. Although it was rough, it still produced good results. From the initial loading time of 50 seconds or even one minute, we can successfully load in about 10 seconds now, and the speed can be increased by nearly 5 times. The overall effect is as follows:

Today’s achievement, although a few months, but everyone gathered firewood, to start a prairie fire, this is not one person, thank but less.