Vue数据代理的实现流程逐步讲解

2023-01-07,,

通过一个对象代理对另一个对象中的属性的操作(读/写),就是数据代理。要搞懂Vue数据代理这个概念,那我们就要从Object.defineProperty()入手,Object.defineProperty()是Vue中比较底层的一个方法,在数据劫持,数据代理以及计算属性等地方都或多或少的用到了本函数

目录
  • 一,前言
  • 二,数据代理的实现
    • 1,Vue 是如何操作数据的
    • 2,当前是如何操作数据的
    • 3,数据代理的思路
    • 4,数据代理的实现
    • 5,数据代理的测试
  • 三,结尾

    一,前言

    上篇,主要介绍了 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 上

    到此这篇关于Vue数据代理的实现流程逐步讲解的文章就介绍到这了,更多相关Vue数据代理内容请搜索北冥有鱼以前的文章或继续浏览下面的相关文章希望大家以后多多支持北冥有鱼!

    《Vue数据代理的实现流程逐步讲解.doc》

    下载本文的Word格式文档,以方便收藏与打印。