Vue.js设计与实现分支切换与清除学习总结

 更新时间:2023年05月31日 14:48:54   作者:玛拉_以琳  
这篇文章主要为大家介绍了Vue.js设计与实现分支切换与清除学习总结,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

分支切换

const data = { ok: true, text: 'hello world' }
const obj = new Proxy(data, {/*....*/}
effect(function effectFn () {
    document.body.innerText = obj.ok ? obj.text : 'not'
})

如上代码, effectFn函数中存在三元表达式, 根据objok属性的值执行响应的代码, ok属性改变时, 执行的代码也会改变, 就是所谓分支切换. 在effectFn中, 若ok的值为true, 会触发oktext的读取操作, 因此该函数与这两个响应数据都建立了联系, 如下图:

但是当okfalse时, 不会对text进行读取操作, 因此无论text的值怎么改变, DOM都不会进行更新, 但是因为与辅助用函数进行了绑定, 函数依然会运行, 这是不应该的.

为要解决这个问题, 可以在每次副作用函数执行时, 可以先将其从所有与之关联的集合中删除. 因此就需要记录哪些集合中(就是上图中的Set)含有该副作用函数, 可以给副作用函数, 添加一个属性, 值为数组用于记录, 如下代码:

// 使用全局变量储存被注册的副作用函数
let activeEffect = undefined
function effect (fn) {
    const effectFn = () => {
        // 当 effectFn 执行时, 将其设置为当前激活的副作用函数
        activeEffect = effectFn
        fn()
    }
    // activeEffect.deps 用与储存所有包含该副作用函数(即关联该副作用函数)的集合 Set
    activeEffect.deps = []
    // 执行副作用函数
    effectFn()
 }

在副作用函数是Proxy中的拦截读取操作时绑定的, 因此可以在拦截读取操纵中收集包含该副作用函数的集合:

function track (target, key) {
    // 没有 activeEffect 直接结束
    if (activeEffect) return
    // 根据 target 从 WeakMap中获取 Map
    let depsMap = bucket.get(target)
    if (!depsMap) bucket.set(target, (depsMap = new Map()))
    let desp = depsMap.get(key)
    if (!deps) depsMap.set(key, (deps = new Set()))
    // 将当前激活的副作用函数添加到依赖集合中
    deps.add(activeEffect)
    // deps 就是存有当前副作用函数的集合, 即存在联系的依赖集合
    // 将其添加到 activeEffect.deps 中
    activeEffect.deps.push(deps)
}

关系如下图:

清除依赖

根据上图的联系, 就可以在副作用函数每次执行时, 根据 effectFn.deps 将副作用函数从依赖中(Set)删除

// 使用全局变量储存被注册的副作用函数
let activeEffect = undefined
function effect (fn) {
    const effectFn = () => {
        // 调用 cleanup 函数完成清理
        cleanup(effectFn)
        // 当 effectFn 执行时, 将其设置为当前激活的副作用函数
        activeEffect = effectFn
        fn()
    }
    // activeEffect.deps 用与储存所有包含该副作用函数(即关联该副作用函数)的集合 Set
    activeEffect.deps = []
    // 执行副作用函数
    effectFn()
 }
 function cleanup(effectFn) {
     // 遍历数组
     for (let item of effectFn.deps){
         // item 就是副作用函数集合(Set)
         item.delete(effectFn)
     }
     // 最后重置数组
     effectFn.deps.length = 0
 }

但此时会导致现在的响应式代码出现无限循环, 问题出在拦截设置操作中:

function trigger (target, key) {
    const depsMap = bucket.get(target)
    if (!depsMap) return
    const effects = depsMap.get(key)
    effects && effects.forEach(fn => fn()) // 问题出在这一行
}

上面代码中, 最后一行遍历的effects实际上就是当前key的副作用集合Set, 在遍历中副作用函数会运行, 此时会cleanup进行清除, 但是副作用函数的执行又会将其重新被收集到同一个集合中, 出现了一边删除该函数一边收集该函数导致死循环. 可以用另一个SSet进行遍历

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

目前为止响应式完整代码

// 储存副作用函数的桶
  const bucket = new WeakMap()
  // 用于储存被注册的副作用的函数
  let activeEffect = undefined
  function cleanup (effectFn) {
    for (let itme of effectFn.deps) {
      itme.delete(effectFn)
    }
    effectFn.deps.length = []
  }
  function effect (fn) {
    const effectFn = () => {
      cleanup(effectFn)
      activeEffect = effectFn
      fn()
    }
    effectFn.deps = []
    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)
    // 执行副作用函数
    // effects && effects.forEach(fn => fn())
    const effectsFnRun = new Set(effects)
    effectsFnRun.forEach(fn => fn())
  }
  effect(() => {
    console.log('effect run');
    document.body.innerText = obj.ok ? obj.text : 'not'
  })
  setTimeout(() => {
    obj.ok = false
  }, 2000)

