Avoid abusing this to read data in data in Vue

Avoid abusing this to read data in data in Vue

Preface

In Vue, the data option is a good thing. Once you throw the data in, you can read the data in the data through this anywhere in a Vue component. However, we must avoid abusing this to read data in data. This column will explain where to avoid abuse and what consequences will result from abuse.

1. The process of using this to read data in data

In the Vue source code, getter functions and setter functions are added to the data to convert it into a responsive one. The getter function code is as follows:

function reactiveGetter() {
 var value = getter ? getter.call(obj) : val;
 if (Dep.target) {
  dep.depend();
  if (childOb) {
   childOb.dep.depend();
   if (Array.isArray(value)) {
    dependArray(value);
   }
  }
 }
 return value
}

When this is used to read data in data, the getter function will be triggered, in which var value = getter ? getter.call(obj) : val; is used to obtain the value and then return value is executed to achieve the purpose of reading data.

A conclusion can be drawn here that when Dep.target exists, using this to read data in data will collect dependencies. If this is abused to read data in data, dependencies will be collected repeatedly, causing performance problems.

2. When does Dep.target exist?

Dep.target is assigned by the dependency. Dependencies are also called Watchers or Subscribers. There are three types of dependencies in Vue, two of which are very common, namely watch (listener) and computed (computed properties). There is also a hidden dependency, rendering Watcher, which is created during the first rendering of the template.

Dep.target is assigned when the dependency is created, and the dependency is created using the constructor Watcher.

function Watcher(vm, expOrFn, cb, options, isRenderWatcher) {
 //...
 if (typeof expOrFn === 'function') {
  this.getter = expOrFn;
 } else {
  this.getter = parsePath(expOrFn);
 }
 this.value = this.lazy ? undefined : this.get();
};
Watcher.prototype.get = function get() {
 pushTarget(this);
 try {
  value = this.getter.call(vm, vm);
 } catch (e) {
  
 }
 return value
};
Dep.target = null;
var targetStack = [];
function pushTarget(target) {
 targetStack.push(target);
 Dep.target = target;
}

In the constructor Watcher, the instance method get will be executed at the end, and the assignment of Dep.target to pushTarget(this) will be executed in the instance method get .

The dependency is created when the Vue page or component is first rendered, so the performance problem should be the problem of slow first rendering.

3. Where to abuse this to read data in data

When Dep.target exists, executing these codes that abuse this to read data in data will cause performance problems, so we must also figure out where these codes are written so that they will be executed. In other words, we must figure out where the abuse of this to read data in data will cause performance problems.

In the second section, it is introduced that after Depth.target is assigned value = this.getter.call(vm, vm) will be executed, where this.getter is a function. If this is used to read data, dependencies will be collected. If abused, performance problems will occur.

this.getter is assigned during the dependency creation process, and this.getter for each dependency is different. Let me introduce them one by one below.

  • this.getter that watch (listener) depends on is the parsePath function, and its function parameter is the object being listened to.
var bailRE = new RegExp(("[^" + (unicodeRegExp.source) + ".$_\\d]"));
function parsePath(path) {
 if (bailRE.test(path)) {
  return
 }
 var segments = path.split('.');
 return function(obj) {
  for (var i = 0; i < segments.length; i++) {
   if (!obj) {
    return
   }
   obj = obj[segments[i]];
  }
  return obj
 }
}

In the code below, passing a and abc as parameters to the parsePath function will return a function assigned to this.getter . Executing this.getter.call(vm, vm) will get the values ​​of this.a and this.abc . In this process, there will be no scenario where this is abused to read data in data.

