vue performance optimization

Posted by chrome29 on Sat, 09 Oct 2021 13:24:23 +0200

preface

This paper mainly records the common optimization skills in daily development. Mainly for version 2.x.

Functional component

Functional components are declared using functional fields. It has no data responsive data, this context, and no life cycle hook function. It only accepts a props. When a component of ordinary object type is in a patch, if a node is a component, it will recursively execute the initialization process of the sub component. The functional component render generates ordinary vnode s without the process of recursive sub components, so the rendering overhead will be much lower. In fact, it can be understood as pulling out the DOM, which is a kind of reuse at the DOM level.

We can see from the source code:

function createComponent(Ctor, data, context, children, tag) {
  // ...
  // Judge whether it is a functional component according to the functional field
  if (isTrue(Ctor.options.functional)) {
    return createFunctionalComponent(Ctor, propsData, data, context, children);
  }
  // ...
  // Normal components are initialized here (including response data and hook function execution)
  installComponentHooks(data);
  // ...
  return vnode;
}

As we can see from the above, when creating a component, we will judge whether it is a functional component according to the functional field. It will follow the creation process of functional components, not the creation process of normal components (initialization of life cycle functions, responsive data, etc.).

Functional components are generally used on components that do not interact, do not need to store internal state, and purely display UI. For example, the pages of news announcement details simply display the data.

The usage is as follows:

Vue.component("my-component", {
  functional: true,
  // Props is optional
  props: {
    // ...
  },
  // To make up for the missing instance
  // Provide the second parameter as the context
  render: function (createElement, context) {
    // ...
  },
});

It can also be written like this in versions above 2.5.0

<template functional></template>

Freeze list data

In our normal development, we often encounter some list data. The list data is an Array, and each item of the data is an ordinary object, but the list data is only a simple display, and each item of data does not need to be changed. Then, we can use Object.freeze([]) to freeze list data, reduce the level of data response (recursion) and improve performance.

We can see from the source code:

export class Observer {
  constructor(value: any) {
    def(value, "__ob__", this);
    if (Array.isArray(value)) {
      // Converts all elements in the array into detectable responses
      this.observeArray(value);
    } else {
      // Common object
      this.walk(data);
    }
  }
  walk(data) {
    for (const key in data) {
      if (Object.hasOwnProperty.call(data, key)) {
        //   Convert normal objects to responsive data
        definedRetive(data, key, data[key]);
      }
    }
  }
  observeArray(items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      // Listen for each item in the array
      observe(items[i]);
    }
  }
}
export function observe(value, asRootData) {
  // If the monitored data is a non object type or a vnode, it will not be monitored
  if (!isObject(value) || value instanceof VNode) {
    return;
  }
  let ob;
  if (hasOwn(value, "__ob__") && value.__ob__ instanceof Observer) {
    //   There will be on the monitored data__ ob__ attribute
    ob = value.__ob__;
  } else {
    ob = new Observer(value);
  }
  return ob;
}

From the above, we can see that the data in the array will be recursively monitored. If each item in the array has deeper objects, these deeper objects will also be recursively turned into responsive data.

Object.free can make an object non configurable, that is, it can only be read, that is, set configurable to false, and these operations cannot be added, deleted or modified. When vue responds to data, if it finds that it is a non configurable object, it will return and will not execute the following logic, that is, it will not turn the data into responsive data.

We can see from the source code:

export function defineReactive(
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  // Gets the description of the object
  const property = Object.getOwnPropertyDescriptor(obj, key);
  //   Configurable determines whether it is configurable
  if (property && property.configurable === false) {
    return;
  }
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      // ...
    },
    set: function reactiveSetter(newVal) {
      // ...
    },
  });
}

Frozen list data is generally used in scenarios where there is a large amount of data but there is no need to modify each item of data. Usually, these list data are only used for display. Such as the list of news announcements.

Code example:

<template>
  <div>
    <div v-for="(item, index) in list" :key="index">
      {{ item.label }}
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [],
    };
  },
  created() {
    let list = [];
    for (let i = 1; i < 1000; i++) {
      list.push({
        id: i,
        label: `The first ${i}individual`,
      });
    }
    // Freeze data
    list = Object.freeze(list);
    this.list = list;
  },
};
</script>

Sub component split

When there are the following codes on our page:

<template>
  <div>
    home page
    {{ message }}
    {{ count }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
      message: "hello world",
    };
  },
  mounted() {
    this.timer = setInterval(() => {
      this.count += 1;
    }, 1000);
  },
};
</script>