以上就是Vue.js设计与实现分支切换与清除学习总结的详细内容,更多关于Vue.js分支切换清除的资料请关注脚本之家其它相关文章!

相关文章

  • 一文搞懂Vue3.2中setup语法糖使用

    一文搞懂Vue3.2中setup语法糖使用

    在vue3版本中,引入了一个新的函数,叫做setup。本文将通过实例为大家详细讲讲Vue3.2中setup语法糖的使用,感兴趣的小伙伴可以了解一下
    2022-07-07
  • 使用vuex缓存数据并优化自己的vuex-cache

    使用vuex缓存数据并优化自己的vuex-cache

    这篇文章主要介绍了使用vuex缓存数据并优化自己的vuex-cache,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • vue测试环境打包与生产环境打包文件数量不一致解决方案

    vue测试环境打包与生产环境打包文件数量不一致解决方案

    这篇文章主要为大家介绍了vue测试环境打包与生产环境打包文件数量不一致的解决方案,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • vue实现下拉多选、可搜索、全选功能(示例代码)

    vue实现下拉多选、可搜索、全选功能(示例代码)

    本文介绍了如何在Vue中实现一个树形结构的下拉多选组件,支持任意一级选项的选择,全选功能,以及搜索功能,通过在mounted生命周期中获取数据,并使用handleTree函数将接口返回的数据整理成树形结构,实现了这一功能,感兴趣的朋友一起看看吧
    2025-01-01
  • vue项目两种方式实现竖向表格的思路分析

    vue项目两种方式实现竖向表格的思路分析

    这篇文章主要介绍了vue项目两种方式实现竖向表格的思路分析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 关于单文件组件.vue的使用

    关于单文件组件.vue的使用

    这篇文章主要介绍了关于单文件组件.vue的使用,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • vue3中的对象时为proxy对象如何获取值(两种方式)

    vue3中的对象时为proxy对象如何获取值(两种方式)

    使用vue3.0时,因为底层是使用proxy进行代理的所以当我们打印一些值得时候是proxy代理之后的是Proxy<BR>对象,Proxy对象里边的[[Target]]才是真实的对象,那么如何获取这个值呢,下面下面给大家介绍两种方式,感兴趣的朋友一起看看吧
    2023-01-01
  • 关于vue-resource报错450的解决方案

    关于vue-resource报错450的解决方案

    本篇文章主要介绍关于vue-resource报错450的解决方案,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • vue之el-tree懒加载数据并且实现树的过滤问题

    vue之el-tree懒加载数据并且实现树的过滤问题

    这篇文章主要介绍了vue之el-tree懒加载数据并且实现树的过滤问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • el-element中el-table表格嵌套el-select实现动态选择对应值功能

    el-element中el-table表格嵌套el-select实现动态选择对应值功能

    这篇文章主要给大家介绍了关于el-element中el-table表格嵌套el-select实现动态选择对应值功能的相关资料,文中通过实例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-01-01

最新评论