const vm = new MVVM({
el: "#test",
data: {
name: '张三2'
}
})
先new一个vue配置对象
数据代理其实就是利用了Object.defineProperty方法来给
new出来的一个vue对象添加一个和vue中data里相同属性名的属性,
所以vue中访问data里的属性,不需要this.data.name
只需要this.name 就可以访问到,这是因为,我们在创建这个vue实例的时候,
就已经把data中的数据代理给了vm 也就是vue这个对象本身 所以相当于vue里面也有了name这个属性
我们在new MVVM的时候,就会去调用一个构造函数
/*
相关于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) { // 属性名: name
// 对指定属性实现代理
me._proxy(key);
});
MVVM.prototype = {
$watch: function (key, cb, options) {
new Watcher(this, key, cb);
},
// 对指定属性实现代理
_proxy: function (key) {
// 保存vm
var me = this;
// 给vm添加指定属性名的属性(使用属性描述)
Object.defineProperty(me, key, {
configurable: false, // 不能再重新定义
enumerable: true, // 可以枚举
// 当通过vm.name读取属性值时自动调用
get: function proxyGetter() {
// 读取data中对应属性值返回(实现代理读操作)
return me._data[key];
},
// 当通过vm.name = 'xxx'时自动调用
set: function proxySetter(newVal) {
// 将最新的值保存到data中对应的属性上(实现代理写操作)
me._data[key] = newVal;
}
});
}
};
可以看到上述代码中, new MVVM()就是调用这个构造函数,里面会传一个配置对象作为参数,也就是
{
el: "#test",
data: {
name: '张三2'
}
}
MVVM构造函数在接收到这个配置对象之后,首先把这个对象保存起来,赋值给$options
this.$options = options;
此函数中的this均指向vm 即 const vm = new MVVM 里的vm
然后再把原对象中的data对象,赋值给vm里的_data,同时又保存在data中
var data = this._data = this.$options.data;
接着调用对象的keys方法得到此对象中可枚举的所有属性组成的数组,遍历一次,
将每个属性都代理给vm 即me._proxy(key);
Object.keys(data).forEach(function (key) { // 属性名: name
// 对指定属性实现代理
me._proxy(key);
});
接下来,进入_proxy方法,去实现代理
// 对指定属性实现代理
_proxy: function (key) {
// 保存vm
var me = this;
// 给vm添加指定属性名的属性(使用属性描述)
Object.defineProperty(me, key, {
configurable: false, // 不能再重新定义
enumerable: true, // 可以枚举
// 当通过vm.name读取属性值时自动调用
get: function proxyGetter() {
// 读取data中对应属性值返回(实现代理读操作)
return me._data[key];
},
// 当通过vm.name = 'xxx'时自动调用
set: function proxySetter(newVal) {
// 将最新的值保存到data中对应的属性上(实现代理写操作)
me._data[key] = newVal;
}
});
}
};
_proxy方法接收一个参数key 即原data对象中每个可枚举的属性的属性名
调用Object.defineProperty方法
先配置属性
configurable: false, // 不能再重新定义
enumerable: true, // 可以枚举
get方法 和set方法就实现了数据代理
// 当通过vm.name读取属性值时自动调用
get: function proxyGetter() {
// 读取data中对应属性值返回(实现代理读操作)
return me._data[key];
},
// 当通过vm.name = 'xxx'时自动调用
set: function proxySetter(newVal) {
// 将最新的值保存到data中对应的属性上(实现代理写操作)
me._data[key] = newVal;
}
当我们 写语句 vm.xxx的时候,就会去调用到get方法, get方法会返回me._data.xxx
同样的当我们给vm.xxx赋值的时候,就回去调用set方法,
在这个构造函数中,data中的数据都会通过defineProperty方法被添加到vm对象中,
vm对象里新增的这些属性都是可操控的,被监视的,
也就是表面上看起来我们是在操作vm.xxx其实实际上我们操作的是vm._data里的xxx 也就是data中的xxx
所以数据代理的根本就是我们想要操作 a.b.xxx
经过数据代理后我们可以实现表面操作a.xxx其实操作的是a.b.xxx
网友评论