Home > Web Front-end > JS Tutorial > body text

Array change detection problem in vue

php中世界最好的语言
Release: 2018-04-17 15:36:22
Original
1660 people have browsed it

This time I will bring you the problem of array change detection in vue. What are the precautions for array change detection in vue? The following is a practical case, let's take a look.

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <p class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </p>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     // WHY: 更新数据,view层未渲染,但通过console这个数组可以发现数据确实更新了
      if (typeof(this.numbers[index]) === "undefined" ) {
       this.numbers[index] = 1;
      } else {
       this.numbers[index]++;
      }
    }
   }
  });
 </script>
</body>
</html>
Copy after login
The implementation purpose here is very clear --- I hope to detect whether li exists when clicking it. Of course it does not exist, so I set the value to 1. If I click it again, let the number accumulate.

But the problem that arises is: after clicking, the number is not updated in the view layer, and through console printing, it is found that the data is indeed updated, but the view layer does not detect it in time. And what I have been thinking is: Since Vue implements two-way data binding, why is it not updated in the view layer after the model layer changes?

First, I considered whether this was an array problem, so I tested the following example:

Example 2

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
 <script src="https://unpkg.com/vue@2.3.3/dist/vue.js"></script>
 <style>
  li:hover {
   cursor: pointer;
  }
 </style>
</head>
<body>
 <p class="wrap">
  <ul>
   <li v-for="item,index in items" v-on:click="handle(index)">
    <span>{{item.name}}</span>
    <span>{{numbers[index]}}</span>
   </li>
  </ul>
 </p>
 <script>
  var vm = new Vue({
   el: ".wrap",
   data: {
    numbers: [],
    items: [
     {name: 'jjj'},
     {name: 'kkk'},
     {name: 'lll'},
    ]
   },
   methods: {
    handle: function (index) {
     // 不是数组,这里更新数据就可以直接在view层渲染
     this.items[index].name += " success";
    }
   }
  });
 </script>
</body>
</html>
Copy after login
At this time, when I tested again, I found that when the model layer here changes, the view layer can be updated in a timely and effective manner.

The most important sentence is---if the object is responsive, ensure that the

property is also responsive after it is created, and triggers a view update. This method is mainly used to avoid Vue from being able to detect the property. restrictions were added.

So under what circumstances can Vue fail to detect that attributes have been added? According to the reference link, we saw a good explanation in the document --- In-depth reactive principle

First, we need to understand how Vue implements two-way binding of data!

Pass a normal

JavaScript object to the data option of the Vue instance. Vue will traverse all the properties of this object and use Object.defineProperty Converts all these properties to getters/setters. Object.defineProperty is only ES5 supports but cannot shim features, which is why Vue does not support IE8 and lower browsers.

Knowledge supplement:

Accessor properties do not contain data values; they contain a pair of getter and setter functions (these two functions are not required). When the accessor property is read, the getter function is called, which is responsible for returning a valid value; when the accessor property is written, the setter function is called and the new value is passed in. This function is responsible for deciding how to process the data.

Accessor properties cannot be defined directly and must be defined using Object.defineProperty().

Here is an example:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>vue</title>
</head>
<body>
 <script>
  var book={
    _year:2004,
    edition:1
  };
  Object.defineProperty(book,"year",{
    get:function(){
      return this._year;
    },
    set:function(newValue){
      if(newValue>2004){
        this._year=newValue;
        this.edition+=newValue-2004;
      }
    }
  });
  console.log(book.year); // 2004 在读取访问器属性时会调用get函数
  book.year=2005; // 在给访问器属性赋值时会调用set函数
  console.log(book.edition); // 2
 </script>
</body>
</html>
Copy after login
This example should provide a good understanding of accessor properties.

Therefore, when the accessor attribute value under the object changes (Vue will convert the attributes into accessor attributes, as mentioned before), then the set function will be called, and then Vue can track the changes through this set function. , call related functions to update the view.

Each component instance has a corresponding watcher instance object, which records the properties as dependencies during component rendering. Later, when the dependency's setter is called, the watcher will be notified to recalculate, causing its associated components to be updated. . That is, during the rendering process, the getter function of the object attribute will be called, and then the getter function will notify the water object to declare it as a dependency. After the dependency, if the object attribute changes, the setter function will be called to notify the watcher, and the watcher will Re-render the component to complete the update.

OK! Now that we know the principle, we can further understand why the previous array problem occurred!

Change detection issues

Received by the limitations of modern JavaScript browsers, it is actually mainly Object.observe() The method support is not good, and Vue cannot detect the addition or deletion of objects. However, Vue performs the setter/getter conversion process on the property when initializing the instance, so the property must be on the object from the beginning in order for Vue to convert it.

所以对于前面的例子就不能理解了 --- 数组中index都可以看做是属性,当我们添加属性并赋值时,Vue并不能检测到对象中属性的添加或者删除,但是其的确是添加或删除了,故我们可以通过console看到变化,所以就没有办法做到响应式; 而在第二个例子中,我们是在已有的属性的基础上进行修改的,这些属性是在最开始就被Vue初始化实例时执行了setter/getter的转化过程,所以说他们的修改是有效的,model的数据可以实时的在view层中得到相应。

补充知识: 什么是 Object.observe() ?

在介绍之前,不得不残忍的说,尽管这个方法可以在某些浏览器上运行,但事实是这个方法已经废弃!

概述: 此方法用于异步地监视一个对象的修改。当对象的属性被修改时,方法的回调函数会提供一个有序的修改流,然而这个接口已经从各大浏览器移除,可以使用通用的proxy 对象。      

方法:

Object.observe(obj, callback[, acceptList])
Copy after login

其中obj就是被监控的对象, callback是一个回调函数,其中的参数包括changes和acceptList,

changes一个数组,其中包含的每一个对象代表一个修改行为。每个修改行为的对象包含:

  • name: 被修改的属性名称。

  • object: 修改后该对象的值。

  • type: 表示对该对象做了何种类型的修改,可能的值为"add", "update", or "delete"。

  • oldValue: 对象修改前的值。该值只在"update"与"delete"有效。

acceptList在给定对象上给定回调中要监视的变化类型列表。如果省略, ["add", "update", "delete", "reconfigure", "setPrototype", "preventExtensions"] 将会被使用。

var obj = {
 foo: 0,
 bar: 1
};
Object.observe(obj, function(changes) {
 console.log(changes);
});
obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]
obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]
delete obj.baz;
// [{name: 'baz', object: <obj>, type: 'delete', oldValue: 2}]
Copy after login

相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!

推荐阅读:

layer前端组件图片显示功能

web前端必看4本开发书籍

The above is the detailed content of Array change detection problem in vue. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template