# 问题
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的数据也被成功修改了~