As you can see from the above, the page has a timer, so it will trigger an update every second. Because the update of vue is component granularity (only the components with data changes will be updated, and the sub components will not be updated recursively), the whole page will be updated again. When there are other complex logic on our page, this update process is very time-consuming (first convert to vnode - > patch and compare the old and new vnode - > updates). So we need to package the above code into a component to reduce the scope of re update. The code is as follows

Count component

<template>
  <span>
    {{ count }}
  </span>
</template>

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  mounted() {
    this.timer = setInterval(() => {
      this.count += 1;
    }, 1000);
  },
};
</script>
<template>
  <div>
    home page
    {{ message }}
    <count-component />
  </div>
</template>

<script>
export default {
  data() {
    return {
      message: "hello world",
    };
  },
};
</script>

Local variable (CACHE variable)

Let's look at the following code first:

<template>
  <div>{{ result }}</div>
</template>

<script>
export default {
  data() {
    return {
      start: 1,
      base: 24,
    };
  },
  computed: {
    result() {
      let result = this.start;
      for (let i = 0; i < 1000; i++) {
        result +=
          this.base * this.base + this.base + this.base * 2 + this.base * 3;
      }
      return result;
    },
  },
};
</script>

As can be seen from the above, the calculation attribute result will frequently access the data of this.base when calculating the result.

Let's look at the source code of data response in vue:

function defineReactive(obj: Object, key: string, val: any) {
  const dep = new Dep();
  let childOb = observe(val);
  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter() {
      // ...
      // Dependency collection during getter
      if (Dep.target) {
        dep.depend();
        if (childOb) {
          childOb.dep.depend();
          if (Array.isArray(val)) {
            dependArray(val);
          }
        }
      }
      return val;
    },
    set: function reactiveSetter(newVal) {
      if (newVal === val) {
        return;
      }
      val = newVal;
      childOb = observe(newVal);
      dep.notify();
    },
  });
}

Generally speaking, when reading this.base attribute, its getter will be triggered, and then dependency collection related logic code will be executed. In the calculation attribute of result, this.base attribute will be read 6 times in each for loop, with a total of 1000 cycles, so the getter depends on collecting relevant logic code and will be executed 6000 times. These 6000 times are all useless, resulting in performance degradation.

In fact, this. Base only needs to be read once and then perform dependency collection once. Therefore, we can use the local variable to cache the value of this.base attribute. Later, we use this local variable instead of this.base, so we won't rely on the relevant logic of collection. The optimized code is as follows:

<template>
  <div>{{ result }}</div>
</template>

<script>
export default {
  data() {
    return {
      start: 1,
      base: 24,
    };
  },
  computed: {
    result({ base, start }) {
      let result = start;
      for (let i = 0; i < 1000; i++) {
        result +=
          Math.sqrt(Math.cos(Math.sin(base))) +
          base * base +
          base +
          base * 2 +
          base * 3;
      }
      return result;
    },
  },
};
</script>

In the actual development, I see that many people like to write this.xxx directly every time they take variables. When there are many accesses (especially in the for loop), the performance defects will be highlighted. So when you frequently read a variable value in a function, remember to use local variables to cache the variable value.

In fact, the performance optimization of local variables can be used not only in vue, but also in other places. For example, when we need to cycle an array, we can cache the length of the array instead of reading the length attribute of the array every time we cycle (in fact, many people like to read the length attribute of the array directly during the cycle). When operating the DOM, you should also cache the local variables used by the DOM, because the reading of the DOM is quite performance consuming.

Cache features of computed

<template>
  <div>
    <div :style="style">bar1--{{ count }}</div>
    <div :style="getStyle()">bar2--{{ count }}</div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      width: 100,
      count: 0,
    };
  },
  computed: {
    style() {
      console.log("style");
      return { width: `${this.width}px` };
    },
  },
  mounted() {
    setInterval(() => {
      this.count += 1;
    }, 1000);
  },
  methods: {
    getStyle() {
      console.log("getStyle");
      return { width: `${this.width}px` };
    },
  },
};
</script>

From the above, we can see that the return of the style calculation attribute is actually the same as that of the getStyle function. But when our timer starts, it will trigger the view update every second. We can see from the console that getStyle is printed once every second, while style is printed only once. Thanks to the cache feature of vue's calculated attribute, the style attribute will be recalculated only when the width value changes. The count attribute is not a dependent variable of the style attribute, so the change of count will not affect the count attribute. Therefore, we should be good at using the calculated property instead of returning a value through a methods function. The methods function will be triggered and re executed with each view update. If the methods function contains a large number of logical operations, it will cause a lot of performance loss.

