# 问题
let vm = new Vue({
el: "#app",
data() {
return { name: "smeb", obj: { key: "val" }, arr: [1, 2, 3] };
},
});
vm.name
等价于$options.data.name
,是如何实现的?
# 数据代理的实现
# 思路
为了让外部的vm
实例能够直接拿到被观测后的data
,可以将被处理后的data
直接挂载到vm
实例上:
// src/state.js#initData
function initData(vm) {
let data = vm.$options.data;
data = vm._data = isFunction(data) ? data.call(vm) : data;
observe(data);
}
首先,先做一次代理 ——
vm._data = isFunction(data) ? data.call(vm): data
,将data
挂载到vm._data
下,这样vm
实例就能够在外部通过vm._data.message
获取到data.message
—— 用_data
把data
的数据存起来然后,再做一次代理 ——
data = vm._data
,将vm
实例操作vm.message
代理到vm._data
上,这样,外部就可以直接通过vm.message
获取到data.message
# 实现
Vue
状态初始化阶段,通过observe()
实现数据响应式之后,通过Object.defineProperty
对_data
中的数据操作进行代理和劫持;将vm.xxx
在vm
实例上的取值操作,代理到vm._data.xxx
上
// src/state.js#initData
function initData(vm) {
let data = vm.$options.data;
data = vm._data = isFunction(data) ? data.call(vm) : data;
observe(data);
// 当 vm.message 在 vm 实例上取值时,将它代理到vm._data上去取
for (let key in data) {
Proxy(vm, key, "_data");
}
}
Proxy
的实现就是通过Object.defineProperty
来实现数据代理和数据劫持的 —— 配置get
和set
# ==自己写一个数据代理==
let _data = { name: "smeb", age: 25 };
let vm = {};
console.log("初始化的Data: ", _data);
console.log("初始化的Vm: ", vm);
function xxx(vm, key, source) {
Object.defineProperty(vm, key, {
enumerable: true,
get() {
return source[key];
},
set(val) {
source[key] = val;
},
});
}
for (let i in _data) {
xxx(vm, i, _data);
}
console.log("数据代理之后的vm: ", vm);
console.log("数据代理之后的_data: ", _data);
// console.log(": ", vm);
console.log("修改前的_data: ", _data);
vm.name = "the shy";
console.log("修改后的_data: ", _data);
输出结果如下:
可以看到通过代理之后的vm
上存在了其属性的get
和set
方法,实现了数据代理和数据劫持,我们修改vm
的数据,_data
的数据也被成功修改了~