美文网首页
5. vue 响应式原理

5. vue 响应式原理

作者: 一土二月鸟 | 来源:发表于2020-09-30 10:16 被阅读0次
数组和对象类型当值变化时如何劫持到?
  1. 对象内部通过defineReactive方法,使用Object.defineProperty将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。
  2. 多层对象是通过递归来实现劫持,Vue3中是使用proxy来实现响应式数据。
内部依赖收集是怎样做到的?
  1. 每个属性都拥有自己的dep属性,存放他所依赖的watcher,当属性变化后会通知自己对应的watcher去更新
  2. 1)对象层级过深,性能就会差 (2)不需要响应数据的内容不要放到data中 (3)Object.freeze() 可以冻结数据(4)需要响应的数据需要在defineProperty运行之前定义。
  • 对象响应式
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="test"></div>
  <script type="text/javascript">

    let data = { count: 1 };
    let tempFn = null;

    const defineReactive = data => {
      for (let key in data) { 
        const dep = []; // 为每一个属性设置单独的回调工厂,get时,添加回调。set时,执行所有的回调。即实现了reactive。
        let value = data[key]; // 此处的data[key] 为初始值
        Object.defineProperty(data, key, {
          set(val) {
            value = val;
            dep.forEach(fn => fn());
            return val;
          },
          get() {
            if(tempFn) dep.push(tempFn);
            return value;
          }
        })
      }
    }

    defineReactive(data); // 这也是为什么注册响应式属性时,要有初始属性的原因。如果提前没有定义属性,则无法通过defineReactive对该属性预设逻辑。

    const watcher = (fn) => {
      tempFn = fn;
      fn();
      tempFn = null; // 挂载完函数,将其清空,否则在运行get方法时,会添加重复的函数到fnArr中
    }

    watcher(() => { // 注册函数
      test.innerHTML = data.count; // 执行此行代码时,会触发get方法,则会将缓存的tempFn存储到fnArr中
    });

    watcher(() => {
      console.log(data.count); // 执行此行代码时,会触发get方法,则会将缓存的tempFn存储到fnArr中
    });

  </script>
</body>

</html>

数组响应式

  • 因为调用数组是通过api方法的方式,通过改写数组对象的原型链,实现拦截方法的目的。
  • 数组不适用defineProperty的原因是数组的属性为自己的索引,如果数组的数据量特别大,不适合给每个索引进行定义,性能太差。
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <div id="test"></div>
  <script type="text/javascript">

    const arr = [];

    function defineReactive(arr) {
      let arrayMethods = Object.create( Array.prototype );  // 使用Array.prototype是为了保留数组的其他方法可供arr使用

      arr.__proto__ = arrayMethods; // arr 的原型的原型为Array.prototype

      arrayMethods.push = function (val) {
        Array.prototype.push.call(this, val);
        render();
      }

    }

    function render() {
      test.innerHTML = arr;
    }

    defineReactive(arr);

    arr.push(123);

  </script>
</body>

</html>

相关文章

网友评论

      本文标题:5. vue 响应式原理

      本文链接:https://www.haomeiwen.com/subject/nxbnektx.html