watch:{
 a:function(newVal, oldVal){
 //Do something}
}
vm.$watch('abc', function (newVal, oldVal) {
 // do something })
  • There are two types of this.getter that computed (calculated properties) rely on. If the value of the calculated property is a function, then this.getter is this function. If the value of the calculated property is an object, then this.getter is the get property value of this object, and the get property value is also a function. In this function, there may be a scenario where this is abused to read data in data. For example, the code is as follows.
computed:{
 d:function(){
  let result = 0;
  for(let key in this.a){
   if(this.a[key].num > 20){
    result += this.a[key].num + this.b + this.c;
   }else{
    result += this.a[key].num + this.e + this.f;
   }
  }
  return result;
 }
}

In the calculated property d, there is an abuse of this to read data. Here, this.a is an array. At this time, the value of Dep.target is the dependency of the calculated property d. In the loop this.a , this is used to obtain the data of a, b, c, e, and f, so that these data undergo a series of complex logical operations to repeatedly collect the dependency of the calculated property d. This slows down the process of obtaining the value of the calculated property d, causing performance issues.

  • this.getter of the rendering Watcher is a function as follows:
updateComponent = function() {
 vm._update(vm._render(), hydrating);
};

Among them, vm._render() will convert the rendering function render generated by the template template into a virtual DOM(VNode):vnode = render.call(vm._renderProxy, vm.$createElement); , let’s take an example to illustrate what the rendering function render is.

For example, template template:

<template>
 <div class="wrap">
 <p>{{a}}<span>{{b}}</span></p>
 </div>
</template>

The rendering function render will be generated by vue-loader, as shown below:

(function anonymous() {
 with(this) {
  return _c('div', {
   attrs: {
    "class": "wrap"
   }
  }, [_c('p', [_v(_s(a)), _c('span', [_v(_s(b))])])])
 }
})

The function of the with statement is to specify a default object for one or a group of statements. For example with(this){ a + b } is equivalent to this.a + this.b Then using {{ a }} in the template is equivalent to using this to read the data a in data. Therefore, in the rendering function render generated by the template, there may be scenarios where this is abused to read data in data. For example, the code looks like this:

<template>
 <div class="wrap">
 <div v-for=item in list>
  <div> {{ arr[item.index]['name'] }} </div>
  <div> {{ obj[item.id]['age'] }} </div>
 </div>
 </div>
</template>

When using v-for to loop the list array, this is used to read the data of arr and obj in data, so that these data undergo a series of complex logical operations to repeatedly collect this dependency, which slows down the initial rendering and causes performance problems.

4. How to avoid abusing this to read data in data

In summary, abusing this to read data in data in calculated properties and templates will lead to repeated collection of dependencies, resulting in performance problems. How can we avoid this situation?

  • How to avoid in computed properties

Use ES6 object deconstruction assignment to avoid this. The value of the calculated property is a function whose parameter is Vue's instantiated this object. In the above example of abusing this in the calculated property, it can be optimized like this.

Before optimization:

computed:{
 d:function(){
  let result = 0;
  for(let key in this.a){
   if(this.a[key].num > 20){
    result += this.a[key].num + this.b + this.c;
   }else{
    result += this.a[key].num + this.e + this.f;
   }
  }
  return result;
 }
}

After optimization:

computed: {
 d({ a, b, c, e, f }) {
 let result = 0;
 for (let key in a) {
  if (a[key].num > 20) {
  result += a[key].num + b + c;
  } else {
  result += a[key].num + e + f;
  }
 }
 return result;
 }
}

In the above, destructuring assignment is used to assign a, b, c, e, and f in data to corresponding variables a, b, c, e, and f in advance. Then, data can be accessed through these variables in the calculated properties without triggering the dependency collection of the corresponding data in data. In this way, the data in data is read only once using this, and dependency collection is triggered only once, avoiding performance problems caused by repeated dependency collection.

  • How to avoid in template

Process the data used by the v-for loop in advance, and do not read array or object type data in the v-for loop. In the above example of abusing this in the template template, it can be optimized like this.

Assuming that list, arr, and obj are all data returned by the server, and arr and obj are not used in any module rendering, they can be optimized like this.

Before optimization:

<template>
 <div class="wrap">
 <div v-for=item in list>
  <div> {{ arr[item.index]['name'] }} </div>
  <div> {{ obj[item.id]['age'] }} </div>
 </div>
 </div>
</template>

After optimization:

<template>
 <div class="wrap">
 <div v-for=item in listData>
  <div{{item.name}} </div>
  <div>{{item.age}}</div>
 </div>
 </div>
</template>
<script>
const arr = [];
const obj = {}
export default {
 data() {
 return {
  list: [],
 }
 },
 computed: {
 listData: function ({list}) {
  list.forEach(item => {
  item.name = arr[item.index].name;
  item.age = obj[item.id].age;
  })
  return list;
 }
 },
}
</script>

The above is the details of avoiding the abuse of this to read data in data in Vue. For more information on avoiding the abuse of this in Vue, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Detailed explanation of the implementation principle of Vue2.0/3.0 two-way data binding
  • Analysis on the problem of data loss caused by forced refresh of vuex
  • How does Vue track data changes?
  • Vue+canvas realizes the effect of refreshing waterfall chart from top to bottom in real time (similar to QT)
  • Solution to Vue data assignment problem
  • Vue resets data to its initial state
  • Analysis and solution of data loss during Vue component value transfer
  • SpringBoot+Vue realizes data adding function
  • Handwritten Vue2.0 data hijacking example
  • Vue data two-way binding implementation method
  • Design a data collector with vue

<<:  Solution to the low writing efficiency of AIX mounted NFS

>>:  How to upgrade MySQL 5.6 to 5.7 under Windows

Recommend

Introduction to JavaScript Number and Math Objects

Table of contents 1. Number in JavaScript 2. Math...

How to reference jQuery in a web page

It can be referenced through CDN (Content Delivery...

js realizes horizontal and vertical sliders

Recently, when I was doing a practice project, I ...

Three.js sample code for implementing dewdrop animation effect

Preface Hello everyone, this is the CSS wizard - ...

The 6 Most Effective Ways to Write HTML and CSS

This article shares the 6 most effective methods,...

Steps to create a WEBSERVER using NODE.JS

Table of contents What is nodejs Install NodeJS H...

How to modify the master-slave replication options in MySQL online

Preface: The most commonly used architecture of M...

Implementation of whack-a-mole game in JavaScript

This article shares the specific code for JavaScr...

Calling the search engine in the page takes Baidu as an example

Today, it suddenly occurred to me that it would be...

Basic implementation method of cross-component binding using v-model in Vue

Hello everyone, today we will talk about how to u...

Solution for FileZilla 425 Unable to connect to FTP (Alibaba Cloud Server)

Alibaba Cloud Server cannot connect to FTP FileZi...

Implement MySQL read-write separation and load balancing based on OneProxy

Introduction Part 1: Written at the beginning One...