Vue3源码reactivity响应式篇之reactive响应式对象的track与trigger

概览

BaseReactiveHandler类的get方法中,有如下代码块if (!isReadonly2){track(target, "get", key);},这表示通过reactiveshallowReactive创建的响应式对象,非只读的,当读取代理对象proxyTarget的某个属性key时,都会被该get方法拦截,即调用track()方法建立依赖。

而当对代理对象proxyTarget进行赋值或更新某个属性的值时,会被set方法拦截,即调用trigger()方法触发依赖(而删除会被deleteProperty拦截)。

因此对于reactive响应式对象的响应式处理,和tracktrigger方法密不可分。

本文主要介绍tracktrigger是如何进行依赖的收集与触发的全流程。

源码分析

在vue3中,维护了一个全局的targetMap WeakMap实例对象,用于存储响应式对象与依赖的映射关系。

js 复制代码
const targetMap = new WeakMap();

track方法

track方法收集依赖就是往变量targetMap中添加相关元素,存储响应式对象与依赖的映射关系。

track的源码实现如下:

js 复制代码
function track(target, type, key) {
    if (shouldTrack && activeSub) {
        let depsMap = targetMap.get(target);
        if (!depsMap) {
            targetMap.set(target, depsMap = new Map());
        }
        let dep = depsMap.get(key);
        if (!dep) {
            depsMap.set(key, dep = new Dep());
            dep.map = depsMap;
            dep.key = key;
        }
        dep.track();
    }
}

这里暂且不论shouldTrackactiveSub,假定满足track的条件。首先从targetMap中读取depsMap,若depsMap中不存在,则创建一个新的Map的实例,并将其赋值给depsMap,并且存储到targetMap中。然后从depsMap中获取key对应的依赖关系dep,同理,若dep不存在,则创建一个新的Dep实例,并将其赋值给dep,并且存储到depsMap中,然后将depmapkey分别绑定depsMapkey。最后调用dep.track()方法。

dep.track()执行后,会将当前的activeSub添加到depsubs数组中。

trigger方法

track方法的源码实现如下:

js 复制代码
function trigger(target, type, key, newValue, oldValue, oldTarget) {
    const depsMap = targetMap.get(target);
    if (!depsMap) {
        globalVersion++;
        return;
    }
    const run = (dep) => {
        if (dep) {
            dep.trigger();
        }
    };
    startBatch();
    if (type === "clear") {
        depsMap.forEach(run);
    } else {
        const targetIsArray = isArray(target);
        const isArrayIndex = targetIsArray && isIntegerKey(key);
        if (targetIsArray && key === "length") {
            const newLength = Number(newValue);
            depsMap.forEach((dep, key2) => {
                if (key2 === "length" || key2 === ARRAY_ITERATE_KEY || !isSymbol(key2) && key2 >= newLength) {
                    run(dep);
                }
            });
        } else {
            if (key !== void 0 || depsMap.has(void 0)) {
                run(depsMap.get(key));
            }
            if (isArrayIndex) {
                run(depsMap.get(ARRAY_ITERATE_KEY));
            }
            switch (type) {
                case "add":
                    if (!targetIsArray) {
                        run(depsMap.get(ITERATE_KEY));
                        if (isMap(target)) {
                            run(depsMap.get(MAP_KEY_ITERATE_KEY));
                        }
                    } else if (isArrayIndex) {
                        run(depsMap.get("length"));
                    }
                    break;
                case "delete":
                    if (!targetIsArray) {
                        run(depsMap.get(ITERATE_KEY));
                        if (isMap(target)) {
                            run(depsMap.get(MAP_KEY_ITERATE_KEY));
                        }
                    }
                    break;
                case "set":
                    if (isMap(target)) {
                        run(depsMap.get(ITERATE_KEY));
                    }
                    break;
            }
        }
    }
    endBatch();
}

trigger方法在响应式数据发生变化后会被触发,vue3会先判断全局响应式集合对象targetMap中是否存在依赖对象depsMap,即是否有被追踪依赖,若不存在,则将globalVersion1 ,然后直接返回,没有下一步;定义run方法;调用startBatch方法,表示要进行批处理。判断type是否clear,是,则遍历依赖对象depsMap,执行run方法;否则判断target是否是数组,以及key是否数组的索引,然后根据typekey取出响应式数据对应的具体依赖,调用run方法;最后调用endBatch方法。

相关推荐
SoaringHeart1 小时前
Flutter最佳实践:IM聊天文字链接自动识别跳转
前端·flutter
掘金一周2 小时前
企业中要做智能体,最佳的方案是什么? | 沸点周刊 6.18
前端·人工智能·ai编程
Darling噜啦啦2 小时前
CSS 3D 变换与 Flex 布局实战:从零打造旋转立方体
前端·css
秃头网友小李2 小时前
前端难点:keep-alive 缓存什么?RouterView 的 key 为什么要带 scopeId?
前端·vue.js
鱼人2 小时前
CSS 变量:一个变量救你一百次复制粘贴
前端
长大19882 小时前
CSS 到底是什么?和 HTML 的区别一次讲清楚
前端
禅思院2 小时前
路由性能优化终极指南:从懒加载漏洞到边缘渲染的架构跃迁
前端·架构·前端框架
怕浪猫2 小时前
Electron 开发实战(十六):总结与展望|生态现状、框架对比、行业趋势与学习指南
前端·javascript·electron
文心快码BaiduComate2 小时前
Comate 搭载GLM-5.2:百万上下文,稳定支撑长程任务
前端·程序员·开源
星栈2 小时前
Dioxus 的 `rsx!` 语法:如果你会 React,上手确实特别快
前端·前端框架