preface

In the background management system I was responsible for, the two functions of adding staff and editing staff shared one component, but I encountered a problem. Using v-for to render some tags, when using the editor function, delete the object array element and the corresponding tag will disappear on the page. However, when you use the add people function, you delete the object array element and the corresponding tag does not disappear on the page.

Personal problems remain:

this.$set(this.numbers,index,++this.numbers[index]);
this.$set(this.numbers,index,1); I don't have to use this every time.$setTo receive changed data, think you can hang up once?Copy the code
The Object.defineProperty() and reactive principles still need to be explored, as well as the data attributes and browser attributes of Object.defineProperty()Copy the code

Problem Description:

  • Enter the Add People page first

  • Click Add Department

  • Click OK and render the array data to push with V-for

  • And then finished the son! By clicking X, the array element is reduced, but none of the sections on the page are reduced. Because I just defines a person is empty in the data object, confirm the event trigger in use for this. The person. DeparmentList = DeparmentListArray selection page to receive the department of array data, enclosing object. DeparmentList response type object is just a common property, no response, also won’t be vue detected, view also failed to update. You should use this.$set(this.person, deparmentList, deparmentListArray); I am a piece of shit! It just calls data driven view.

Demo1: Object array

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
  <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
  <style>
    li:hover {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div 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>
  </div>
  <script>
    var vm = new Vue({
      el: ".wrap",
      data: {
        numbers: [],
        items: [
          {name: 'jjj'},
          {name: 'kkk'},
          {name: 'lll'},
        ]
      },
      methods: {
        handle: function(index) {// WHY: Update the data, the view layer is not rendered, but the console array shows that the data is actually updatedif (typeof(this.numbers[index]) === "undefined" ) {
             this.numbers[index] = 1;
           } else{ this.numbers[index]++; } console.log(this.numbers); }}}); </script> </body> </html>Copy the code

The goal here is very clear – I want to check if LI exists when I click on it, which it doesn’t, so I set the value to 1, and if I click again, the numbers add up. However, the problem is that the number is not updated in the View layer after the click, and it is found through the console printing that the data is actually updated, but the View layer did not detect it in time, and I have been thinking: Since vUE implements two-way data binding, why isn’t it updated in the View layer after changes are made in the Model layer?

First, I consider whether this is an array, so I test the following example:

