手动实现一个极简版 Vue3 Reactivity

862 阅读1分钟

前言

之前对Vue3的响应式源码进行比较详细的解读,这次就来手动实现一个简单的响应式

响应式原理

我们首先来通过流程图整体回顾下Vue3 Reactivity的整体流程

sxijbV.png

代码实现

工具函数

// 判断是否是对象
function isObject(val) {
  return typeof val === 'object' && val !== null
}
// 对象是否有某个属性
function hasOwn(target,key) {
  return target.hasOwnProperty(key)
}

数据存储

// 已有代理的存储
let toProxy = new WeakMap();
let toRaw = new WeakMap();

// 当前激活的effect栈
let activeEffectstack = []
// 依赖映射表
let targetMap = new Map()

主要逻辑

// 创建响应式对象
function reactive(target) {
  return createReactiveObj(target)
}

function createReactiveObj(target) {
  // 仅接受Object对象
  if(!isObject(target)) {
    return target;
  }
  // 检查是否已代理过该对象
  let proxy = toProxy.get(target);

  if(proxy) {
    return proxy
  }

  if(toRaw.has(target)) {
    return target;
  }
  // 使用proxy代理
  let observed = new Proxy(target,baseHandler);

  // 维护proxy表
  toProxy.set(target,observed);
  toRaw.set(observed,target);

  return observed;
}

track & trigger

function track(target,key) {
  // 当前激活栈的栈顶就是当前激活的effect
  const effect = activeEffectstack[activeEffectstack.length -1];
  if(effect) {
  	// 维护依赖表
    let depsMap = targetMap.get(target);
    if(!depsMap) {
      targetMap.set(target,(depsMap = new Map()))
    }
    let deps = depsMap.get(key)

    if(!deps) {
      depsMap.set(key,(deps = new Set()))
    }

    if(!deps.has(effect)) {
      deps.add(effect)
    }
  }
}

function trigger(target,key,type) {
  // 从依赖表中获取effect并逐个执行
  let depsMap = targetMap.get(target)

  if(depsMap) {
    let deps = depsMap.get(key)
    if(deps) {
      deps.forEach((effect)=>{
        effect();
      })
    }
  }
}

effect

// 依赖函数
function effect(fn) {
  const effectF = function () {
    try {
      // 加入effect栈
      activeEffectstack.push(effectF)
      return fn()
    } finally {
      activeEffectstack.pop();
    }
  }
  // 创建effect就会触发一次
  effectF();
  return effectF
}

演示

let obj = reactive({name: 'raw'});

effect(()=>{
  console.log(obj.name);
})

obj.name = 'observed'

sxkIpQ.png