Vue.js 3 渐进式实现之响应式系统——第四节:封装 track 和 trigger 函数

往期回顾

  1. 系列开篇与响应式基本实现
  2. effect 函数注册副作用
  3. 建立副作用函数与被操作字段之间的联系

封装 track 和 trigger 函数

目前的实现中,我们直接在 get 拦截函数里编写把副作用函数收集到"桶"里的逻辑,但更好的做法是将这部分逻辑单独封装到一个 track 函数中,取名 track 是为了表达追踪的含义。

同样的,set 中触发副作用函数重新执行的逻辑也可以封装到 trigger 函数中。

代码

javascript 复制代码
// 用一个全局变量存储被注册的副作用函数
let activeEffect
// effect 函数用于注册副作用函数
function effect(fn) {
    // 当调用 effect 注册副作用函数时,将副作用函数 fn 赋值给 activeEffect
    activeEffect = fn
    fn()
}

// 存储副作用函数的桶
const bucket = new WeakMap()

// 原始数据
const data = { text: 'hellow world' }
// 对原始数据的代理
const obj = new Proxy(data, {
    // 拦截读取操作
    get(target, key) {
        // 把副作用函数收集到桶中
        track(traget, key)
        // 返回属性值
        return target[key]
    },
    // 拦截设置操作
    set(target, key newVal) {
        // 设置属性值
        target[key] = newVal
        // 把副作用函数从桶里取出并执行
        trigger(target, key)
        // 返回 true 代表设置操作成功
        return true
    }
})

// track 函数 在 get 拦截函数中被调用,用来追踪副作用函数
function track(target, key) {
    // 没有 activeEffect 直接 return
    if (!activeEffect) return terget[key]

    // 根据 target 从"桶"中取得 depsMap,也是Map类型:key --> effects
    let depsMap = bucket.get(target)
    // 如果不存在 depsMap,就新建一个 Map 并与 target 关联
    if (!depsMap) {
        depsMap = new Map()
        bucket.set(target, depsMap)
    }

    // 再根据 key 从 depsMap 中取得 deps。
    // deps是一个 Set 类型,储存所有与当前 key 相关联的副作用函数
    let deps = depsMap.get(key)
    // 如果 deps 不存在,同样新建一个 Set 并与 key 关联
    if (!deps) {
        deps = new Set()
        depsMap.set(key, deps)
    }

    // 最后将当前激活的副作用函数添加到"桶"里
    deps.add(activeEffect)
}

// trigger 函数 在 set 拦截函数中被调用,用来触发更新
function trigger(target, key) {
    const depsMap = bucket.get(target)
    // 如果这个对象没有被追踪的依赖,没有需要重新运行的副作用函数,直接 return
    if (!depsMap) return

    const effects = depsMap.get(key)
    // 如果这个对象的这个key没有被追踪的依赖,没有需要重新运行的副作用函数,啥也不干
    // 否则就把 effects 中的函数依次执行
    effects && effects.forEach(effect => effect())

    // 使用 ?. 可选链操作符,也可以写成这样
    // bucket.get(target)?.get(key)?.forEach(effect => effect())
}

已实现

本节没有实现任何新功能,只是把依赖收集触发更新 的逻辑分别封装到 tracktrigger 函数中,代码逻辑更清晰并且更加灵活。

负责依赖收集的 track 函数、和负责触发更新的 trigger 函数是响应式系统的核心部分。未来将要实现的很多新功能的逻辑都需要由这两个函数来承载,并且它们也会在不同的地方被调用。因此将 track 和 trigger 单独抽离封装是为了给后续逐步完善响应式系统、增加新功能提供可能。

缺陷/待实现

副作用函数中有不同代码分支比如 if、switch 或三元表达式时,分⽀切换可能会产⽣遗留的副作⽤函数。

下一节我们将详细描述什么是分支切换和遗留的副作用,以及如何解决这个问题。

相关推荐
pe7er34 分钟前
使用 force-resolutions 强制锁定 npm/yarn 依赖版本
前端·javascript
JinSo38 分钟前
EasyDashboard 数据源功能来啦!
前端·javascript·github
晓得迷路了1 小时前
栗子前端技术周刊第 86 期 - React Native 0.80、Bun v1.2.16、Astro 5.10...
前端·javascript·bun
心.c1 小时前
React基础
前端·javascript·react.js
江城开朗的豌豆1 小时前
Vue新手必看!1分钟快速创建项目的魔法命令
前端·javascript·vue.js
10年前端老司机3 小时前
Vue3项目中使用vue-draggable-plus实现拖拽需求简直不要太丝滑
前端·javascript·vue.js
动能小子ohhh8 小时前
html实现登录与注册功能案例(不写死且只使用js)
开发语言·前端·javascript·python·html
Jimmy9 小时前
理解 React Context API: 实用指南
前端·javascript·react.js
crary,记忆10 小时前
微前端MFE:(React 与 Angular)框架之间的通信方式
前端·javascript·学习·react.js·angular
星空寻流年10 小时前
javaScirpt学习第七章(数组)-第一部分
前端·javascript·学习