Vue中响应式原理的理解

先看一下响应式的简单例子:

 var o = {};
    o.name = '张三';

    // 等价于
    Object.defineProperty( o, 'age', {
        configurable: true,
        writable: true,
        enumerable: !true, // 可枚举
        value: 19
    } );

    Object.defineProperty( o, 'gender', {
        configurable: true,
        enumerable: true, // 可枚举
        get () { 
            return 'getter被调用';
        },
        set ( newVal ) {
            console.log( '赋值的新值为: ', newVal );
        }
    } );
复制代码

赋值的时候正常,但是取值的时候发现并没有返回刚才赋值的 ‘nan’

image.png

如果同时使用 get 和 set 需要一个中间变量存储真正的数据,修改如下:

var o = {};
    
    // 给 o 提供属性
    o.name = '张三';

    // 等价于
    Object.defineProperty( o, 'age', {
      configurable: true,
      writable: true,
      enumerable: !true, // 可枚举
      value: 19
    } );

    // get 和 set 上
    // 要响应式就表示在赋值和读取的时候, 附带的要做一些事情
    let _gender;
    Object.defineProperty( o, 'gender', {
      configurable: true,
      enumerable: true, // 可枚举
      get () { // 如果使用 o.gender 来访问数据, 就会调用 get 方法 ( getter, 读取器 )
        return _gender;
      },
      set ( newVal ) { // 如果 o.gender = 'xxx', 那么就会调用 这个 set 方法, 设置的值会作为参数传入 set
        // console.log( '赋值的新值为: ', newVal );
        _gender = newVal;
      }
    } );
复制代码

image.png

这里其实是给_gender赋的值,但是不能将_gender暴露在全局作用域下,这样也不安全,再者,给一个属性设置get/set,就要设置一个中间变量,增加内存消耗。

在Vue中,如何给data里的对象转换为响应式的呢?

在 Vue 使用 defineRective( target, key, value, enumerable )这样一个函数的闭包,那么如何实现呢?

1.先看一个简单的版本:

var o = {
      name: 'jim',
      age: 19,
      gender: '男'
    } ;


    // 简化后的版本
    function defineReactive( target, key, value, enumerable ) {
      // 函数内部就是一个局部作用域, 这个 value 就只在函数内使用的变量 ( 闭包 )
      Object.defineProperty( target, key, {
        configurable: true,
        enumerable: !!enumerable,

        get () {
          console.log( `读取 o 的 ${key} 属性` ); // 额外
          return value;
        },
        set ( newVal ) {
          console.log( `设置 o 的 ${key} 属性为: ${newVal}` ); // 额外
          value = newVal;
        }
      } )
    }

    // 将对象转换为响应式的
    let keys = Object.keys( o );
    for ( let i = 0; i < keys.length; i++ ) {
      defineReactive( o, keys[ i ], o[ keys[ i ] ], true );
    }
复制代码

控制台打印结果:

image.png

以上是简化版本,可以看到,对象已经是响应式的了!

但是,这只针对对象是一层结构的,例如: data: {name: ‘张三’},但是实际开发中,对象有N多层,对象中有数组,该如何转换成响应式的呢?

let data = {
      name: '张三'
      , age: 19
      , course: [
        { name: '语文' },
        { name: '数学' },
        { name: '英语' },
      ]
    }; // 除了递归还可以使用队列 ( 深度优先转换为广度优先 )



    let ARRAY_METHOD = [
      'push',
      'pop',
      'shift',
      'unshift',
      'reverse',
      'sort',
      'splice',
    ];
    let array_methods = Object.create( Array.prototype );
    ARRAY_METHOD.forEach( method => {
      array_methods[ method ] = function () {
        // 调用原来的方法
        console.log( '调用的是拦截的 ' + method + ' 方法' );

        // 将数据进行响应式化
        for( let i = 0; i < arguments.length; i++ ) {
          reactify( arguments[ i ] );
        } 

        let res = Array.prototype[ method ].apply( this, arguments );
        // Array.prototype[ method ].call( this, ...arguments ); // 类比
        return res;
      }
    } );

    function defineReactive( target, key, value, enumerable ) {
      // 函数内部就是一个局部作用域, 这个 value 就只在函数内使用的变量 ( 闭包 )

      if ( typeof value === 'object' && value != null && !Array.isArray( value ) ) {
        // 是非数组的引用类型
        reactify( value ); // 递归
      }

      Object.defineProperty( target, key, {
        configurable: true,
        enumerable: !!enumerable,

        get () {
          console.log( `读取 ${key} 属性` ); // 额外
          return value;
        },
        set ( newVal ) {
          console.log( `设置 ${key} 属性为: ${newVal}` ); // 额外
          value = newVal;
        }
      } );
    }
    // 将对象 o 响应式化
    function reactify( o ) {
      let keys = Object.keys( o );

      for ( let i = 0; i < keys.length; i++ ) {
        let key = keys[ i ]; // 属性名
        let value = o[ key ];
        if ( Array.isArray( value ) ) {
          // 数组
          value.__proto__ = array_methods; // 数组就响应式了
          for ( let j = 0; j < value.length; j++ ) {
            reactify( value[ j ] ); // 递归
          }
        } else {
          // 对象或值类型
          defineReactive( o, key, value, true );
        }
      }
    }
    

    reactify( data );
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享