微型Vue框架实现Part2——数据代理实现

微型Vue框架构建Part1——基本目录结构
微型Vue框架构建Part3——$Mount方法实现
微型Vue框架构建Part4——Render渲染前

概述

具有代表性的数据模型

  • 对象嵌套对象
  • Array类型的数据模型,且数组内置方法触发后会改变代理数据

      data: {
          name: "小明",
          age: 10,
          address: "家庭住址",
          info: {
              IDCard: "身份证号码",
              school: "学校名字",
              obj: {
                  name: "sddd"
              }
          },
          list: [{ a: 1, b: 2 }]
      }
    

    数据代理实现——代理单个数据

      /**
       * @param {*} key 需要代理的字段 —— 字符串类型
       * @param {*} proxyObj  代理对象 —— Object类型
       * @param {*} originObject 原数据对象 —— Object类型
       * @returns
       */
      function proxyKey(key, proxyObj, originObject) {
        Object.defineProperty(proxyObj, key, {
          configurable: true, // 允许增删代理对象的key键,原数据也会同步
          get: function () {
            return originObject[key];
          },
          set: function (val) {
            return (originObject[key] = val);
          },
        });
        return proxyObj;
      }
     
     // 代码实践
    const prpxyObj = {};
    let data = {
        a: 20,
        b: 30
    }
     window.test = proxyKey("a", prpxyObj, data)

代理数组方法

  • 目的:修改数组对象的原型链,改变数组内置方法的执行体,如push方法、pop方法
  • Object.defineProperty()中的value属性:代理属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等),即你可控制返回结果。

      const arrProxyFunc = new Array();
      
      /**
      * obj:代理对象
      * proxyFunction:需要被代理的数组方法
      */
      function proxyArrayFunctionCall(obj, proxyFunction) {
        Object.defineProperty(obj, proxyFunction, {
          enumerable: true,
          configurable: true, // 允许删除代理数据的内容
          value: function (...args) {
            // 映射Array原型链上的方法,
            const arrProxyFunc = arrayProtoType[proxyFunction];
            // 改变this指向
            const result = arrProxyFunc.apply(this, args);
            return result;
          },
        });
      }
    
      /**
      *  array: 需要被代理的数组方法——其原型链将会完全覆盖
      */
      function proxyArray(array) {
         // 创建代理对象——新的数组原型链,完全覆盖之前的原型链
        const obj = {
          eleType: "Array",
          pop() {},
          push(){}
        };
        // 代理自定义的方法
        proxyArrayFunctionCall(obj, "pop", );
        proxyArrayFunctionCall(obj, "push");
    
        /**
        * 覆盖原本原型链——这样就实现了数组方法的代理
        * 完全覆盖之前数组的原型链,不再支持内置的数组方法,只能使用obj对象提供的方法
        */
        array.__proto__ = obj;
        return array;
      }
      
      // 代码调试
      const arr = [];
      window.arr = arr;
      window.test = proxyArray(arr);
      
  • 可以看到,无论是arr的原型链被我们修改了,这样就实现了对数组内置方法的代理
  • 我们在实现数据代理的时候会用到这个点
    微型Vue框架实现Part2——数据代理实现_第2张图片

实现Vue框架中的数据代理

  • 实现对象嵌套对象的数据代理:proxyObject()方法中可以看到代码片段
  • 实现数组类型的数据代理:proxyArray()
  • 命名空间:getNameSpace(),该方法是为了保证在对象嵌套对象的情况下,数据源不会取错。如代理obj对象下的test,代理num这个key时,它在原数据的取值应是obj.test.num。所以,当实现其代理时命名空间就是字符串obj.test

      let obj = {
          a: 10,
          test: {
              num: 20
          }
      }
  • Object.getOwnPropertyNames(): 返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组。
  • 我们在实现代理数组方法时用到了Object.getOwnPropertyNames,映射数组原型链上所有的内置方法。
/**
 * 数据代理
 */
    const arrayProtoType = new Array();

    function getNameSpace(nameSpace = null, key = null) {
      if (nameSpace && !key) {
        return nameSpace;
      } else if (key && !nameSpace) {
        return key;
      } else {
        return `${nameSpace}.${key}`;
      }
    }

    function getObjectKey(obj) {
      return Object.keys(obj);
    }

    /**
     *
     * @param {*} key 需要代理的字段
     * @param {*} proxyObj  代理对象
     * @param {*} originObject 原数据对象
     * @returns
     */
    function proxyKey(key, proxyObj, originObject) {
      Object.defineProperty(proxyObj, key, {
        configurable: true,
        get: function () {
          return originObject[key];
        },
        set: function (val) {
          return (originObject[key] = val);
        },
      });
      return proxyObj;
    }

    /**
     * 代理对象
     * @param {} option
     * @returns
     */
    function proxyObject(vm, option, nameSpace) {
      let proxyObj = {};
      const proxyAllKey = getObjectKey(option);
      for (let i = 0; i < proxyAllKey.length; i++) {
        const key = proxyAllKey[i];
        const val = option[key];

        proxyKey(key, proxyObj, option);

        // 对象嵌套对象或是数组类型的情况
        if (val instanceof Object || val instanceof Array) {
          option[key] = constructorProxy(vm, val, getNameSpace(nameSpace, key));
        }
      }
      return proxyObj;
    }

    /**
     * 代理Array原型链上的某个内置方法
     * @param {*} vm
     * @param {*} obj // 代理对象
     * @param {*} proxyFunction // 需要代理的数组方法
     * @param {*} nameSpace
     */
    function proxyArrayFunctionCall(vm, obj, proxyFunction, nameSpace) {
      Object.defineProperty(obj, proxyFunction, {
        enumerable: true,
        configurable: true, // 允许删除代理数据的内容
        value: function (...args) {
          const arrProxyFunc = arrayProtoType[proxyFunction];
          const result = arrProxyFunc.apply(this, args);
          return result;
        },
      });
    }

    /**
     * 代理数组——覆盖数组对象本来的原型链
     * 目的:当时触发代理对象的pop、push...方法时,执行我们自定义的内容
     *        也就是 proxyArrayFunctionCall 函数中 value的返回值
     * @param {*} vm
     * @param {*} array
     * @param {*} nameSpace
     */
    function proxyArray(vm, array, nameSpace) {
      /**
       * 获取 Array 原型链上的所有内置方法的key
       * 如常用的数组方法:push、pop、unshift...
       */
      const arrayProtoTypeAllKey = Object.getOwnPropertyNames(
        arrayProtoType.__proto__
      );

      const obj = {
        eleType: "Array",
        ...arrayProtoTypeAllKey,
      };
      arrayProtoTypeAllKey.forEach((functionName) => {
        proxyArrayFunctionCall(vm, obj, functionName, nameSpace);
      });

      // 覆盖原本原型链——这样就实现了数组方法的代理
      array.__proto__ = obj;
      return array;
    }

    function constructorProxy(vm, options, nameSpace = "") {
      let proxy = {};

      if (options instanceof Array) {
        proxy = new Array(options.length);

        for (let i = 0; i < options.length; i++) {
          if (options[i] instanceof Object) {
            proxy[i] = proxyObject(vm, options[i], nameSpace);
          } else {
            proxy[i] = proxyKey(options[i], {}, options[i]);
          }
        }

        proxy = proxyArray(vm, options, nameSpace);
      } else if (options instanceof Object) {
        proxy = proxyObject(vm, options, nameSpace);
      } else {
        throw new Error("Error");
      }
      return proxy;
    }
    export default constructorProxy;

你可能感兴趣的:(微型Vue框架实现Part2——数据代理实现)