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