2020年11月20日

响应式原理(2)- 数组类型

作者 rourou

数据data中包含数组类型,在进行响应式转换的过程中,会涉及到push新增的数据等操作,需将其转化为响应式数据。(以下示例为push方法)

在对方法进行拦截的一般处理办法:

  • 1、使用一个临时的函数名存储函数
  • 2、重新定义原来的函数
  • 3、定义扩展功能
  • 4、调用临时存的那个函数
 // 1
    function fn(){
      console.log('原始的方法');
    }
    let fun = fn;
    // 2
    fn = function (){
      // 4
      fun();
      // 3
      console.log('扩展的方法');
    }
    fn();

拦截数组的方法(示例的方法是push):修改原型链结构__proto__

  • 1、定义数组的变量a
  • 2、创建数组原型
  • 3、重写的方法
  • 4、调用临时变量a
let ARRAY_METHOD = ['push'];
    // 1
    let arr = []
    // 2
    let array_methods = Object.create(Array.prototype);
    // 3 循环需扩展的数组方法名,并创建一个方法
     ARRAY_METHOD.forEach(method => {
      array_methods[ method ] = function (){
        console.log('调用拦截');
        // 调用原有的方式
        let res = Array.prototype[ method ].apply(this , arguments);
        return res;
      }
    })
    // 4
    arr.__proto__ = array_methods;

对数组类型进行响应式处理:需在Object.defineProperty中进行递归数据响应化方法reactify。以及在数组方法拦截中调用改方法进行数据push

 var data = {
      name:'张三',
      age:12,
      code:[
        {name:'语文'},
        {name:'数学'}
      ]
    }
// 数组方法拦截开始
    let ARRAY_METHOD = ['push'];
    let array_methods = Object.create(Array.prototype);
    // 3 循环需扩展的数组方法名,并创建一个方法
     ARRAY_METHOD.forEach(method => {
      array_methods[ method ] = function (){
        console.log('调用拦截');
        // 变为响应式数据
        for(var i=0;i<arguments.length;i++){
          reactify(arguments[i])
        }
        // 调用原有的方式
        let res = Array.prototype[ method ].apply(this , arguments);
        return res;
      }
    })
// 数组方法拦截结束
    function defineReactive(target, key, value, enumerable){
      if(typeof value === 'object' && value!= null && !Array.isArray(value)){
        reactify(value);
      }
      
      Object.defineProperty(target, key, {
        configurable: true,
        enumerable: !!enumerable,
        set(newValue){
          if(typeof newValue === 'object' && newValue!= null && !Array.isArray(newValue)){
            reactify(newValue);
          }else if(Array.isArray(newValue)){
            for(var i=0;i<newValue.length;i++){
              reactify(newValue[i]);
            }
          }
          value = newValue;
          
        },
        get(){
          console.log(value)
          return value;
        }
      })
    }

    function reactify(data){
      let keys = Object.keys(data);

      for(var i=0;i<keys.length;i++){
        let key = keys[i];
        let val = data[key];

        if(Array.isArray(val)){
          // 数组类型
          val.__proto__ = array_methods;//数组响应式方法调用

          for(var j=0;j<val.length;j++){
            reactify(val[j]);
          }
        }else{
          // 引用类型
          defineReactive(data, key, val, true);
        }
      }
    }

    reactify(data);