Vue数据代理的实现流程逐步讲解
BraveWangDev 人气:0一,前言
上篇,主要介绍了 Vue 数据初始化流程中,数组类型的数据劫持是如何实现的,核心思路如下:
出于对性能的考虑,Vue 没有对数组采用 Object.defineProperty 进行递归劫持,而是对能够导致原数组变化的 7 个方法进行了拦截和重写,实现了对数组的数据劫持
至此,已经完成了对响应式数据(对象和数组)的劫持(深层劫持)操作
本篇,继续介绍 Vue 数据初始化流程中, Vue 实例上数据代理的实现
二,数据代理的实现
1,Vue 是如何操作数据的
在 Vue 中,是可以在外部直接通过 vm 实例进行数据访问和操作:
let vm = new Vue({ el: '#app', data() { return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]} } }); console.log(vm.message) console.log(vm.arr.push(4))
抛出问题:vm.message 等价于 $options.data.message,是如何实现的?
2,当前是如何操作数据的
当前代码,外部的 vm 实例只能拿到 vm. o p t i o n s ,拿到 d a t a 需要 v m . options,拿到 data 需要 vm. options,拿到data需要vm.options.data
// src/state.js#initData function initData(vm) { let data = vm.$options.data; data = isFunction(data) ? data.call(vm) : data; observe(data); data.message data.arr.push(4); }
要想实现 vm.message 和 $options.data.message 等效
相当于将 vm 实例操作代理到 $options.data 上,即实现数据代理
3,数据代理的思路
为了让外部的 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 实例就能够在外部通过 vm._data.message 获取到 data.message
接下来,再做一次代理,将 vm 实例操作(vm.message),代理到 vm._data 上即可
4,数据代理的实现
通过 Object.defineProperty 对 _data 中的数据操作进行劫持
即:vm.message 在 vm 实例上取值时,将它代理到 vm._data 上取值
// 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') } } // src/state.js#Proxy /** * 代理方法 * 当取 vm.key 时,将它代理到 vm._data上去取 * @param {*} vm vm 实例 * @param {*} key 属性名 * @param {*} source 代理目标,这里是vm._data */ function Proxy(vm, key, source) { Object.defineProperty(vm, key, { get(){ return vm[source][key] }, set(newValue){ vm[source][key] = newValue; } }) }
5,数据代理的测试
let vm = new Vue({ el: '#app', data() { return { message: 'Hello Vue', obj: { key: "val" }, arr:[1,2,3]} } }); console.log(vm) console.log(vm.message)
观察打印结果:
获取 vm 实例时,会通过 get 方法将 _data 全部属性打印出来
当前 vm 实例上,包含 data 全部属性及对应的 get、set 方法
这样,就实现了数据代理:
当从 vm 实例取值时,就会被代理到 vm._data 取值
三,结尾
本篇主要介绍了 Vue 数据初始化流程中,Vue 实例上数据代理的实现,核心思路如下:
- 将 data 暴露在 vm._data 实例属性上
- 利用 Object.defineProperty 将 vm.xxx 操作代理到 vm._data 上
加载全部内容