vue响应式原理实现

113 阅读2分钟

vue通过使用Object.defineProperty实现数据的响应式更新,(vue3使用proxy),响应式本质是数据和函数的关联,数据更新会执行依赖该数据的函数

下面自己写一个dmeo模拟下响应式的实现

//例如更新如下dom
 <div>
     姓名:<span class="data"></span>
 </div>

//通过修改如下数据更新dom
const obj = {
    data: '张三';
}

//模拟render方法, 简单实现dom更新
function setName() {
   document.getElementsByClassName('data')[0].innerHTML = obj.data;
}

建立了dom,数据,还模拟了render方法,接下来通过执行observer方法实现数据响应式更新

function observer(obj) {
  for (const key in obj) {
    let value = obj[key];
    Object.defineProperty(obj, key, {
      get: function () {
        //当获取数据的时候执行get方法,同时收集使用了该数据的函数
        return value;
      },
      set: function (newValue) {
        //改变该数据的时候执行set方法,同时派发跟新,执行之前收集的函数,实现dom的渲染
        value = newValue;
        setName();
      },
    });
  }
}

当调用set的时候执行了render方法(这里是setName),这个render方法就是依赖该数据的方法,也就是get里面收集的方法,但是get方法只知道obj数据被读取了,并不知道该数据是在哪个函数里面读取的,所以需要对当前正在执行的函数进行监控

//当执行render的时候,修改当前window的_func属性为当前函数,这个get函数里面就可以通过读取window._func知道当前哪个函数在执行
window._func = render;
render();
window._func = null;

//这样每执行一个函数都需要这样使用window._func包裹一层,函数多了会很冗余,可以单独封装个函数,把要执行的函数交给该函数
function getDepFn(fn){
    window._func = fn;
    fn();
    window._func = null;
}

然后再重写observer方法

function observer(obj) {
  for (const key in obj) {
    let value = obj[key];
    const arr = []; //收集依赖该数据的函数
    Object.defineProperty(obj, key, {
      get: function () {
        //当获取数据的时候执行get方法,同时收集使用到该数据的函数
        if(!arr.includes(window._func) && window._func){
          arr.push(window._func);
        }
        return value;
      },
      set: function (newValue) {
        //改变该数据的时候执行set方法,同时派发跟新,执行之前收集的函数,实现dom的渲染
        value = newValue;
        arr.forEach(fn => {
          fn();
        });
      },
    });
  }
}

上面只是简单模拟了vue的响应式实现实现,实际的源码会复杂的多,有许多细节需要处理,这里不就详细展开。