1. Why can’t the view update the render after updating the data?

Users of Vue will inevitably encounter such a “pit”

Why can’t THE view render when I update the data?

For example

<! -- Using vue-cli scaffolding -->

<template>

  <div>

    <button @click="change"</button>

    <button @click="add"> Add object attributes </button>

    <div v-for="(val, key) in obj" :key="key">

      {{ val }}

    </div>

  </div>

</template>



<script>

export default {

  data () {

    return {

      obj: {

        a: '1'.

        b: '2'.

        c: '3'

      }

    }

  },

  methods: {

    change () {

      this.obj.a = '0'

    },

    add () {

      this.obj.d = '4'

    }

  }

}

</script>

Copy the code

When I click the “Change Object Properties” button, the view is updated

Instead, I click the add Object Properties button, and the view doesn’t update


Before opening the vue-devTools tool

We can see that the data has been updated!

Only the view is not updated!

So why is that?

In fact, this is the responsivity principle of the Vue involved

In Vue2. X, the reactive core API is Object.defineProperty

The following with a section of JS code, simulation vUE responsive source code

// Define an empty object

  const propsObj = {}

  

// Define the listener

  const obj = {

    a: '1'.

    b: '2'.

    c: '3'

  }

  

// Simulate update view

  const updateView = (key, oldVal, newVal) => {

Console. log(' View updated ~ ')

The console. The log (` key values${key}Update from${oldVal}Update to the${newVal} `)

  }

  

// define listener responsive methods

  const  defineReactive = (target, key, value) => {

/ / core API

    Object.defineProperty(target, key, {

      get() {

        return value

      },

      set(newValue) {

        updateView(key, value, newValue)

        value = newValue

      }

    })

  }

  

// Iterate over the setup listener

  for (const key of Object.keys(obj)) {

    defineReactive(propsObj, key, obj[key])

  }

  

// Update data

  propsObj.a = '0'// Trigger response

  propsObj.d = '4'// Add object attributes without triggering responsiveness

Delete propsobj. a // Deletes object properties without triggering reactive type

Copy the code

Output result:

visible

If you want to trigger reactive updates

You want to make sure that the properties of the object trigger the response

In the first example, the “Add Object Properties” button is clicked and the view is not updated

Because he’s adding an attribute to the object

Of course! Also note that the delete operation cannot trigger responsiveness either!

To sum up:

1. Make sure that the object property changed is an existing property of the object to listen to the response

2. When you add or delete object attributes, you cannot listen in response mode

In fact, it is clearly stated on the official website of Vue


Arrays are much the same


The “small difference” of arrays is that:

Vue overrides these array methods so that they can trigger view updates


2. The solution?

① Use vue. set/vue. delete

Use Vue’s API specifically for this type of problem

<! -- Using vue-cli scaffolding -->

<template>

  <div>

    <button @click="change"</button>

    <button @click="add"> Add object attributes </button>

    <div v-for="(val, key) in obj" :key="key">

      {{ val }}

    </div>

  </div>

</template>



<script>

export default {

  data () {

    return {

      obj: {

        a: '1'.

        b: '2'.

        c: '3'

      }

    }

  },

  methods: {

    change () {

      this.obj.a = '0'

    },

    add () {

// Add object methods

      this.$set(this.obj, 'd'.'4')

    }

  }

}

</script>

Copy the code

Of course, delete is also similar, not to repeat

Note the Vue version: 2.20+

(2) use the vm. $forceUpdate

Use force update views

<! -- Using vue-cli scaffolding -->

<template>

  <div>

    <button @click="change"</button>

    <button @click="add"> Add object attributes </button>

    <div v-for="(val, key) in obj" :key="key">

      {{ val }}

    </div>

  </div>

</template>



<script>

export default {

  data () {

    return {

      obj: {

        a: '1'.

        b: '2'.

        c: '3'

      }

    }

  },

  methods: {

    change () {

      this.obj.a = '0'

    },

    add () {

      this.obj.d = '4'

// Force an update to the view

      this.$forceUpdate(a)

    }

  }

}

</script>

Copy the code

③ Use deep copy and reassign to replace

For objects, deep copy can use json.parse (json.stringify (obj))

As long as the change object is not the same object

Because we’re reassigning, it shouldn’t be the same object

That’s how reactive is triggered

For example

<! -- Using vue-cli scaffolding -->

<template>

  <div>

    <button @click="change"</button>

    <button @click="add"> Add object attributes </button>

    <div v-for="(val, key) in obj" :key="key">

      {{ val }}

    </div>

  </div>

</template>



<script>

export default {

  data () {

    return {

      obj: {

        a: '1'.

        b: '2'.

        c: '3'

      }

    }

  },

  methods: {

    change () {

      this.obj.a = '0'

    },

    add () {

// Const obj = this.obj is not available

// Reason: They are identical and refer to the same object

/ / conso namely; e.log(obj === this.obj) //true

// Make a deep copy of the object using sequence, deserialization, and reassign it

      const obj = JSON.parse(JSON.stringify(this.obj))

      obj.d = '4'

      this.obj = obj

    }

  }

}

</script>

Copy the code

For arrays, you can use methods like slice, concat, and so on


Thank you for reading