96行乞丐版reactivity教你debug vue-next

时间:2021-1-8 作者:admin

这边文章主要面向准备着手看vue-next源码,但是无从下手的朋友

  1. 如何通过chrome + vscode debug Vue3 的测试案例
  2. reactivity 精简版(只包含了最基本的数据流逻辑)
  3. 非复刻源码,只注重怎么实现dataFlow

通过chrome + vscode debug Vue3源码

【博客地址】: shancw96.github.io/blogs/2020/…

开启chrome 调试界面 chrome://inspect

  1. 打开 chrome,并输入地址: chrome://inspect
  2. 选择 Open dedicated DevTools for Node

在 package.json – script 添加如下命令

...
"scripts": {
    ...
    "test:debug": "node --inspect node_modules/.bin/jest --runInBand"
  },
...

开始对测试案例进行debug

npm run test:debug <path_to_test>

example:debug reactivity 模块下的reactive.spec.ts

相对路径(vue-next 根目录):./packages/reactivity/__tests__/reactive.spec.ts

  1. 开启chrome://inspect设置

  2. 在测试文件或者源文件(独立模块)中添加debugger

测试模块中添加

`npm run test:debug ./packages/reactivity/__tests__/reactive.spec.ts`
  1. 自动触发debug

96行乞丐版reactivity 奉上,辅助阅读源码

const isObject = val=> val !== null && typeof val === 'object'
// 依赖存放targetMap 
//targetMap : object -> key -> Array<dep>
let targetMap = new WeakMap()
function reactive(target) {
  // 1. 判断是否为对象,proxy 只能代理对象
  if(!isObject(target)) return target
  const proxy = new Proxy(target, {
    get,
    set
  })
  return proxy
}
// proxy - get
function get(target, key, receiver) {
  const res = Reflect.get(target, key, receiver)
  // 依赖收集
  track(target, key)
  // proxy 只代理了当前层的key,如果key 对应的value 是嵌套的,那么需要进行对应的依赖收集
  if(isObject(res)) {
    reactive(res)
  }
  return res
}
// proxy - set
function set(target, key, value, receiver) {
  const result = Reflect.set(target, key, value, receiver)
  trigger(target, key)
  return result
}

// 写在effect下的响应式数据会进行依赖收集(track)
function effect(fn) {
  const effect = createReactiveEffect(fn)
  effect()
  return effect
  function createReactiveEffect(fn) {
    const effect = function reactiveEffect() {
      try {
        activeEffect = effect
        return fn()
      }finally {
        activeEffect = undefined
      }
    }
    effect.deps = []
    return effect
  }
}
// 依赖收集函数
function track(object, key) {
  // 非effect下调用的 proxy.key不进行依赖收集
  if (!activeEffect) {
    return
  }
  // 有副作用函数需要将副作用函数和当前对象的具体key相互关联
  // 1.按照对象初始化分组
  let depsMap = targetMap.get(object)
  if(!depsMap) {
    targetMap.set(object, depsMap = new Map())
  }
  let dep = depsMap.get(key)
  if(!dep) {
    depsMap.set(key, dep = new Set())
  }
  //2. key <-> Array<Effect>
  if(!dep.has(activeEffect)) {
    dep.add(activeEffect)
    activeEffect.deps.push(dep)
  }
}
// 依赖触发
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  if(!depsMap.get(key)) {
    // 从没有被track
    return
  }
  const effects = new Set()
  // 将所有的非 activeEffect 加入到等待执行队列中
  const add = effectsToAdd => {
    if(!effectsToAdd) return
    effectsToAdd.forEach(effect => {
      if(effect !== activeEffect) {// ? 为什么不能是activeEffect
        effects.add(effect)
      }
    })
  }

  // 将depsMap中 key 对应的effect 复制一份,放到等待执行队列中
  if(key !== void 0) {
    add(depsMap.get(key))
  }
  //执行队列
  effects.forEach(effect => effect())
}

简单测试

// -------test--------
// test
const data = {
  msg: 'hey there',
  count: 1
}
const proxyData = reactive(data)
effect(() => {
  console.log('effect(msg)', proxyData.msg)
})
proxyData.msg = 'hey there! i\'m here'
proxyData.count = 2//

effect(() => console.log('effect 2(count):', proxyData.count))
proxyData.count = 'should work'

/**
*effect(msg) hey there
effect(msg) hey there! i'm here
effect 2(count): 2
effect 2(count): should work
*/

写在最后:上述代码根据vue3源码进行大量删减,保留最基本的逻辑功能(依赖收集),会存在很多问题,只进行了基本类型的测试,勿杠。

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。