Demo2: Non-object array

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
  <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
  <style>
    li:hover {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div 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>
  </div>
  <script>
    var vm = new Vue({
      el: ".wrap",
      data: {
        numbers: [],
        items: [
          {name: 'jjj'},
          {name: 'kkk'},
          {name: 'lll'},
        ]
      },
      methods: {
        handle: function(index) {// Instead of an array, update the data to render this.items[index].name += in the View layer" success"; }}}); </script> </body> </html>Copy the code

When I tested it again, I found that when the Model layer changed, the View layer was updated in a timely and effective manner.

Why not arrays?

The following description was found in an obscure place on the document:

If the object is reactive, ensure that the property is also created reactive and trigger view updates. This method is mainly used to get around the limitation that the Vue cannot detect that the property is being added.

Root data object of Vue instance: Data is the root data object of Vue instance, and Vue will recursively convert data's properties to getters/setters so that data's properties can respond to data changes. It is recommended that all root-level reactive attributes be declared before the instance is created. This is subject to practical demonstration!

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

Pass a normal JavaScript Object to the Vue instance’s data option, and Vue iterates through all of the Object’s properties and converts them into getters/setters using Object.defineProperty. Object.defineproperty is a feature that is only supported by ES5 and cannot be shim, which is why Vue does not support IE8 and earlier browsers.

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

Reference article:

What is the Object. DefineProperty ()

Data properties versus accessor properties

<! 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 Call book. Year =2005; // called when assigning accessor propertiessetFunction of the console. The log (book. Edition); // 2 </script> </body> </html>Copy the code

This example should give you a good understanding of accessor properties.

Therefore, when the accessor property value of the object changes (vUE converts the properties to accessor properties, as mentioned earlier), the set function is called. Vue can use this set function to track the change and call related functions to update the view.

Each component instance has a corresponding Watcher instance object, which records properties as dependencies during component rendering and then, when setters for dependencies are called, informs Watcher to recalculate, causing its associated components to be updated.

OK! Now that we know how this works, we can take a closer look at why the previous array problem occurred!

Change detection problem

The Object. Observe () method is not well supported. Vue cannot detect the addition or deletion of objects. However, Vue performs setter/getter conversion on the property when it initializes the instance, so the property must be on the object to begin with in order for Vue to convert it.

When we add an attribute and assign a value, Vue does not detect the addition or deletion of an attribute to the object, but it does, so we can see the change through the console, so there is no way to be responsive. In the second example, we make changes to existing properties that perform setter/getter transformations when Vue initializable instances, so their changes are valid and model data is available in the View layer in real time.

What is Object.observe()?

Before I go into this, I have to be cruel to say that although this method works on some browsers, the fact is that this method is obsolete!

Overview: This method is used to monitor changes to an object asynchronously. The method’s callback provides an orderly flow of changes when an object’s properties are modified, but the interface has been removed from major browsers and generic proxy objects can be used.

Methods:

Object.observe(obj, callback[, acceptList])

The obj is the object to be monitored, and the callback is a callback function with parameters including CHANGES and acceptList. Changes is an array, each of which represents a modification action. Each object that modifies the behavior contains:

  • Name: indicates the name of the modified attribute.
  • Object: modified value of the object.
  • Type: indicates the type of modification made to the object. Possible values are “add”, “update”, or “delete”.
  • OldValue: the value of the object before modification. This value is valid only for “update” and “delete”.

AcceptList List of change types to monitor for a given callback on a given object. If omitted, [“add”, “update”, “delete”, “reconfigure”, “setPrototype”, “preventExtensions”] will be used.

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 the code

Documentation :Object.ovserve()

Object.observe() sparks the data binding revolution

The solution

Add response properties to nested objects using the vue.set (Object, key, value) method. You can also use the vm.$set instance method, which is also an alias for the global vue.set method.

Examples of code to resolve:

<! DOCTYPE html> <html lang="en">
<head>
  <meta charset="UTF-8">
  <title>vue</title>
  <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
  <style>
    li:hover {
      cursor: pointer;
    }
  </style>
</head>
<body>
  <div 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>
  </div>
  <script>
    var vm = new Vue({
      el: ".wrap",
      data: {
        numbers: [],
        items: [
          {name: 'jjj'},
          {name: 'kkk'},
          {name: 'lll'},
        ]
      },
      methods: {
        handle: function(index) {// WHY: Update the data, the view layer is not rendered, but the console array shows that the data is actually updatedif (typeof(this.numbers[index]) === "undefined" ) {
             this.$set(this.numbers, index, 1);
           } else {
             this.$set(this.numbers, index, ++this.numbers[index]); }}}}); </script> </body> </html>Copy the code

In this way, we can achieve the ultimate goal!

The above section refers to the array under data, and if the array is in store, it can be like this:

[ADD_ONE] (state, index) {
      if ( typeof state.numbers[index] == "undefined") {
        Vue.set(state.numbers, index, 1)
      } else {
        Vue.set(state.numbers, index, ++state.numbers[index])
      }
    }
Copy the code

Vue. Set () is used to change and add.

Note: this is to determine the increase and decrease of index, so use vue.set ()

If we’re in store actions we need to populate the array in stroe

The method is as follows:

The state content:

kindnames: []

The Mutations content:

    [ADD_KIND_NAME] (state, name) {
      state.kindnames.push(name);
    } 
Copy the code

Note: Push is used directly here

Of course, in addition to push, we can shift and so on.

Contents of actions:

commit(ADD_KIND_NAME, state.items[index++].name);
Copy the code

Here, state.items[index++].name gets strings one by one.

Note: Also refer to the documentation – details and best practices

The author links: www.cnblogs.com/zhuzhenwei9…