前言
- 常网IT源码上线啦!
- 本篇录入吊打面试官专栏,希望能祝君拿下Offer一臂之力,各位看官感兴趣可移步🚶。
- 有人说面试造火箭,进去拧螺丝;其实个人觉得问的问题是项目中涉及的点 || 热门的技术栈都是很好的面试体验,不要是旁门左道冷门的知识,实际上并不会用到的。
- 接下来想分享一些自己在项目中遇到的技术选型以及问题场景。
代码 = 人生。
这就像调试一段极其复杂的、包含了无数变量和未知函数的代码。你需要不断地运行、测试、观察结果、找出bug、修改代码、再次运行......

考完软考,去公园逛逛,感觉很像食人花。还有牙齿。
一、前言
在 JavaScript 中,我们可以通过两种技术劫持 property 访问的方式。
getter / setters 和Proxy。
而在Vue3,我们Proxy给reactive
创建响应式对象,使用getter / setters用于ref
劫持基本类型。
reactive
的核心代码:
java
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
track(target, key)
return target[key]
},
set(target, key, value) {
target[key] = value
trigger(target, key)
}
})
}
ref
的核心代码:
java
function ref(value) {
const refObject = {
get value() {
track(refObject, 'value')
return value
},
set value(newValue) {
value = newValue
trigger(refObject, 'value')
}
}
return refObject
}
如代码所示:
-
get = track: Vue 3 的响应式系统在设置属性时通过
track
跟踪并收集依赖 -
set = trigger:在设置属性时通过
trigger
触发更新,来实现响应性。
直入正文。
🤡官接着往下追问:你说get的时候调用了track,set的时候调用了trigger,那他们内部具体做了什么?
二、深挖track和trigger
track
track()
函数其核心职责是建立数据属性 与副作用函数(effect)之间的动态关联。当响应式对象的属性被访问时,系统通过该函数记录下"哪些 effect 依赖于此属性"的关键信息。
如果有effect,就去查找该属性的订阅者集合(Set,从3.4之后就改为Map)。
java
let activeEffect
function track(target, key) {
if (activeEffect) {
// 查找相应属性订阅的 effect 集合,如果没有则新建
const effects = getSubscribersForProperty(target, key)
effects.add(activeEffect)
}
}
activeEffect
是一个全局变量,用于存储当前正在运行的副作用函数。在响应式系统里,副作用函数是指那些会产生副作用(如更新 DOM、发送网络请求等)的函数。在副作用函数运行前,会把这个函数赋值给 activeEffect
,方便后续追踪依赖。
函数功能:
track
函数的作用是追踪对象 target
的属性 key
与当前副作用函数 activeEffect
之间的依赖关系。
参数:
-
target:要追踪的对象。
-
key:要追踪的对象属性名。
执行逻辑:
-
if (activeEffect):检查
activeEffect
是否存在。若存在,说明有副作用函数正在运行,需要进行依赖追踪;若不存在,则不做任何操作。 -
const effects = getSubscribersForProperty(target,key):调用
getSubersForProperty
函数,该函数的作用是查找对象target
的属性key
所订阅的副作用函数集合。若该集合不存在,就会新建一个。 -
effects.add(activeEffect):将当前的副作用函数
activeEffect
添加到属性key
对应的副作用函数集合中,以此建立依赖关系。
讲原理是会枯燥一点的,但耐心看下去,多看几遍,知识就是你的了,Get~
trigger
而trigger()
用来执行 effect。
在 trigger()
内部会find寻找 track()
时保存的该属性的所有订阅者的 effect,从而执行它们。
java
function trigger(target, key) {
const effects = getSubscribersForProperty(target, key)
effects.forEach((effect) => effect())
}
getSubscribersForProperty
函数实现
java
// 模拟 getSubscribersForProperty 函数
const targetMap = new WeakMap();
function getSubscribersForProperty(target, key) {
let depsMap = targetMap.get(target);
if (!depsMap) {
depsMap = new Map();
targetMap.set(target, depsMap);
}
let effects = depsMap.get(key);
if (!effects) {
effects = new Set();
depsMap.set(key, effects);
}
return effects;
}
// 模拟副作用函数
function effect() {
console.log('副作用函数');
}
// 设置 activeEffect
activeEffect = effect;
const target = { name: 'Dignity' };
// 追踪依赖
track(target, 'name');
// 查看依赖关系
const effects = getSubscribersForProperty(target, 'name');
console.log(effects.has(effect)); // 输出: true
理解一下他们之间的关系:
track :get的时候,跟踪、收集依赖
-- 操作effect
-- 这些依赖会存在一个map,在map如果找到,就知道,此时的effect和这一次的依赖有关系;
-- 当然,如果没找到,就创建一个
trigger():set的时候,先去找track,因为里面有保存该属性的所有订阅者的effect,执行effect
effect:更新视图
三、通俗易懂说
还是把重点讲解一下track和trigger的作用。
activeEffect
就像是一个小本子,专门用来记录当前正在干活的那个"工人"(副作用函数)。在干活之前,我们会把这个"工人"的名字写在小本子上,这样后面就能知道是谁在干活啦。
track
函数就像是一个"牵线人",它的工作是把对象的某个属性和正在干活的"工人"联系起来。
- 当有"工人"(副作用函数)正在干活的时候(也就是
activeEffect
有记录),"牵线人"就开始工作了。 - "牵线人"会去看看这个对象的这个属性有没有一个"朋友群"(订阅的副作用函数集合),如果没有就建一个。
- 然后把正在干活的"工人"拉进这个"朋友群"里,这样这个属性和这个"工人"就建立了联系,以后这个属性有变化,就知道要通知这个"工人"来干活啦。
我们模拟了 getSubscribersForProperty
函数,它就像是一个"管理员",负责管理每个对象属性的"朋友群"。
我们定义了一个"工人"(副作用函数 effect),然后把他的名字写在小本子(activeEffect)上。
接着"牵线人"(track 函数)把对象 target
的 name
属性和这个"工人"联系起来。
最后我们查看这个属性的"朋友群",发现这个"工人"已经在里面了,说明联系建立成功啦。
至此撒花~
后记
我们在实际项目中或多或少遇到一些奇奇怪怪的问题。
自己也会对一些写法的思考,为什么不行🤔,又为什么行了?
最后,祝君能拿下满意的offer。
我是Dignity_呱,来交个朋友呀,有朋自远方来,不亦乐乎呀!深夜末班车
👍 如果对您有帮助,您的点赞是我前进的润滑剂。
以往推荐
别在傻傻分不清any void never unknown的场景啦
vue2和Vue3和React的diff算法展开说说:从原理到优化策略
玩转Vue插槽:从基础到高级应用场景(内含为何Vue 2 不支持多根节点)