Note:

  • Here’s what I found when I learned React that I couldn’t detect when modifying reference type data
  • Then I didn’t encounter these situations in VUE before
  • Vue and React both have this problem

1. Vue cannot detect that the instance does not exist when it is createddataThe property of the

Reason: Since Vue performs getter/setter conversions on property when initializing the instance, the property must exist on the data object for Vue to convert it to reactive.

Scene:

Template: '<div>{{message}}</div>'}) vm.message = 'Hello! '//' vm.message 'is not responsiveCopy the code

Solutions:

Var vm = new Vue({data: {// declare a and b to be null string message: ",}, template: '<div>{{ message }}</div>' }) vm.message = 'Hello! 'Copy the code

2. Vue cannot detect the addition or removal of object property

Reason: Official – Due to JavaScript (ES5) limitations, vue.js cannot detect additions or deletions of object attributes. Because vue.js converts a property to a getter/setter when initializing an instance, the property must be on the data object for vue.js to transform it in order for it to be responsive.

Scene:

Var vm = new Vue({data:{obj: {id: 001}}) '<div>{{obj.message}}</div>'}) vm.obj.message = 'hello' // Delete vm.obj.id // Not responsiveCopy the code

Solutions:

Set Vue. Set (vm.obj, propertyName, newValue) Object. Assign (this.obj, {a: 1, b: 2}) this.obj = object. assign({}, this.obj, {a: 1, b: 2}) this.obj = object. assign({}, this.obj, {a: 1, b: 1) Delete Vue. Delete (vm.obj, propertyName) // Delete vm.obj (vm.obj, propertyName)Copy the code

3. Vue cannot detect direct modification of an array item through an array index

Reason: Official – Due to JavaScript limitations, Vue cannot detect array and object changes; Especially rain Stream – performance costs are not proportional to the user experience.

Scene:

Var = new vm Vue ({data: {items: [' a ', 'b', 'c']}}) vm. The items [1] = 'x' / / not responsiveCopy the code

Solutions:

// Vue.set
Vue.set(vm.items, indexOfItem, newValue)

// vm.$set
vm.$set(vm.items, indexOfItem, newValue)

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
Copy the code

Extension: Object.defineProperty() can detect array changes

Object.defineproperty () can detect array changes. Adding an index attribute to an array will not detect data changes, because the index of the new array cannot be detected, nor will deleting an index attribute.

Scene:

var arr = [1, 2, 3, 4] arr.forEach(function(item, index) { Object.defineProperty(arr, index, { set: Function (value) {console.log(' trigger setter') item = value}, get: Function () {console.log(' trigger getter') return item}})}) arr[1] = '123' Arr [5] = 5 // Does not fire the setter and getterCopy the code

4. Vue cannot detect changes in the length of the array directly modified

Reason: Official – Due to JavaScript limitations, Vue cannot detect array and object changes; Especially rain Stream – performance costs are not proportional to the user experience.

Scene:

Var vm = new Vue({data: {items: ['a', 'b', 'c']}}) vm.items. Length = 2 // Not responsiveCopy the code

Solutions:

vm.items.splice(newLength)
Copy the code

5. Manipulating DOM data before an asynchronous update is executed does not change

Reason: Vue executes asynchronously when updating the DOM. As long as it listens for data changes, Vue opens a queue and buffers all data changes that occur in the same event loop. If the same watcher is triggered more than once, it will only be pushed into the queue once. This removal of duplicate data while buffering is important to avoid unnecessary computation and DOM manipulation. Then, in the next event loop, “TICK,” Vue refreshes the queue and performs the actual (de-duplicated) work. Vue internally attempts to use native Promise.then, MutationObserver, and setImmediate for asynchronous queues, and setTimeout(fn, 0) instead if the execution environment does not support it.

Scene:

<div id="example">{{message}}</div>
Copy the code
var vm = new Vue({ el: '#example', data: { message: $el.textContent === 'new message' // false vm.$el.style.color = 'red' // The page does not changeCopy the code

Solutions:

var vm = new Vue({ el: '#example', data: { message: '123'}}) vm.message = 'new message' // Change data // Use vue.nexttick (callback) Callback will be called vue.nexttick (function after DOM update is complete () {vm.$el.textContent === 'new message' // true vm.$el.style.color = 'red' // Text color becomes red})Copy the code

Extension: Data response misunderstandings caused by asynchronous updates

<! -- The page says: I update! --> <div id="example">{{message.text}}</div>Copy the code
var vm = new Vue({ el: '#example', data: { message: $nextTick(function () {this.message = {} this.message. '})Copy the code

In the previous code, we declared an empty message object in the data object, and then executed the following two pieces of code in the asynchronous callback that was triggered after the next DOM update loop:

this.message = {}; This.message. text = 'I update! 'Copy the code

At this point, the template has been updated, and the page will finally say I have updated! .

If you think that the template is updated and should be responsive, then you have made a mistake.

We initially declared a message empty object in the data object and did not have a text property, so the text property was not responsive.

But the template has actually been updated. What’s going on here?

This is because the DOM update of Vue. Js is asynchronous, that is, the directive does not update immediately after the setter operation occurs, and there is a delay in the instruction update operation. When the directive update is actually performed, the text property is already assigned, so the directive updates the template with a new value.

Each instruction/data binding in the template has a corresponding Watcher object, which records properties as dependencies during computation. Later, when the setter of the dependency is called, watcher recalculation is triggered, which causes its associated instructions to update the DOM.

The specific process is as follows:

  • performthis.message = {};When,setterIs invoked.
  • Vue. Js to trackmessageRely on thesetterWhen called, it fireswatcherRecalculate.
  • This.message. text = 'I update! '; 对 textProperty is assigned.
  • After the asynchronous callback logic completes execution, it causes its associated instructions to update the DOM, and the instruction update begins execution.

So the actual action that triggers a template update is this.message = {}; Because setters are triggered, the only layer of data with responsive properties is the message layer, which doesn’t have dynamically added properties.

Corresponding to the second point above – Vue cannot detect the addition or removal of object property

6. The loop nesting level is too deep and the view is not updated?

See some people on the Internet say that the data update level is too deep, causing the data not to update or update slowly, leading to the attempt not to update?

Since I have never encountered this situation, and when I tried to recreate it, it didn’t happen, SO I won’t say much about it (if anyone has ever encountered this in a real situation, leave a comment).

The solution to the above situation is to use forced updates:

If you find yourself needing a forced update in Vue, 99.9% of the time, you’ve done something wrong.

vm.$forceUpdate()
Copy the code

7. Extension: When routing parameters change, the page is not updated (data is not updated)

Expand a problem that the page does not update because of routing parameter changes. The page does not update essentially means that the data is not updated.

Reason: When a routing view component references the same component, when the route changes, the component cannot be updated, which is often referred to as the page cannot be updated.

Scene:

<div id="app">
  <ul>
    <li><router-link to="/home/foo">To Foo</router-link></li>    
    <li><router-link to="/home/baz">To Baz</router-link></li>    
    <li><router-link to="/home/bar">To Bar</router-link></li>    
  </ul>    
  <router-view></router-view>
</div>
Copy the code
const Home = {
  template: `<div>{{message}}</div>`,
  data() {
    return {
      message: this.$route.params.name
    }
  }
}

const router = new VueRouter({
  mode:'history',
    routes: [
    {path: '/home', component: Home },
    {path: '/home/:name', component: Home }
  ]
})

new Vue({
  el: '#app',
  router
})
Copy the code

In the previous code, we configured a dynamic route ‘/home/:name’ in the routes build option. They share the same route component home, which means they reuse the RouterView.

During route switching, the page will render only the parameters matched by the first route, and the message will remain unchanged during route switching.

Solutions:

There are many ways to solve the problem. Here are just a few of the ways I use.

  1. Watch for changes to $route.

    const Home = {
      template: `<div>{{message}}</div>`,
      data() {
        return {
          message: this.$route.params.name
        }
      },
      watch: {
           '$route': function() {
           this.message = this.$route.params.name
        }
        }
    }
    ...
    new Vue({
      el: '#app',
      router
    })
    Copy the code
  2. Bind the key attribute to the

    so that Vue thinks it is a different

    .

    Cons: We don’t need to worry about component updates if we jump from /home to /user, so the key property is unnecessary.

    <div id="app">
      ...
      <router-view :key="key"></router-view>
    </div>
    Copy the code

    The original address: segmentfault.com/a/119000002…