The calculation attribute source code of vue is as follows:

const computedWatcherOptions = { lazy: true };
function initComputed(vm: Component, computed: Object) {
  // Add one to the component instance_ computedWatchers property, which holds all computed watcher s
  const watchers = (vm._computedWatchers = Object.create(null));
  // Traverse all the attributes above computed
  for (const key in computed) {
    const userDef = computed[key];
    // computed can be a function or an object
    const getter = typeof userDef === "function" ? userDef : userDef.get;
    // Data response watcher
    watchers[key] = new Watcher(
      vm,
      getter || noop,
      noop,
      computedWatcherOptions
    );
    if (!(key in vm)) {
      defineComputed(vm, key, userDef);
    }
  }
}

function defineComputed(target: any, key: string, userDef: Object | Function) {
  if (typeof userDef === "function") {
    sharedPropertyDefinition.get = createComputedGetter(key);
    sharedPropertyDefinition.set = noop;
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop;
    sharedPropertyDefinition.set = userDef.set || noop;
  }
  // Override get, set
  Object.defineProperty(target, key, sharedPropertyDefinition);
}

function createComputedGetter(key) {
  // A ` getter is returned`
  return function computedGetter() {
    const watcher = this._computedWatchers && this._computedWatchers[key];
    // The existence of the watcher indicates that the computed attribute exists
    if (watcher) {
      // If the responsive data that computed depends on changes, it will trigger watcher.update, set dirty to true, and recalculate the computed property
      // If there is no change, the previous value is returned
      if (watcher.dirty) {
        // The evaluate function will retrieve the value of watcher.value and set watcher.dirty to false. It will not be recalculated next time
        watcher.evaluate();
      }
      return watcher.value;
    }
  };
}

function createGetterInvoker(fn) {
  return function computedGetter() {
    return fn.call(this, this);
  };
}

class Watcher {
  constructor(vm, expOrFn, cb, options, isRenderWatcher) {
    this.vm = vm;
    if (isRenderWatcher) {
      vm._watcher = this;
    }
    vm._watchers.push(this);
    if (options) {
      // Initialize to true
      this.lazy = !!options.lazy;
    }
    this.getter = expOrFn;
    // Initialize to true
    this.dirty = this.lazy;
    // The default is undefined
    this.value = this.lazy ? undefined : this.get();
  }
  update() {
    if (this.lazy) {
      // When the calculated dependent data changes, dirty will be set to true
      this.dirty = true;
    }
  }
  evaluate() {
    // Retrieve value
    this.value = this.get();
    this.dirty = false;
  }
}

v-if and v-for do not appear at the same time

The v-for instruction is used to loop through the list. v-if is used to hide components. Components hidden with v-if will not execute internal rendering logic. Let's look at the following code:

<template>
<div>
<div v-for='(item,index) in list' v-if='index%2===0' class='item'>{{item}}</div>
<div>
</template>
<script>
export default {
  data(){
    return {
      list:[1,2,3,4,5,6,7,8,9,10]
    }
  }
}
</script>

When v-if and v-for appear at the same time, the priority of v-for will be higher than that of v-if. That is to say, the div of class='item 'will be rendered into 10 divs first, and then judge whether the index number is even or not. Five (5 odd) renderings are useless. Five times of useless work will undoubtedly cause a waste of performance. Therefore, we can filter out the data that does not need to be displayed with the help of computed, and then use the v-for circular list. The code is as follows:

<template>
<div>
<div v-for='(item,index) in showList' class='item'>{{item}}</div>
<div>
</template>
<script>
export default {
  data(){
    return {
      list:[1,2,3,4,5,6,7,8,9,10]
    }
  },
  computed:{
    showList(){
      const list = this.list
      return list.filter((item,index)=>index%2===0)
    }
  }
}
</script>

Sometimes we need to control whether the list is displayed according to a field. The code is as follows:

<template>
<div>
<div v-for='(item,index) in list' v-if='show'>{{item}}</div>
<div>
</template>
<script>
export default {
  data(){
    return {
      list:[1,2,3,4,5,6,7,8,9,10],
      show:false
    }
  }
}
</script>

