数据响应式即是当页面当中的数据发生改变的时候,vue 会跟新数据并且重新渲染页面,改变页面上原先对应的数值。
那么 vue 又是怎样知道一个数据发生了变化呢?
vue 对数据的监听
通过 Object.defineProperty 可以为对象 data 添加属性 value。这个 value 同样支持函数的形式,这样就可以添加上 get / set, 用于读写对象中的属性值,并对其进行监听。其中 set 函数又支持写入一个判断的方式,让特定的数值才被允许写入。
let data2 = {}
data2._n = 0 // _n 用来存储 n 的值
Object.defineProperty(data2, 'n', {
get(){
return this._n
},
set(value){
if(value < 0) return
this._n = value
}
})
这样可以监听 data2 里面的 n 的变化。但是这样存在一些小问题。
比如别人可以通过修改存储 n 的 _n 来绕过 Object.defineProperty 对 data 作出修改。为了让所有的数据变化都能让 vue 知道,于是需要添加代理来专门负责对象属性的读写。
let myData5 = {n:0}
let data5 = proxy2({ data:myData5 }) // 括号里是匿名对象,无法访问
function proxy2({data}/*解构赋值*/){
// 这里的 'n' 写死了,理论上应该遍历 data 的所有 key,这里做了简化
let value = data.n
Object.defineProperty(data, 'n', {
get(){
return value
},
set(newValue){
if(newValue<0)return
value = newValue
}
})
// 就加了上面几句,这几句话会监听 data
const obj = {}
Object.defineProperty(obj, 'n', {
get(){
return data.n
},
set(value){
if(value<0)return//这句话多余了
data.n = value
}
})
return obj // obj 就是代理
}
这种设计模式当然被应用到了 vue 当中,vue 的写法如下
const vm = new Vue({data:{n:0}})
这里的 vm 便是 data 内对象的代理,会对其的所有属性监控,这样一来 vm 就会掌握 data 的变化,在变化时调用 render(data) ,在页面中重新渲染变化部分的内容。
data 的新增
上面说的监听属性存在一个前提:那就是先得有这个属性,如果监听了一个不存在的属性会怎样?
当然会报错。vue 会给出一个警告。
这时候我们要用到 Vue.set 和 this.$set 来新增一个可供监听的属性:
Vue.set(this.obj, 'b' , 1)
this.$set(this.obj , 'b' ,1)
这样就会新增一个 key ,兵器自动创建代理和监听。在值改变的时候触发更新,重新渲染改变的值。
那如果是数组呢
数组没有办法确定之后会有少个数据,所以没有办法将所有的 key 提前声明出来。这时就需要引用到 vue 修改过的数组相关 API:
- push() —新增
- pop() —弹出最后一个
- shift() —弹出首个
- unshift() —在开头新增
- splice() —中间删除某个
- sort() —排序
- reverse() —逆排序
通过调用这 7个API,会自动地处理代理与监听,并且及时更新页面数据。