企业网站建设的三个核心问题,软文营销文章范文,如何做网络营销推广南宁,邢台市effect.ts#xff1a;详解 1. 理解activeEffect1.1 定义1.2 通过一个例子来说明这个过程a. 副作用函数的初始化b. 执行副作用函数前c. 访问state.countd. get拦截器中的track调用e. 修改state.count时的set拦截器f. trigger函数中的依赖重新执行 1.3 实战应用1.4 activeEffect… effect.ts详解 1. 理解activeEffect1.1 定义1.2 通过一个例子来说明这个过程a. 副作用函数的初始化b. 执行副作用函数前c. 访问state.countd. get拦截器中的track调用e. 修改state.count时的set拦截器f. trigger函数中的依赖重新执行 1.3 实战应用1.4 activeEffect的作用 2. 理解ReactiveEffect2.1 定义2.2 源码解析2.3 重要属性解释 3. 理解effect函数3.1 定义3.2 源码解析3.3 工作原理3.3.1 封装副作用函数3.3.2 自动执行和响应3.3.3 控制执行 3.4 runner解析 4. 小结4.1 副作用函数的初始化4.2 依赖收集Tracking4.3 触发更新Triggering4.4 重新执行副作用函数 在Vue 3的响应式系统中
effect.ts是一个核心文件它负责实现副作用函数
ReactiveEffect的注册、追踪和触发更新机制。这个机制是Vue 3自动响应式更新的基石允许我们构建高效且响应式的用户界面。以下是对
effect.ts文件关键部分的详细解析。 1. 理解activeEffect
在之前的章节中我们看到track函数建立依赖即将当前访问的属性与activeEffect建立依赖关系。然而直接从effect.ts里的源码看起来activeEffect的含义可能不是很明显。因此在学习这部分源码时首先需要清楚activeEffect的概念。
1.1 定义
activeEffect是一个全局变量用于存储当前正在执行的ReactiveEffect实例。当Vue的响应式系统执行一个副作用函数时它会把这个副作用函数对应的ReactiveEffect实例设置为activeEffect这样在副作用函数执行期间任何响应式数据的访问都能通过activeEffect关联起来实现依赖追踪。
1.2 通过一个例子来说明这个过程
import { reactive, effect } from vue;//使用reactive创建一个响应式状态state
const state reactive({ count: 0 });//通过effect注册一个副作用函数这个函数简单地打印出state.count的值
effect(() {console.log(state.count);
});//修改state.count的值时希望上面注册的副作用函数能被重新执行从而打印出新的count值
state.count;如何实现依赖追踪
a. 副作用函数的初始化
调用effect()函数时effect函数内部会创建一个ReactiveEffect实例来封装这个副作用函数并立即执行副作用函数。
b. 执行副作用函数前
在执行这个副作用函数之前effect函数会将这个ReactiveEffect实例设置为activeEffect表示当前正在执行的副作用。
c. 访问state.count
副作用函数内部访问了state.count触发state的get拦截器。
d. get拦截器中的track调用
get拦截器调用track函数track检查activeEffect是否存在从而将state.count与副作用函数关联。
e. 修改state.count时的set拦截器
修改state.count触发state的set拦截器进而触发trigger函数。
f. trigger函数中的依赖重新执行
trigger根据建立的依赖关系找到所有依赖于state.count的副作用函数并重新执行包括我们的示例副作用函数。
1.3 实战应用
改造我们之前的vue项目里的代码来看一下实际使用 在页面初始化时effect函数就会立即执行一次副作用函数输出0
执行点击事件state.count值变化会触发副作用函数输出1
1.4 activeEffect的作用
全局追踪: activeEffect允许响应式系统知道当前执行的副作用函数是自动依赖追踪的关键。 依赖收集: 当响应式数据被访问时通过activeEffect将数据与副作用函数关联起来。 更新触发: 响应式数据变化时通过收集的依赖信息找到并执行相关副作用函数。
2. 理解ReactiveEffect
2.1 定义
ReactiveEffect类封装副作用函数及其行为代理副作用函数的执行来跟踪依赖并在依赖数据变化时触发更新。
2.2 源码解析
export class ReactiveEffectT any {//表示这个副作用是否处于活跃状态。如果为false则副作用不会再响应依赖数据的变化。active true//存储了这个副作用所依赖的所有数据的集合Dep类型。每个Dep代表一个响应式数据的依赖关系。deps: Dep[] []/*** 某些属性或方法可以在ReactiveEffect类的实例被创建后添加或修改computed属性就是一个例子。* 可选属性这个属性用于指示副作用是否与计算属性相关联可以在创建ReactiveEffect实例之后附加上去* internal - internal标记意味着这部分内容主要供框架内部使用并非设计为库或框架的公开API的一部分*/computed?: ComputedRefImplT/*** internal* 可选属性表示是否允许这个副作用递归地调用自身*/allowRecurse?: boolean//可选回调函数当这个副作用被停止时调用onStop?: () void// dev only仅在开发模式下使用的回调函数用于调试跟踪onTrack?: (event: DebuggerEvent) void// dev only仅在开发模式下使用的回调函数用于触发更新onTrigger?: (event: DebuggerEvent) void//_dirtyLevel等内部属性: 用于内部状态管理比如判断副作用的脏检查级别等/*** internal*/_dirtyLevel DirtyLevels.Dirty/*** internal*/_trackId 0/*** internal*/_runnings 0/*** internal*/_shouldSchedule false/*** internal*/_depsLength 0/*** 构造函数* fn副作用函数* trigger触发函数内部使用通常为NOOP即空操作* scheduler可选的调度器函数* scope可选的作用域*/constructor(public fn: () T,public trigger: () void,public scheduler?: EffectScheduler,scope?: EffectScope,) {recordEffectScope(this, scope)}/*** dirty属性的get方法在ReactiveEffect类中是用来确定副作用函数是否需要被重新执行的。这个机制主要用于优化计算属性和其他依赖缓存的场景*/public get dirty() {//检查_dirtyLevel检查当前副作用的_dirtyLevel脏检查级别if (this._dirtyLevel DirtyLevels.MaybeDirty_ComputedSideEffect ||this._dirtyLevel DirtyLevels.MaybeDirty) {//查询脏数据为了确定是否真的需要重新计算会将_dirtyLevel设置为QueryingDirtythis._dirtyLevel DirtyLevels.QueryingDirty//并暂时停止依赖追踪以避免在这个过程中不必要地收集新的依赖pauseTracking()/*** 检查计算属性遍历所有已注册的依赖这些依赖代表副作用函数所依赖的数据。*/for (let i 0; i this._depsLength; i) {const dep this.deps[i]//如果其中的依赖是计算属性dep.computed则会尝试触发这些计算属性的重新计算通过triggerComputed函数。if (dep.computed) {triggerComputed(dep.computed)//如果在这个过程中发现_dirtyLevel变为Dirty表示确实有数据变化需要重新计算副作用函数那么循环就会提前结束if (this._dirtyLevel DirtyLevels.Dirty) {break}}}/*** 重置脏检查级别如果遍历完所有依赖后_dirtyLevel仍然是QueryingDirty* 表示没有发现需要更新的数据那么将_dirtyLevel设置为NotDirty意味着不需要重新执行副作用函数。*/if (this._dirtyLevel DirtyLevels.QueryingDirty) {this._dirtyLevel DirtyLevels.NotDirty}//最后恢复依赖追踪resetTracking()}//返回值最后通过检查_dirtyLevel是否大于等于Dirty来决定是否标记为脏如果是则表示需要重新执行副作用函数return this._dirtyLevel DirtyLevels.Dirty}public set dirty(v) {this._dirtyLevel v ? DirtyLevels.Dirty : DirtyLevels.NotDirty}/*** 执行副作用函数* 在执行之前会设置全局的activeEffect为当前实例以便在副作用函数执行期间能够收集依赖。* 执行完成后恢复activeEffect和shouldTrack的状态。* 如果副作用当前不处于活跃状态直接执行副作用函数而不进行依赖收集*/run() {this._dirtyLevel DirtyLevels.NotDirtyif (!this.active) {return this.fn()}let lastShouldTrack shouldTracklet lastEffect activeEffecttry {//shouldTrack设置为true允许依赖收集shouldTrack true/*** this引用的是ReactiveEffect类的当前实例* 将全局的activeEffect变量设置为当前的副作用实例*/activeEffect this/*** _runnings属性记录了当前副作用函数被执行的次数* 这个计数器主要用于内部管理确保副作用的正确执行和清理*/this._runningspreCleanupEffect(this)return this.fn()} finally {postCleanupEffect(this)this._runnings--activeEffect lastEffectshouldTrack lastShouldTrack}}/*** 停止这个副作用响应其依赖数据的变化。在停止前后会执行一些清理操作并调用onStop回调如果有的话*/stop() {if (this.active) {preCleanupEffect(this)postCleanupEffect(this)this.onStop?.()this.active false}}
}2.3 重要属性解释
active: 控制副作用是否响应依赖数据的变化。 deps: 存储副作用所依赖的数据集合。 dirty: 脏检查机制优化计算属性更新。
3. 理解effect函数
3.1 定义
在Vue的响应式系统中effect函数用于注册一个副作用函数effect function这个副作用函数可以自动响应其内部使用的响应式数据的变化。简单来说就是当我们使用reactive或ref创建的响应式数据在这个副作用函数内被访问时Vue会记住这个访问行为并在数据变化时重新执行这个副作用函数。
3.2 源码解析
/*** fn要注册为副作用的函数当响应式数据变化时这个函数会被重新执行。* options可选参数用于控制副作用的行为。常见的选项包括* - lazy如果为true则副作用函数不会立即执行直到手动调用返回的runner函数。* - scheduler自定义的调度函数用于控制副作用函数的重新执行时机。* - onStop当调用stop函数停止副作用时执行的回调函数。*/
export function effectT any(fn: () T,options?: ReactiveEffectOptions,
): ReactiveEffectRunner {// 如果fn已经是一个effect函数则获取其原始函数进行重新包装if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {fn (fn as ReactiveEffectRunner).effect.fn}// 创建一个ReactiveEffect实例封装副作用函数fnconst _effect new ReactiveEffect(fn, NOOP, () {if (_effect.dirty) {_effect.run()}})// 应用传入的options配置到_effect实例上if (options) {extend(_effect, options)if (options.scope) recordEffectScope(_effect, options.scope)}// 如果不是懒执行lazy为false则立即执行一次副作用函数if (!options || !options.lazy) {_effect.run()}// 返回一个可以控制副作用执行的runner函数并附加effect实例const runner _effect.run.bind(_effect) as ReactiveEffectRunnerrunner.effect _effectreturn runner
}3.3 工作原理
3.3.1 封装副作用函数
通过创建一个ReactiveEffect实例来封装传入的fn函数。这个封装包括对副作用函数的执行上下文管理以及依赖追踪和触发逻辑的处理。
3.3.2 自动执行和响应
默认情况下封装的副作用函数会立即执行一次。在执行过程中ReactiveEffect会设置activeEffect指向当前的副作用实例这样任何被访问的响应式数据都会将当前的副作用函数收集为依赖。当响应式数据变化时所有收集的依赖会被重新执行即重新执行副作用函数。
3.3.3 控制执行
effect函数返回一个runner函数通过这个runner函数可以手动控制副作用的执行特别是在配置了lazy选项时。同时runner函数上附带的effect属性允许直接访问到内部的ReactiveEffect实例进一步控制或查询副作用的状态。
3.4 runner解析
const runner _effect.run.bind(_effect) as ReactiveEffectRunner
runner.effect _effect这段代码的目的是创建一个可以被直接调用的函数runner用来控制副作用effect的执行并且让这个runner函数具有一些额外的属性和能力。下面详细解释这段代码的含义和作用
简化API 通过返回一个函数runner使用者可以直接执行这个函数来触发副作用而不需要从_effect实例中调用run方法。这样的API更加简洁直观。
绑定上下文_effect.run.bind(_effect)
使用.bind(_effect)是为了确保在run方法内部this关键字指向的是_effect实例本身而不是其他的上下文。这样无论runner函数在哪里被调用它内部通过this访问的都是正确的ReactiveEffect实例。
类型安全as ReactiveEffectRunner 这是TypeScript的类型断言用来指明runner函数是ReactiveEffectRunner类型。ReactiveEffectRunner类型除了是一个可调用的函数之外还额外附带了一个effect属性。这样做的目的是为了让TypeScript的类型系统知道runner不仅仅是一个普通的函数还是一个特殊的对象拥有effect属性。
保留引用runner.effect _effect 这行代码给runner函数对象添加了一个名为effect的属性并将这个属性的值设置为_effect实例。这样做的目的是让外部代码能够通过runner函数直接访问到内部的ReactiveEffect实例。这在某些场景下非常有用比如需要停止或查询副作用的状态时可以通过runner.effect来操作。
综上所述这段代码创建了一个runner函数这个函数不仅可以被调用来执行副作用还允许外部代码通过runner.effect直接访问和控制副作用实例。这是Vue响应式系统灵活且强大的设计之一。
4. 小结
再看我们1.2的示例来回顾一下这里的每一步都是响应式系统核心机制的重要组成部分
4.1 副作用函数的初始化
使用effect(fn)时Vue会创建一个ReactiveEffect实例这个实例包装了用户定义的副作用函数fn。这时全局的activeEffect被设置为这个实例并且shouldTrack变为true允许依赖收集。
4.2 依赖收集Tracking
在副作用函数执行过程中任何被访问的响应式对象属性例如state.count的访问都会被相应对象的get拦截器捕获。get拦截器会调用track函数将当前的activeEffect副作用函数与这个属性关联起来并在targetMap中记录这个依赖关系。
4.3 触发更新Triggering
当响应式对象的属性被修改时如state.count被赋予一个新值这个操作会被属性所属对象的set拦截器捕获。set拦截器随后调用trigger函数查找所有依赖于被修改属性的副作用函数并触发它们重新执行。
4.4 重新执行副作用函数
trigger函数根据在targetMap中记录的依赖关系找到所有依赖的副作用函数并重新执行它们。这样任何基于这个响应式数据的计算或UI更新都会被自动进行。