As can be seen from the above, show is false, which means that 10 meaningless renderings have been made. We can separate the v-for and v-if instructions and let the v-if execute first, so that we won't make 10 meaningless renderings. The code is as follows:

<template>
<div>
<template v-if='show'>
<div v-for='(item,index) in list' >{{item}}</div>
</tempalte>
<div>
</template>
<script>
export default {
  data(){
    return {
      list:[1,2,3,4,5,6,7,8,9,10],
      show:false
    }
  }
}
</script>

Let's take a look at the source code of vue:

function genElement(el: ASTElement, state: CodegenState): string {
  if (el.parent) {
    el.pre = el.pre || el.parent.pre;
  }

  if (el.staticRoot && !el.staticProcessed) {
    // Static node
    return genStatic(el, state);
  } else if (el.once && !el.onceProcessed) {
    // v-once instruction
    return genOnce(el, state);
  } else if (el.for && !el.forProcessed) {
    // v-for instruction
    return genFor(el, state);
  } else if (el.if && !el.ifProcessed) {
    // v-if instruction
    return genIf(el, state);
  } else if (el.tag === "template" && !el.slotTarget && !state.pre) {
    return genChildren(el, state) || "void 0";
  } else if (el.tag === "slot") {
    return genSlot(el, state);
  } else {
    // ...
  }
}

From the above if else judgment conditions, we can see that the execution of v-for takes precedence over v-if

Data that does not need to be rendered in the view should not be written in the data

The data rendered in the view refers to the data used in the < template > < / template > HTML template, which are all responsive data. The data defined in the data field will become responsive data, but some data do not need to be displayed in the view, so do not declare the data in the data. For example, paging parameters need to be used for rolling loading in the mobile terminal, and these paging parameters should not be written in the data (actually, as far as I can see, many people like the paging parameters pageSize and pageIndex written in data).

The code before optimization is as follows:

export default {
  data() {
    return {
      pageSize: 10,
      pageIndex: 1,
    };
  },
  methods: {
    getList() {
      axios
        .get("xxx", {
          params: { pageSize: this.pageSize, pageIndex: this.pageIndex },
        })
        .then(() => {});
    },
    scrollBottom() {
      this.pageIndex += 1;
      this.getList();
    },
  },
};

As we can see above, pageSize and pageIndex are defined in data, which means that these two data will become responsive data, but in fact, pageSize and pageIndex do not need to be in the view. When we read pageSize and pageIndex, we will follow the logic of getter dependency collection. When we write, setter will notify the logic of update, Since there is no need to feed back to the view, the logic in getters and setters is doing useless work and consuming performance.

The optimized code is as follows:

export default {
  created() {
    this.pageSize = 10;
    this.pageIndex = 1;
  },
  methods: {
    getList() {
      axios
        .get("xxx", {
          params: { pageSize: this.pageSize, pageIndex: this.pageIndex },
        })
        .then(() => {});
    },
    scrollBottom() {
      this.pageIndex += 1;
      this.getList();
    },
  },
};

As can be seen from the above, we hung pageSize and pageIndex on the instance this, so that we can access them in each function of the component and avoid turning them into responsive data.

Let's look at the source code of vue:

function initState(vm: Component) {
  vm._watchers = [];
  const opts = vm.$options;
  // Determine whether there is a data field
  if (opts.data) {
    // Initialize data in data field
    initData(vm);
  } else {
    observe((vm._data = {}), true /* asRootData */);
  }
}

function initData(vm: Component) {
  let data = vm.$options.data;
  data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
  const keys = Object.keys(data);
  let i = keys.length;
  while (i--) {
    const key = keys[i];
    // Proxy the data in data to this
    proxy(vm, `_data`, key);
  }
  // Convert data in data into responsive data
  observe(data, true /* asRootData */);
}

It can be seen from the above that during initData, the data in data will be proxied to this and converted into responsive data at the same time. Therefore, some data that need to be shared but do not need response should not be written in the data field, otherwise useless operations will be carried out when the data is read and written. We can mount it directly on the component instance this at an appropriate time Some variables to improve performance.

key in v-for

In v-for, we set the key to give vnodes a unique identifier, identify whether the old and new vnodes are the same vnode, and quickly find the changes of the old and new vnodes. When we do not set it, the default is undefined, that is, each item in v-for has the same key value.

Let's first look at the logic of the sameVnode function in the vue source code:

