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 或三元表达式时,分⽀切换可能会产⽣遗留的副作⽤函数。

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

相关推荐
大橙子额7 小时前
【解决报错】Cannot assign to read only property ‘exports‘ of object ‘#<Object>‘
前端·javascript·vue.js
WooaiJava8 小时前
AI 智能助手项目面试技术要点总结(前端部分)
javascript·大模型·html5
LYFlied8 小时前
从 Vue 到 React,再到 React Native:资深前端开发者的平滑过渡指南
vue.js·react native·react.js
Never_Satisfied9 小时前
在JavaScript / HTML中,关于querySelectorAll方法
开发语言·javascript·html
董世昌419 小时前
深度解析ES6 Set与Map:相同点、核心差异及实战选型
前端·javascript·es6
B站_计算机毕业设计之家9 小时前
豆瓣电影数据采集分析推荐系统 | Python Vue Flask框架 LSTM Echarts多技术融合开发 毕业设计源码 计算机
vue.js·python·机器学习·flask·echarts·lstm·推荐算法
WeiXiao_Hyy9 小时前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
xjt_090110 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农10 小时前
Vue 2.3
前端·javascript·vue.js
辰风沐阳11 小时前
JavaScript 的宏任务和微任务
javascript