手写一个响应式系统

122 阅读2分钟

响应式

const user = reactive({name:'xx'});

const handleUser = (newName) => {
  user.name = newName;
}
  • 构建一个响应式数据对象user, 在模版里面展示user.name, 点击更换名字,模版自动更新user.name为新名字,数据的改变同步视图的更新。

视图自动更新的完整流程

  1. 渲染阶段:Vue 组件渲染时,响应式数据被访问。这时 Proxyget 拦截器会触发依赖收集,将当前的渲染函数(即 effect)与被访问的数据属性关联起来。
  2. 数据修改:当用户交互或其他操作导致响应式数据发生变化时,Proxyset 拦截器会捕捉这个修改操作,并调用 trigger 函数。
  3. 触发更新trigger 函数通知所有依赖于修改后的数据属性的 effect 函数重新执行,这些 effect 函数通常会重新渲染视图。
  4. 视图更新:由于 effect 函数重新执行,组件视图将被重新渲染,展示最新的数据状态。

proxy代理对象做数据劫持

const baseHandlers = {
  get: function(target, key) {
      const res = Reflect.get(target, key)
      track(target, "get", key);
      return res;
  },
  set: function(target, key, value, receiver) {
    const result = Reflect.set(target, key, value, receiver)
    // 在触发 set 的时候进行触发依赖
    trigger(target, "set", key)
    return result
  }
}

// 创建一个Proxy对象对数据进行劫持,进行依赖收集的track,和修改时候的统一响应式触发trigger;
const proxy = new Proxy(
  target,
  baseHandlers
)
proxyMap.set(target, proxy)
return proxy

新组装的effect函数

  • 用来包裹真正的副作用函数fn, 然后执行一遍fn; 进行依赖收集
function effect(fn, options) {
  const effectFn = () => {
    activeEffect = effectFn;
    return fn()
  }
  effectFn();
  return effectFn;
}

track的实现:依赖收集

const map = new Map()
function track(target, key, effect) {
  const depsMap = map.get(target)
  if(!depsMap) {
    const depsMap = new Map();
    map.set(target, depsMap)
  }
  let dep = depsMap.get(key);
  if(!dep) {
    dep = new Set();
  }
  if(!dep.has(activeEffect) && activeEffect) {
    deps.add(activeEffect)
  }
  depsMap.set(key, deps)
 }
  • 依赖收集,就收集进去target[key]对应的副作用函数; 副作用函数和业务如何结合起来,依赖收集的时候为什么要activeEffect;用activeEffect来关联两边副作用的执行;

trigger的实现- 触发更新

function trigger(target, type, key) {
  const desMap = map.get(target)
  if(!desMap) return;
  const desp = desMap.get(key);
  if(!deps) return;
  deps.forEach(effectFn => {
    effectFn();
  })
}