赞
踩
实现原理:
Object.defineProperty()
对属性的读取、修改进行拦截data 属性中,只有属性描述符 configurable 为 true 的属性才可以被 VueJS 进行依赖收集。VueJS 会在 data 属性中的每个属性上调用 Object.defineProperty()
方法,为它们添加 getter 和 setter。这样,当你访问或修改 data 属性中的属性时,VueJS 就可以知道它们被访问或修改了,从而触发相应的更新。
存在问题:
Vue 会加工、代理 data
为 _data
& $data
,通过浏览器控制台可以观察到,vm.$data
的属性都是响应式数据(有对应的 getter、setter)。但是,如果我们后期自己给 Vue 实例添加属性,因为该属性没有经过 Vue 加工,所以不是响应式数据(没有 getter、setter)。
如果我们在 data
中放置一个数组数据,该数组会被代理,但数组内的元素不会被代理。就是说,只有修改数组本身,页面才会响应;而修改数组元素的话,页面不会响应。
所以,通过 this.arr[index]
[删除]、[添加]、[修改] 数组的元素,数据会被修改,但页面不会重新渲染。
<button @click="arr.length = arr.length - 1">删除数组元素</button>
<button @click="arr[arr.length] = 'z'">添加数组元素</button>
<button @click="arr[0] = '0'">修改数组元素</button>
<span v-for="item in arr"> {{item}} </span>
const vm = new Vue({
el: '#app',
data: {
arr: ['s', 'p', 'm'],
},
});
方法 1:使用变更方法
可以使用能改变原数组的方法:1. push()
、 2. pop()
、 3. unshift()
、 4. shift()
、 5. splice()
、 6. sort()
、 7. reverse()
;这 7 个方法都被 Vue 重写过,使用它们更新数据能触发页面的重新渲染。
<button @click="arr.shift()">删除数组元素</button>
<button @click="arr.push('z')">添加数组元素</button>
<button @click="arr.splice(0, 1, '0')">修改数组元素</button>
可以发现,现在不只是数据发生了变化,页面也会重新渲染了。
方法 2:替换数组
可以通过 filter
、map
等函数,对原数组变量进行重新赋值。
<button @click="arr = arr.filter(ele => ele == 's')">过滤数组元素</button>
<span v-for="item in arr"> {{item}} </span>
你可能认为这将重新渲染整个列表。事实并非如此。Vue 为了使得 DOM 元素得到最大范围的重用 实现了一些智能的启发式方法,所以用一个含有大量相同元素的数组去替换原来的数组是非常高效的操作。
如果我在 data
里面放置了一个对象数据,该对象会被代理,对象里面的属性也会被代理。就是说,不论我们修改对象本身,还是 [修改] 对象的属性,数据都是响应性的,会即时显示回页面。但是,如果我们为对象 [添加]、[删除] 属性,数据会被改变,但页面不会重新渲染。
<button @click="obj.sex = 'male'">添加 sex 属性</button>
<button @click="delete obj.name">删除 name 属性</button>
<button @click="obj.age++">修改 age 属性值</button>
<span v-for="item in obj"> {{item}} </span>
const vm = new Vue({
el: '#app',
data: {
obj: { name: 'superman', age: 22 },
},
});
方法:替换对象
我们可以把整个对象数据替换掉,来显示更新后的对象数据。
<button @click="change">修改整个对象</button>
<span v-for="item in obj"> {{item}} </span>
const vm = new Vue({ el: '#app', data: { obj: { name: 'superman', age: 22 }, }, methods: { change() { let newObj = {}; // 创建新对象 for (let [key, value] of Object.entries(this.obj)) { // 使用解构赋值 newObj[key] = value; // 复制原对象的属性 } newObj.sex = 'male'; // 添加属性 / 修改属性值 this.obj = newObj; // 对原对象重新赋值 }, }, });
我们知道 data
会被 Vue 代理为 _data
& $data
,在这之前,Vue 会先对 data
内的数据进行加工,使其成为响应性数据。
如果我们直接在浏览器控制台给 Vue 实例添加属性,那这个后期添加上来的属性,因为没有被 Vue 加工,所以不是响应性数据。
vm.obj.gender = 'male'; // 无效写法
// 等效于 vm.$data.obj.gender = 'male'
Vue.set(对象, "属性名", 属性值)
/ vm.$set(对象, "属性名", 属性值)
- 用于设置响应性的 [属性] / [元素] 。
Vue.set(vm.obj, 'gender', 'male'); // 牛逼写法
vm.$set(vm.obj, 'gender', 'male'); // 也牛逼写法
这是 Vue 提供给我们的 API,使我们后期添加的数据,也经过 Vue 处理,成为响应性数据。
<p v-for="(item, name, index) of obj">{{index}} - {{name}} - {{item}}</p>
<button @click="change">调用 set 修改对象数据</button>
const vm = new Vue({
el: '#app',
data: {
obj: { name: 'superman', age: 21 },
},
methods: {
change() {
Vue.set(this.obj, 'age', '22'); // 修改属性值
this.$set(this.obj, 'sex', 'male'); // 添加属性
},
},
});
这个方法也可以应用到数组身上,给数组 [添加元素] / [修改元素值]:vm.$set(数组, 下标, 元素值)
/ Vue.set(数组, 下标, 元素值)
。
Vue.set(vm[._data].arr, 0, 'new value');
vm.$set(vm[._data].arr, 0, 'new value');
注意:添加的对象不能是 [Vue 实例] (vm
) / [Vue 实例的根数据对象] (vm._data
)。就是说,不能通过这个方法给 Vue 添加 [响应式数据] 。
Vue.delete(对象, "属性名")
/ vm.$delete(对象, "属性名")
- 用于删除 [属性] / [元素]
<template> <div> <p v-show="person.name">{{ person.name }}</p> <p>{{ person.age }}</p> <button @click="change_person">点击修改</button> </div> </template> <script> import Vue from 'vue'; export default { name: 'App', data() { return { person: { name: 'superman', age: 21 }, }; }, methods: { change_person() { Vue.delete(this.person, 'name'); // this.$delete(this.person, "name") }, }, }; </script>
new Proxy(data, {
// 拦截 [获取] 属性
get(target, prop) {
return Reflect.get(target, prop); // 返回属性值
},
// 拦截 [添加]、[修改] 属性
set(target, prop, value) {
return Reflect.set(target, prop, value); // true-执行成功、false-执行失败
},
// 拦截 [删除] 属性
deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop); // true-删除成功、false-删除失败
},
});
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。