在Vue中,为何我更新了数据,视图却没法更新渲染?

1.更新了数据,视图却没法更新渲染的缘由?

使用Vue的小伙伴,都不免会遇到这么一个“坑”vue

为何我更新了数据,视图却没法渲染?vue-cli

举个例子api

<!--使用vue-cli脚手架-->
<template>
  <div>
    <button @click="change">改变对象属性</button>
    <button @click="add">添加对象属性</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>
复制代码

当我点击了 “改变对象属性” 按钮,视图发生了更新数组

而我点击了 “添加对象属性” 按钮,视图而不更新markdown

而在打开 vue-devtools 工具工具

咱们能够发现其实数据是已经更新了!oop

只是视图没有更新! ui

那这是为何呢?this

其实这就是涉及的Vue的响应式原理spa

在Vue2.x中,响应式的核心api是 Object.defineProperty

下面用一段js代码,模拟一下vue响应式源码

 // 定义空对象
  const propsObj = {}
  
  // 定义监听对象
  const obj = {
    a: '1',
    b: '2',
    c: '3'
  }
  
  // 模拟更新视图
  const updateView = (key, oldVal, newVal) => {
    console.log(`视图更新了~`)
    console.log(`key值${key}更新,从 ${oldVal} 更新到 ${newVal} `)
  }
  
  // 定义监听响应式方法
  const  defineReactive = (target, key, value) => {
   // 核心api
    Object.defineProperty(target, key, {
      get() {
        return value
      },
      set(newValue) {
        updateView(key, value, newValue)
        value = newValue
      }
    })
  }
  
  // 遍历设置监听
  for (const key of Object.keys(obj)) {
    defineReactive(propsObj, key, obj[key])
  }
  
  // 更新数据
  propsObj.a = '0' // 触发响应式
  propsObj.d = '4' // 添加对象属性,不触发响应式
  delete  propsObj.a // 删除对象属性,不触发响应式
复制代码

输出结果:

可见

若是想要触发响应式(视图)更新

你要确保对象的属性触发响应式

而在第一个例子中,点击了 “添加对象属性” 按钮,视图而不更新

由于他在对象作了一个添加属性的操做

固然!还要注意的是,删除操做也是没法触发响应式的!

总结一下:

1.确保改变对象属性是对象已经有的属性,才能监听到响应式

2.作添加、删除操做对象属性,响应式没法监听到

其实,在Vue官网有明确说明

数组也是大同小异的

数组的“小异”在于:

Vue重写了这些数组方法,让这些方法能够触发视图更新

2.解决办法?

①使用Vue.set / Vue.delete

使用Vue专门解决此类问题的api

<!--使用vue-cli脚手架-->
<template>
  <div>
    <button @click="change">改变对象属性</button>
    <button @click="add">添加对象属性</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.$set(this.obj, 'd''4')
    }
  }
}
</script>
复制代码

固然删除也是相似的,再也不赘述

不过要注意Vue版本: 2.20+

②使用vm.$forceUpdate

使用强制更新视图

<!--使用vue-cli脚手架-->
<template>
  <div>
    <button @click="change">改变对象属性</button>
    <button @click="add">添加对象属性</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'
      // 对视图进行强制更新
      this.$forceUpdate()
    }
  }
}
</script>
复制代码

③使用深拷贝,从新赋值替换

对于对象,深拷贝能够使用 JSON.parse(JSON.stringify(obj))

只要改变对象不是同一个对象便可

由于要从新赋值,不该该为同一对象

这样才能触发响应式

举个例子

<!--使用vue-cli脚手架-->
<template>
  <div>
    <button @click="change">改变对象属性</button>
    <button @click="add">添加对象属性</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
      // 缘由: 他们是恒等的 指向同一个对象
      // 即 conso;e.log(obj === this.obj) // true
      // 使用序列、反序列化进行对象深拷贝,对其从新赋值
      const obj = JSON.parse(JSON.stringify(this.obj))
      obj.d = '4'
      this.obj = obj
    }
  }
}
</script>
复制代码

而对于数组,能够使用slice、concat等方法

感谢阅读

相关文章
相关标签/搜索