function sameVnode(a, b) {
  return (
    a.key === b.key &&
    a.asyncFactory === b.asyncFactory &&
    ((a.tag === b.tag &&
      a.isComment === b.isComment &&
      isDef(a.data) === isDef(b.data) &&
      sameInputType(a, b)) ||
      (isTrue(a.isAsyncPlaceholder) && isUndef(b.asyncFactory.error)))
  );
}

As you can see,

  • When judging whether the old and new vnodes are the same vnode, we will first judge whether the key values in the old and new vnodes are the same. The difference is different vnodes. If they are the same, we will continue to make more complex logical judgments later.
  • When we do not set the key value in v-for, that is, the key value is undefined, that is, a.key = = = b.key - > undefined = = = undefined - > true. Whether the old and new vnodes are the same or different vnodes, more complex logical judgment will follow.
  • When we set the key value, and each key is different. When the old and new vnodes are not the same vnodes, a.key = = = b.key - > false, we can directly return false when comparing the key values. There is no need to go through more complex logical judgment. Only when the old and new vnodes are the same, that is, a.key = = = b.key - > true, we can go through more complex judgment Logical judgment.
  • Compared with not setting a key, setting a key mainly reduces the complex logical judgment process behind a.key === b.key, thus reducing the execution time of js.

When we set the key value, to ensure the uniqueness of the key value, we'd better not use the array index as the key value, which may cause some bug s. Let's take a look at the following code that uses the array index as the key value

list-card.vue

<template>
  <div :class="{ red: isClick }" @click="isClick = true">
    {{ label }}
  </div>
</template>
<script>
export default {
  props: ["label"],
  data() {
    return {
      isClick: false,
    };
  },
};
</script>
<style>
.red {
  color: red;
}
</style>

app.vue

<template>
  <div id="app">
    <list-card v-for="(item, index) in list" :key="index" :label="item" />
    <button @click="onClick">add to</button>
  </div>
</template>
<script>
import ListCard from "./component/list-card.vue";
export default {
  name: "App",
  components: {
    ListCard,
  },
  data() {
    return {
      list: [1, 2, 3, 4, 5, 6],
    };
  },
  methods: {
    onClick() {
      this.list.unshift(this.list.length + 1);
    },
  },
};
</script>

Above, we can see that the list card component maintains an isClick variable. When you click the component, the component font will turn red. When we click the Add button, we will add elements at the top of the array. Now let's do the following:

  • Click the third element, that is, the component with label=3. At this time, the font of the component with label=3 turns red
  • Click the Add button to add elements to the top of the array. At this time, the list array becomes [7,1, 2, 3, 4, 5, 6]
  • At this time, our ideal effect should be the fourth element, that is, the component with label=3 should be in red font. But the actual effect is that the third element, that is, the component with label=2, becomes a red font

keep-alive

In the actual development process, each time we render the route, we will re render the components, which will go through the processes of render and patch. If the component logic is complex or the nesting is deep, the whole rendering process will be very time-consuming.

We can use the keep alive component to wrap the router view, so that we can cache the rendered routing components. The next time we need to render components, we will not go through the created, mounted and other lifecycle functions, but will trigger the activated and deactivated lifecycle functions. Keep alive can be used to cache not only routing components, but also dynamic components. This is an optimization method that uses space for time.

The main parameters of keep alive are as follows:

  • include: string or regular expression. You can also use an array to specify which components to cache
  • exclude: string or regular expression. You can also use array to specify which components are not cached
  • max: maximum cached component instance

Note: when you use the include and exclude parameters, the component must have the name option. Without the name option, it will match its local registered name (the key value of the parent component components option). Anonymous components cannot be matched.

Code example:

<template>
  <!-- Comma separated string -->
  <keep-alive include="a,b">
    <router-view></router-view>
  </keep-alive>

  <!-- regular expression  (use `v-bind`) -->
  <keep-alive :include="/a|b/">
    <component :is="view"></component>
  </keep-alive>

  <!-- array (use `v-bind`) -->
  <keep-alive :include="['a', 'b']">
    <component :is="view"></component>
  </keep-alive>
</template>

A common scenario is to jump from a list to a detail page in the mobile terminal, and then return to the list from the detail page, which needs to be restored to the state when the list last entered the detail page.

Progressive rendering

Progressive rendering is to delay rendering components in batches, that is, render component A first and render component B after A few seconds. When there are many components on A page, the browser needs to render more DOM at the same time, which will give people the feeling of page jamming.

Let's see how to implement batch delay rendering

