解析vue3响应式原理以及api编写

前言

vue3响应式原理加api编写,快速明白vue3响应式原理

GitHub博客:https://github.com/jiejiangzi/blog/issues/8

vue3响应式原理实现

先写一段代码看下

实现effect

var name = 'sl', age = 22;
effect1 = () => `我叫${name},今年${age}岁`
effect2 = () => `我叫${name},今年${age+1}岁`
console.log(effect1()) //我叫sl,今年22岁
console.log(effect2()) //我叫sl,今年23岁
age = 30;
console.log(effect1())  //我叫sl,今年30岁
console.log(effect2())  //我叫sl,今年31岁

看看有什么可以优化的点呢?

首先:多个函数,在age发生变化后需要手动再次调用多个函数才可以获取最新信息

期望可以修改信息以后自动调用多个函数

如何实现呢

可以想到将多个函数存放到一起存放到gather函数,并且让age发生变化时可以将多个函数调用trigger调用

实现gather及trigger

var name = "sl",
  age = 22;
var tom, joy;
effect1 = () => (tom = `我叫${name},今年${age}岁`);
effect2 = () => (joy = `我叫${name},今年${age + 1}岁`);
var dep = new Set();
function gather() {
  dep.add(effect1);
  dep.add(effect2);
}
function trigger() {
  dep.forEach((effect) => effect());
}
gather();
effect1()
effect2()
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
age = 30;
trigger()
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁

再继续看下还是有什么可以优化的点

如果变量是一个对象或多个对象的话该怎么处理呢

  • 变量为原始类型时Set存储

  • 变量为对象时可以用map存储

  • 多个对象时用weakMap存储

var obj1 = { name: "tom", age: 22 };
var obj2 = { name: "joy", age: 23 };
var tom, joy;
effect1 = () => (tom = `我叫${obj1.name},今年${obj1.age}岁`);
effect2 = () => (joy = `我叫${obj2.name},今年${obj2.age}岁`);
var depsMap = new WeakMap();
function gather(target, key) {
  let depMap = depsMap.get(target);
  if (!depMap) {
    depsMap.set(target, (depMap = new Map()));
  }
  let dep = depMap.get(key);
  if (!dep) {
    depMap.set(key, (dep = new Set()));
  }
  if (target === obj1) {
    dep.add(effect1);
  } else {
    dep.add(effect2);
  }
}
function trigger(target, key) {
  let depMap = depsMap.get(target);
  if (depMap) {
    const dep = depMap.get(key);
    if (dep) {
      dep.forEach((effect) => effect());
    }
  }
}
gather(obj1, "age");//收集依赖
gather(obj2, "age");//收集依赖
effect1();
effect2();
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
obj1.age = 30;
obj2.age = 10;
trigger(obj1, "age");
trigger(obj2, "age");
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁

在继续看看有哪些可以优化的点

上边依赖的收集gather以及函数的更新通知trigger每次都是手动收集手动触发更新,那有什么方法可以自动收集及触发吗

Proxy

实现reactive

先写一个reactive函数

function reactive(target) {
  const handle = {
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(receiver,key) // 设置值时触发自动更新
    },
    get(target, key, receiver) {
      gather(receiver, key); // 访问时收集依赖
      return Reflect.get(target, key, receiver);
    },
  };
  return new Proxy(target, handle);
}

然后将reactive函数应用到之前代码

var obj1 = reactive({ name: "tom", age: 22 });
var obj2 = reactive({ name: "joy", age: 23 });
var tom, joy;
effect1 = () => (tom = `我叫${obj1.name},今年${obj1.age}岁`);
effect2 = () => (joy = `我叫${obj2.name},今年${obj2.age}岁`);
var depsMap = new WeakMap();
function gather(target, key) {
  let depMap = depsMap.get(target);
  if (!depMap) {
    depsMap.set(target, (depMap = new Map()));
  }
  let dep = depMap.get(key);
  if (!dep) {
    depMap.set(key, (dep = new Set()));
  }
  if (target === obj1) {
    dep.add(effect1);
  } else {
    dep.add(effect2);
  }
}
function trigger(target, key) {
  let depMap = depsMap.get(target);
  if (depMap) {
    const dep = depMap.get(key);
    if (dep) {
      dep.forEach((effect) => effect());
    }
  }
}
effect1();
effect2();
console.log(tom); //我叫sl,今年22岁
console.log(joy); //我叫sl,今年23岁
obj1.age = 30;
obj2.age = 10;
console.log(tom); //我叫sl,今年30岁
console.log(joy); //我叫sl,今年31岁

然后还有个问题,就是gather函数中有写死dep添加函数

如何解决呢 重写effect函数

let activeEffect = null
function effect(fn) {
  activeEffect = fn;
  activeEffect();
  activeEffect = null; // 执行后立马变成null
}
var depsMap = new WeakMap();
function gather(target, key) {
  // 避免例如console.log(obj1.name)而触发gather
  if (!activeEffect) return;
  let depMap = depsMap.get(target);
  if (!depMap) {
    depsMap.set(target, (depMap = new Map()));
  }
  let dep = depMap.get(key);
  if (!dep) {
    depMap.set(key, (dep = new Set()));
  }
  dep.add(activeEffect) //将函数添加到依赖
}
effect(effect1);
effect(effect2);

reactive也已经实现了,那么还有ref也实现下

ref

vue3中ref怎么使用呢

var name = ref('tom')
console.log(name.value) // tom

需要使用.value的方式获取值

function ref(name){
    return reactive(
        {
            value: name
        }
    )
}
const name = ref('tom');
console.log(name.value) //tom

完整代码

var activeEffect = null;
function effect(fn) {
  activeEffect = fn;
  activeEffect();
  activeEffect = null; 
}
var depsMap = new WeakMap();
function gather(target, key) {
  // 避免例如console.log(obj1.name)而触发gather
  if (!activeEffect) return;
  let depMap = depsMap.get(target);
  if (!depMap) {
    depsMap.set(target, (depMap = new Map()));
  }
  let dep = depMap.get(key);
  if (!dep) {
    depMap.set(key, (dep = new Set()));
  }
  dep.add(activeEffect)
}
function trigger(target, key) {
  let depMap = depsMap.get(target);
  if (depMap) {
    const dep = depMap.get(key);
    if (dep) {
      dep.forEach((effect) => effect());
    }
  }
}
function reactive(target) {
  const handle = {
    set(target, key, value, receiver) {
      Reflect.set(target, key, value, receiver);
      trigger(receiver, key); // 设置值时触发自动更新
    },
    get(target, key, receiver) {
      gather(receiver, key); // 访问时收集依赖
      return Reflect.get(target, key, receiver);
    },
  };
  return new Proxy(target, handle);
}
function ref(name){
    return reactive(
        {
            value: name
        }
    )
}

推荐学习:《最新的5个vue.js视频教程精选》

以上就是解析vue3响应式原理以及api编写的详细内容,更多请关注www.sxiaw.com其它相关文章!