一、什么是数据响应式
简单来说,数据响应式是指页面视图会随着数据的变化而变化。 比如:
<div id="app">{{ data }}</div>
const vm = new Vue({
el:'#app',
data:{
data:'hello'
}
})
执行代码后,页面中出现 hello
vm.data = 'hi'
执行代码后,页面中 hello 更新为 hi
二、制造可响应数据
当你把一个普通的JavaScript对象传入Vue实例作为 data 选项,Vue将遍历对象所有属性,并使用Object.defineProperty()把这些属性全部转为getter/setter
getter/setter
getter/setter用于对属性的读写进行监控
var obj = {
姓: "张",
名: "三",
get 姓名() {
return this.姓 + this.名;
},
set 姓名(xx) {
this.姓 = xx[0]
this.名 = xx.slice(1)
}
};
console.log(obj.姓名) // 张三
obj.姓名 = "李四"
console.log(obj.姓名); // 李四
get 可以使得函数obj.姓名()转变成属性obj.姓名来读取 set 接受一个新的传参,这个参数就是后来传进来的obj.姓名 = "李四",我们可以对其进行操作
Object.defineProperty()
Object.defineProperty()用来给已经声明过的对象添加getter和setter操作的方法。
我们现在给刚刚的obj添加一个属性 xx,值为 0 ,这个属性是可响应的
var _xx = 0 // 隐藏属性,用来存储'xx'的值
Object.defineProperty(obj, 'xx', { // 使用Object.defineProperty 给 obj 对象添加一个可响应的属性 'xx'
get() {
console.log("正在读取属性")
return _xx
},
set(value) {
_xx = value
console.log("已经修改好属性")
}
})
console.log(obj.xx) // 正在读取属性 0
obj.xx = '王五' // 已经修改好属性
console.log(obj.xx) // 正在读取属性 王五
当读取 xx 时候,不但返回 xx 的值,还会打印"正在读取属性",当修改完 xx 的值,会返回"已经修改好属性"
使用代理制造可响应的数据
上面Object.defineProperty()的例子中,我们给obj创建了一个隐藏属性 "_xx",用来存储 xx 的值。在对 xx 进行读写操作时,其实都是在操作 _xx ,如果我们直接用 this.xx 作为返回值,就会陷入死循环,因为 this.xx 返回之前,浏览器会读取这个属性,就再一次出发了get 操作,无限循环。所以需要一个 _xx 属性来存储 xx 的值,这样我们在 return 的时候就不会陷入死循环。但是如果我们直接操作 _xx 的话也可以修改 xx ,不会触发我们设置的 setter/getter。设置代理就可以解决这个问题
var myData = { xx: 0 }
var obj = proxy({ data: myData });
// proxy 就是代理,返回一个和 data 属性一样的对象
function proxy({ data }) {
let value = data.xx
Object.defineProperty(data, 'xx', {
get() {
return value
},
set(newValue) {
value = newValue
}
})
// 上面监听 data , 下面进行代理
const obj1 = {} // 这个对象是代理对象
Object.defineProperty(obj1, 'xx', {
get() {
return data.xx;
},
set(value) {
data.xx = value;
}
})
return obj1
}
console.log(obj.xx); // 0
obj.xx = "王五";
console.log(obj.xx); // 王五
三、Vue检测对象和数组的变化
对于对象
Vue 无法检测 property 的添加或移除。由于 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。
可以使用Vue.set或this.$set方法向嵌套对象添加响应式属性
Vue.set(vm.object, propertyName, value)
this.$set(this.object, propertyName, value)
对于数组
Vue无法检测到数组新增了下标或利用下标设置数组项。
可以使用Vue.set或this.$set方法
Vue.set(vm.items, indexOfItem, newValue)
也可以使用Vue对数组的一些变更方法:
- push()
- pop()
- shift()
- unshift()
- splice()
- sort()
- reverse()