关于前端:Vuejs设计与实现学习总结第四章4无限递归

1次阅读

共计 2259 个字符,预计需要花费 6 分钟才能阅读完成。

const data = {foo: 1}
const obj = new Proxy(data, {/*...*/})

effect(() => obj.foo = obj.foo + 1)

此项操作会引起栈溢出:

Uncaught RangeError: Maximum call sack size exceeded

在此操作中, 会先读取 obj.foo 的值, 这会触发 track 操作, 将副作用函数入栈, 此时有加一并赋值, 此时会触发 trigger 操作, 将副作用函数出栈并执行, 在这种状况下, 该副作用函数还在执行中, 又开始下一次的执行, 导致有限递归调用本人导致栈溢出报错.

在这个操作中读取与设置的是同一个副作用函数 activeEffect, 因而在trigger 要触发时增加条件: 如果 trigger 触发的副作用函数与以后执行的副作用函数雷同, 则不触发执行:

function trigger (target, key) {const depsMap = bucket.get(target)
    if (!depsMap) return
    const effects = depsMap.get(key)
    
    const effectsToRun = new Set()
    effects && effects.forEach(effectFn => {if (effectFn !== activeEffect) {effectsToRun.add(effectFn)
        }
    })

    effectsToRun.forEach(effectFn => effectFn())
}

目前为止响应式残缺代码

  // 贮存副作用函数的桶
  const bucket = new WeakMap()
  
  // 用于贮存被注册的副作用的函数
  let activeEffect = undefined
  // 副作用函数栈
  const effectStack = []

  function cleanup (effectFn) {for (let itme of effectFn.deps) {itme.delete(effectFn)
    }
    effectFn.deps.length = []}
  
  function effect (fn) {const effectFn = () => {cleanup(effectFn)
        
        // 调用以后的副作用函数时, 赋值给 全局变量
        activeEffect = effectFn
        // 在调用副作用函数之前将该函数压入栈
        effectStack.push(effectFn)
        
        fn()
        
        // 以后的副作用函数执行完结后, 出栈
        effectStrack.pop()
        // activeEffect 还原为之前的值
        activeEffect = effectStack[effectStack.length - 1]
    }
    
    effectFn.desp = []
    effectFn()}

  
  const data = {
    text: 'hello world',
    ok: true
  }
  
  
  const obj = new Proxy(data, {
    // 拦挡读取操作
    get (target, key) {track(target, key)
      // 返回属性值
      return target[key]
    },
  
    // 拦挡设置操作
    set (target, key, newVal) {
      // 设置属性值
      target[key] = newVal
      trigger(target, key)
    }
  })
  
  function track (target, key) {
    // 没有 activeEffect, 间接 return
    if (!activeEffect) return target[key]
  
    // 依据 target 从 '桶' 中回去 depsMap, 它也是一个 Map 类型: key ---> effects
    let depsMap = bucket.get(target)
    // 如果 depsMap 不存在, 则新建一个 Map 并与 target 关联
    if (!depsMap) bucket.set(target, (depsMap = new Map()))
    // 再依据 key 从 depsMap 中去的 deps, 它是一个 Set 类型
    // 外面存贮所有与以后 key 相干的副作用函数: effects
    let deps = depsMap.get(key)
    // 如果 deps 不存在, 同样新建一个 Set 并与 key 关联 0
    if (!deps) depsMap.set(key, (deps = new Set()))
    // 最初将以后激活的副作用函数增加到 '桶' 里
    deps.add(activeEffect)
  }
  
  function trigger (target, key) {
    // 依据 target 从 '桶' 中获得 depsMap, 它是 key --> effects
    const depsMap = bucket.get(target)
    if (!depsMap) return
    // 依据 key 获得所有的副作用函数 effects
    const effects = depsMap.get(key)
  
    // 执行副作用函数
    const effectsToRun = new Set()
    effects && effects.forEach(effectFn => {
        // 若触发执行的副作用函数与以后正在执行的副作用函数雷同, 则不触发执行
        if (effectFn !== activeEffect) {effectsToRun.add(effectFn)
        }
    })

    effectsToRun.forEach(effectFn => effectFn())
  }
  
  effect(() => {console.log('effect run');
    document.body.innerText = obj.ok ? obj.text : 'not'
  })
  
  setTimeout(() => {obj.ok = false}, 2000)
正文完
 0