学习Vue有一段时间了,有粉丝问我,Vue的数据代理与数据劫持到底是什么意思呢?这个过程到底发生了什么呢?今天我就来详细解释一下。
一、基础知识
我们先看 Object.defineProperty()的用法:
Object.defineProperty(obj,prop,descriptor)
参数介绍:
obj:目标对象 prop:需要定义的属性或者方法的名字 descriptor:prop属性所具有的特性 可供定义的特性列表: value :属性的值 writable:若为false,表示属性值不能重写 get:一旦目标属性被访问,则自动调用该方法,并返回该方法的调用结果 set:一旦目标属性被赋值,则自动调用该方法,进行赋值操作 configurable:若为false,则任何尝试删除目标属性,或修改目标属性的 以下特性:writable、configurable、enumerable的行为均无效 enumerable:若为true,则可以在for **...** in 循环或Object**.**keys()可以枚举出来
二、数据代理
1、含义
通过一个对象,代理对另一个对象的操作,即:读、写操作。
2、原理
每个 **Vue **应用都是通过用 Vue 函数创建一个新的 Vue 实例开始的:
var vm = new Vue({
// 选项options(配置项)
})
// 数据data对象
var data = { a: 1 }
// 该data对象被加入到一个 Vue 实例中
var vm = new Vue({
data: data
})
上面的代码一般写成下面的形式:也就是直接在Vue函数中,传入 **data **配置对象
const vm = new MVVM({
el: "#root",
data: {
a: 1
}
})
**数据代理的实质是: **
利用 **Object.defineProperty() **方法,给new出来的Vue实例 **vm **,添加和 **data **配置项中一模一样的属性和值。
为每一个添加到 **vm **上的属性,都指定一个 **getter/setter **
在 **getter/setter 内部去操作(读/写)data **中对应的属性
我们可以在控制台打印 **vm **,如下图
可以看到,每一个属性,都有一对 get/set 或 getter/setter方法,这是实现响应式变化的关键。
数据代理用到的Vue构造函数伪代码如下:
function MVVM(options){
// 将选项对象保存到vm
this.$options = options
// 将data对象保存到 vm和 新定义的data变量中
var data = this._data = this.$options.data
// 将vm保存在me变量
var me = this
// 遍历data中所有属性名
Object.keys(data).forEach(function (key){
// 每次遍历,实现当前属性的代理
me._proxy(key)
})
// 对data进行监视
observe(data,this)
// 创建一个用来编译模板的compile对象,用来解析模板以及模板里的指令
this.$compile = new Compile(options.el || document.body, this)
}
MVVM.prototype = {
$watch:function(key,cb,options){
new Watcher(this,key,cb)
},
// 调用_proxy方法,对指定的属性实现代理
// _proxy方法接收一个参数key,即原data对象中每个可枚举的属性的属性名
_proxy:function(key){
// 将vm保存在me变量
var me = this
// 给vm添加指定属性名的属性
Object.defineProperty(me,key,{
configurable:false, //不能再重新定义该属性
enumerable:true, //可以枚举
// 使用vm.name的形式读取属性值时,自动调用get/getter回调函数
get:function proxyGetter(){
return me._data[key]
},
// 使用vm.name = 'XXX'形式,写操作时,自动调用set/setter回调函数
set:function proxySetter(newValue){
me._data[key] = newValue
}
})
}
}
在 **new **一个 **Vue **实例的时候,会调用上面的构造函数 **MVVM **:
构造函数 **MVVM **会接收到一个选项对象 **options **(也叫配置对象),也就是我们上面说的 **new Vue **里面的选项对象
{
el: "#root",
data: {
a: 1
}
}
接收到 **options **之后,保存在 **this.$options **中。然后将data对象中的属性赋值给 **this._data **,为了实现响应式, **vm **会对自身 **data **(注意,这是 **vm **自己的 data )进行修改,也就是通过Object.defineProperty()给每一个属性添加上 **getter **和 **setter **方法。
表面上,我们是在操作** vm.xxx **,实际上,我们操作的是 **vm._data.xxx **,也就是 **Vue 选项对象中的 data **,这就是数据代理的本质。
vm的_data中,不只是简单的选项对象中的data
尚硅谷的 Vue 全家桶课程讲解的很棒,我这里粘贴了他们的数据代理图示:
3、好处
使用数据代理,之后我们使用 **vm **,访问 **Vue **函数中 **data **的属性时,直接 this.a 就可以了,不需要使用 *this.data.a 。***注意,这里的 this,指向的是 **vm
三、数据劫持
1. 定义
在上面的数据代理讲解中,我们用到了 Object.defineProperty()方法,在每次对属性进行代理时,访问或者设置对象属性值的时候,总要触发 **get **或 **set **函数,返回需要的值或设置相应属性的值,那么,我们可以在触发函数的时候,动一点手脚,做我们自己想做的事情,这就是“数据劫持”操作。
2.vue原理
Vue的数据劫持,实战就是通过Object.defineProperty()方法来劫持对象属性的getter和setter操作,并安装一个监听器,既可以监听对象,又可以监听数组。当数据发生变化时,就发出通知。
Vue 是采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 **getter **和 setter 方法,在数据变动时发布消息给订阅者,触发相应的监听回调,实现响应式系统。
版权归原作者 唯一的阿金 所有, 如有侵权,请联系我们删除。