<template>
  <div>
    <component-a v-if="defer(1)"></component-a>
    <component-b v-if="defer(5)"></component-b>
  </div>
</template>
<script>
const count = 10;
export default {
  data() {
    return {
      displayPriority: 0,
    };
  },
  mounted() {
    this.startDisplayPriority();
  },
  methods: {
    startDisplayPriority() {
      const step = () => {
        requestAnimationFrame(() => {
          this.displayPriority++;
          if (this.displayPriority < count) {
            step();
          }
        });
      };
      step();
    },
    defer(priority) {
      return this.displayPriority >= priority;
    },
  },
};
</script>

We can see that a displayPriority variable is maintained inside the component, and then it is automatically increased to count at most when each frame is rendered through requestAnimationFrame. Then you can control the order of rendering through v-if="defer(xxx)". The smaller the XXX value, the higher the rendering priority. Here, you can only use v-if to control the priority of rendering. You can't use v-show, because v-show will directly render components, but hide the styles.

When there are many components or time-consuming components on your page, using this progressive rendering method can avoid the phenomenon of rendering stuck due to too long js execution time or rendering a large number of DOM elements at one time, and improve the user experience.

Time slice cutting

When we submit a lot of data at one time, the internal js execution time is too long, blocking the UI thread and causing the page to get stuck.

Generally speaking, we add a loading effect when submitting a large amount of data, but when there is a large amount of data, because js execution time is too long and UI thread cannot execute, our loading effect is static rather than dynamic. This is that we can split it into multiple time slices to submit data, so as to shorten the running time of a single js, so that the UI thread will not be blocked, and our loading effect will have a chance to move.

Let's take a look at the time slice cutting technology

function splitArray(items, splitCount) {
  const arr = [];
  const count = Math.ceil(items.length / splitCount);
  for (let i = 0; i < count; i++) {
    arr.push(items.slice(i * splitCount, (i + 1) * splitCount));
  }
  return arr;
}
    fetchData({ commit }, { list, splitCount }) {
      // Splitting data into multiple data blocks
      const chunkList = splitArray(list,splitCount);
      const step = (index) => {
        if (index >= chunkList.length) {
          return;
        }
        // requestAnimationFrame will execute the incoming callback function after each redrawing of the browser, so that the UI thread has the opportunity to execute. Of course, you can also use setTimeout instead of requestAnimationFrame
        requestAnimationFrame(() => {
        // Submit a part of data for each frame in combination with requestAnimationFrame
          const chunk = chunkList[index];
          commit('addItems',chunk)
          index += 1;
          step(index);
        });
      };
      step(0);
    }

Component lazy loading

Lazy component loading is a combination of asynchronous components and the code splitting function of webpack. In this way, the component code can be cut into multiple files, and the code can be loaded only when the component is really used.

Code example:

const comp = (/* webpackChunkName: "comp" */)=>import('./my-component')

Vue.Component('comp',comp)

We can see that there is a parameter of webpackChunkName: "comp", which is used to specify the file name of the code block, that is, the packed file name is comp. Here, I suggest that you package the components of the same module into the same file (as long as the value of webpackChunkName is the same, you can package them into the same file). In fact, I see that many people like that a component is a file. In fact, the packaged file is only a few kb. Send a network request to request the request header carried by the file, The request body and other data may be larger than this file, which will cause unnecessary waste.

summary

Through this article, I hope you can apply these optimization techniques to the actual project development, which can bring you a lot of benefits. Of course, in addition to the above optimization techniques, there are also performance optimization methods such as lazy image loading and virtual scrolling.

Performance optimization should not be done blindly. You should consider what this optimization can bring, what benefits it can bring, and how much it costs. At the same time, we should also consider the possible risks. For example, keep alive above uses space in exchange for time. Once the number of cached components is large, memory is easy to overflow, so we should control the number of cached components.

The performance improvement is accumulated bit by bit, which does not mean that you can suddenly reduce the rendering time from 5 seconds to 3 seconds after making an optimization. In fact, in the actual development, there are many places we can optimize, but you may think it doesn't matter, the impact is small and can be ignored. However, when the project reaches a certain scale, these seemingly insignificant places will be accumulated, and quantitative change will form qualitative change, resulting in the decline of system performance. So many places where you seem indifferent and have little impact are often the breakthrough of performance bottleneck.

Finally, I hope you don't write for writing in your daily development. You should fully consider the performance impact of this line of code you write.

Topics: Javascript Vue.js Optimize