电子销售网站模板,做网站敲代码的图片,传奇开服表,镇江网站建设费用源码环境搭建
【Vue3源码解析】应用实例创建及页面渲染-CSDN博客
写文章时的Vue 版本#xff1a;
version: 3.5.13,针对单个包进行开发环境打包、测试。
pnpm run dev reactivityreactive 创建响应式对象
packages/reactivity/src/reactive.ts
…源码环境搭建
【Vue3源码解析】应用实例创建及页面渲染-CSDN博客
写文章时的Vue 版本
version: 3.5.13,针对单个包进行开发环境打包、测试。
pnpm run dev reactivityreactive 创建响应式对象
packages/reactivity/src/reactive.ts
export function reactive(target: object) {// if trying to observe a readonly proxy, return the readonly version.if (isReadonly(target)) {return target}/*** 通过调用 createReactiveObject 函数创建一个响应式对象并返回该对象。* 第一个参数是 目标对象* 第二个参数用来判断该对象是否是只读的* mutableHandlers 是一个对象包含了响应式对象的一些操作方法如get、set等。* mutableCollectionHandlers 是一个对象包含了响应式集合对象的一些操作方法如get、set等。* reactiveMap 是一个全局 WeakMap 对象用于缓存 target 到 响应式对象的映射关系。*/return createReactiveObject(target,false,mutableHandlers,mutableCollectionHandlers,reactiveMap,)
}function createReactiveObject(target: Target,isReadonly: boolean,baseHandlers: ProxyHandlerany,collectionHandlers: ProxyHandlerany,proxyMap: WeakMapTarget, any,
) {if (!isObject(target)) {if (__DEV__) {warn(value cannot be made ${isReadonly ? readonly : reactive}: ${String(target,)},)}return target}// target is already a Proxy, return it.// exception: calling readonly() on a reactive object// 如果已经是 Proxy 代理对象则直接返回if (target[ReactiveFlags.RAW] !(isReadonly target[ReactiveFlags.IS_REACTIVE])) {return target}// target already has corresponding Proxy// 如果 ProxyMap 中有 target 对应的 proxy 对象 则直接返回const existingProxy proxyMap.get(target)if (existingProxy) {return existingProxy}// only specific value types can be observed.// 类型无效 则直接返回 targetconst targetType getTargetType(target)if (targetType TargetType.INVALID) {return target}// 创建 target 对应的 Proxy 代理对象 并传入 baseHandlersconst proxy new Proxy(target,// COLLECTION Map/Set/WeakMap/WeakSettargetType TargetType.COLLECTION ? collectionHandlers : baseHandlers,)proxyMap.set(target, proxy)return proxy
}effect 和它的函数参数fn的作用
packages/reactivity/src/effect.ts
/*** 创建一个响应式副作用函数** 此函数用于追踪函数内的响应式数据访问和修改以便在数据变化时自动重新执行该函数* 它是响应式系统的核心部分使得开发者可以轻松创建响应式的应用程序** param fn 要追踪的副作用函数它会接收一个用于触发副作用的函数作为参数* param options 可选的副作用选项允许开发者自定义副作用的行为例如是否应该停止追踪等* returns 返回一个绑定了副作用函数的runner函数用于手动触发副作用*/
export function effectT any(fn: () T,// 可以由用户自定义用于定义副作用effect的行为例如shouldTrack、onTrack、onTrigger等// 这样可以覆盖ReactiveEffect的默认scheduler而执行自己的scheduleroptions?: ReactiveEffectOptions,
): ReactiveEffectRunnerT {// 检查传入的函数是否已经是一个ReactiveEffect的副作用函数如果是则使用其内部的副作用函数if ((fn as ReactiveEffectRunner).effect instanceof ReactiveEffect) {fn (fn as ReactiveEffectRunner).effect.fn}// 调用一次effect函数根据传入的fn创建一个新的ReactiveEffect实例对象_effect// 一个fn 对应一个 _effect对象// fn 同时成为_effect 对象的fn属性形成闭包/*** effect(fn)* fn.effect ReactiveEffect对象* ReactiveEffect对象.fn fn*/// 创建一个新的ReactiveEffect实例/*** 当内部执行scheduler的时候它会回头调用effect的run,·而run方法内部会调用fn* 意味着scheduler()run()fn()* 如何执行那么之后我们如果想要重新执行fn函数·只需要执行scheduler就可以了*/const e new ReactiveEffect(fn)// 如果提供了用户自定义的配置则将配置合并到副作用实例中if (options) {extend(e, options)}// 尝试运行副作用函数如果在执行过程中抛出错误则停止追踪并重新抛出错误try {e.run()} catch (err) {e.stop()throw err}// 创建并返回一个副作用函数的runner它绑定了当前的副作用实例const runner e.run.bind(e) as ReactiveEffectRunnerrunner.effect e// 返回副作用函数的runner允许手动触发副作用 e.run() fn()return runner
}/*** 执行当前的副作用函数** 此函数负责执行副作用操作并在执行前后处理一些清理和准备依赖项的工作* 它还管理副作用的执行状态确保在执行过程中不会被中断并在完成后清理依赖项** returns 返回副作用函数的执行结果*/
run(): T {// TODO cleanupEffect// 检查副作用是否处于非激活状态如果是则直接执行副作用函数if (!(this.flags EffectFlags.ACTIVE)) {// stopped during cleanupreturn this.fn()}// 设置当前副作用为正在运行状态this.flags | EffectFlags.RUNNING// 执行清理上一次执行的副作用cleanupEffect(this)// 准备依赖项收集prepareDeps(this)// 保存当前正在执行的副作用和是否应该跟踪的全局状态const prevEffect activeSubconst prevShouldTrack shouldTrack// 设置当前执行的副作用和全局跟踪状态activeSub thisshouldTrack truetry {// 执行副作用函数return this.fn()} finally {// 在开发环境下确保当前执行的副作用被正确恢复if (__DEV__ activeSub ! this) {warn(Active effect was not restored correctly - this is likely a Vue internal bug.,)}// 清理当前执行的副作用的依赖项cleanupDeps(this)// 恢复之前保存的正在执行的副作用和是否应该跟踪的全局状态activeSub prevEffectshouldTrack prevShouldTrack// 清除当前副作用的正在运行状态this.flags ~EffectFlags.RUNNING}
}执行fn后响应式变量依赖的收集和触发
Vue3.4 版本之前最后的一层也就是 nameDepMap 这一层是 Set 结构但是从 3.4 版本开始变成了 Map 用来记录 track 的 id然而Vue3.5对该部分又进行了重构不再记录id只能说学的速度永远跟不上技术更新的速度~ packages/reactivity/src/baseHandlers.ts
/*** BaseReactiveHandler 类实现了 ProxyHandler 接口用于处理响应式对象的代理操作。* 它主要负责拦截和处理对响应式对象的访问和操作根据是否只读和是否浅层次来决定如何处理这些操作。** param _isReadonly 是否只读如果为 true则该响应式对象不可被修改。* param _isShallow 是否浅层次如果为 true则仅对外层属性进行响应式处理不递归处理内部属性。*/
class BaseReactiveHandler implements ProxyHandlerTarget {constructor(protected readonly _isReadonly false,protected readonly _isShallow false,) {}/*** 拦截对目标对象属性的获取操作。* 此方法负责处理各种情况下的属性获取包括处理特殊的 ReactiveFlags、数组的特殊情况、* 属性的追踪、以及返回值的处理等。** param target 目标对象即被代理的响应式对象。* param key 要访问的属性名。* param receiver 代理对象或继承链上的其他对象。* returns 属性值或处理后的值。*/get(target: Target, key: string | symbol, receiver: object): any {// 处理特殊的 ReactiveFlags 属性if (key ReactiveFlags.SKIP) return target[ReactiveFlags.SKIP]const isReadonly this._isReadonly,isShallow this._isShallow// 根据不同的 ReactiveFlags 返回相应的值if (key ReactiveFlags.IS_REACTIVE) {return !isReadonly} else if (key ReactiveFlags.IS_READONLY) {return isReadonly} else if (key ReactiveFlags.IS_SHALLOW) {return isShallow} else if (key ReactiveFlags.RAW) {if (receiver (isReadonly? isShallow? shallowReadonlyMap: readonlyMap: isShallow? shallowReactiveMap: reactiveMap).get(target) ||// receiver is not the reactive proxy, but has the same prototype// this means the receiver is a user proxy of the reactive proxyObject.getPrototypeOf(target) Object.getPrototypeOf(receiver)) {return target}// early return undefinedreturn}const targetIsArray isArray(target)// 处理非只读情况下的特殊属性和方法if (!isReadonly) {let fn: Function | undefinedif (targetIsArray (fn arrayInstrumentations[key])) {return fn}if (key hasOwnProperty) {return hasOwnProperty}}// 获取属性值考虑内置符号和非跟踪键的情况const res Reflect.get(target,key,// if this is a proxy wrapping a ref, return methods using the raw ref// as receiver so that we dont have to call toRaw on the ref in all// its class methodsisRef(target) ? target : receiver,)if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {return res}// 在非只读情况下跟踪属性的访问if (!isReadonly) {// 依赖追踪track(target, TrackOpTypes.GET, key)}// 根据是否浅层次和返回值的类型决定是否需要进一步处理返回值if (isShallow) {return res}if (isRef(res)) {// ref unwrapping - skip unwrap for Array integer key.return targetIsArray isIntegerKey(key) ? res : res.value}if (isObject(res)) {// Convert returned value into a proxy as well. we do the isObject check// here to avoid invalid value warning. Also need to lazy access readonly// and reactive here to avoid circular dependency.return isReadonly ? readonly(res) : reactive(res)}return res}
}class MutableReactiveHandler extends BaseReactiveHandler {constructor(isShallow false) {super(false, isShallow)}/*** 设置一个响应式对象的属性。** 该方法重写了 Proxy 对象的默认 set 行为实现了自定义的属性设置逻辑。* 它处理了各种情况例如浅层响应式、只读属性和 ref 解包以确保响应式系统的正确行为。** param target - 被代理的原始对象。* param key - 要设置的属性的名称或符号。* param value - 属性的新值。* param receiver - 代理对象本身。* returns 返回设置属性的结果。*/set(target: Recordstring | symbol, unknown,key: string | symbol,value: unknown,receiver: object,): boolean {let oldValue target[key]// 如果不是浅层响应式模式需要进一步处理属性和值。if (!this._isShallow) {const isOldValueReadonly isReadonly(oldValue)// 如果新旧值都不是浅层或只读的将它们转换为原始状态。if (!isShallow(value) !isReadonly(value)) {oldValue toRaw(oldValue)value toRaw(value)}// 如果目标不是数组且旧值是 ref 而新值不是 ref需要特殊处理。if (!isArray(target) isRef(oldValue) !isRef(value)) {// 如果旧值是只读的属性不能被设置。if (isOldValueReadonly) {return false} else {// 否则直接设置旧值 ref 的值。oldValue.value valuereturn true}}} else {// 在浅层模式下对象按原样设置无论是否是响应式的。}// 确定键是否已经存在。const hadKey isArray(target) isIntegerKey(key)? Number(key) target.length: hasOwn(target, key)// 使用 Reflect.set 尝试设置属性。const result Reflect.set(target,key,value,isRef(target) ? target : receiver,)// 如果目标是原始对象本身根据属性是新增还是更新触发相应的副作用。if (target toRaw(receiver)) {if (!hadKey) {// 触发新增属性的副作用。trigger(target, TriggerOpTypes.ADD, key, value)} else if (hasChanged(value, oldValue)) {// 触发更新属性的副作用。trigger(target, TriggerOpTypes.SET, key, value, oldValue)}}return result
}deleteProperty(target: Recordstring | symbol, unknown,key: string | symbol,): boolean {const hadKey hasOwn(target, key)const oldValue target[key]const result Reflect.deleteProperty(target, key)if (result hadKey) {trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)}return result}has(target: Recordstring | symbol, unknown, key: string | symbol): boolean {const result Reflect.has(target, key)if (!isSymbol(key) || !builtInSymbols.has(key)) {track(target, TrackOpTypes.HAS, key)}return result}ownKeys(target: Recordstring | symbol, unknown): (string | symbol)[] {track(target,TrackOpTypes.ITERATE,isArray(target) ? length : ITERATE_KEY,)return Reflect.ownKeys(target)}
}packages/reactivity/src/dep.ts
/*** 跟踪对响应式属性的访问。** 该函数会检查当前正在运行的效果effect并将其记录为依赖项dep* 这些依赖项记录了所有依赖于该响应式属性的效果。** param target - 包含响应式属性的对象。* param type - 定义对响应式属性的访问类型。* param key - 要跟踪的响应式属性的标识符。*/
export function track(target: object, type: TrackOpTypes, key: unknown): void {// 仅在启用了跟踪且存在活动效果时进行跟踪。activeSub在3.4版本及之前为activeEffectif (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 depsMapdep.key key}// 在开发模式下记录依赖项并包含详细信息。if (__DEV__) {dep.track({target,type,key,})} else {dep.track()}}
}/*** 查找与目标对象或特定属性关联的所有依赖项并触发存储在其中的效果。** param target - 响应式对象。* param type - 定义需要触发效果的操作类型。* param key - 可用于指定目标对象中的特定响应式属性。* param newValue - 新值用于某些操作类型如 SET。* param oldValue - 旧值用于某些操作类型如 SET。* param oldTarget - 旧的目标对象用于某些操作类型如 CLEAR。*/
export function trigger(target: object,type: TriggerOpTypes,key?: unknown,newValue?: unknown,oldValue?: unknown,oldTarget?: Mapunknown, unknown | Setunknown,
): void {const depsMap targetMap.get(target)if (!depsMap) {// 从未被追踪过globalVersionreturn}const run (dep: Dep | undefined) {if (dep) {if (__DEV__) {dep.trigger({target,type,key,newValue,oldValue,oldTarget,})} else {dep.trigger()}}}startBatch()if (type TriggerOpTypes.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, key) {if (key length ||key ARRAY_ITERATE_KEY ||(!isSymbol(key) key newLength)) {run(dep)}})} else {// 调度 SET | ADD | DELETE 的运行if (key ! void 0 || depsMap.has(void 0)) {run(depsMap.get(key))}// 调度 ARRAY_ITERATE 对于任何数字键的变化长度已在上方处理if (isArrayIndex) {run(depsMap.get(ARRAY_ITERATE_KEY))}// 也对 ADD | DELETE | Map.SET 运行迭代键switch (type) {case TriggerOpTypes.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))}breakcase TriggerOpTypes.DELETE:if (!targetIsArray) {run(depsMap.get(ITERATE_KEY))if (isMap(target)) {run(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {run(depsMap.get(ITERATE_KEY))}break}}}endBatch()
}
ITERATE_KEY))}} else if (isArrayIndex) {// 新索引添加到数组 - 长度变化run(depsMap.get(length))}breakcase TriggerOpTypes.DELETE:if (!targetIsArray) {run(depsMap.get(ITERATE_KEY))if (isMap(target)) {run(depsMap.get(MAP_KEY_ITERATE_KEY))}}breakcase TriggerOpTypes.SET:if (isMap(target)) {run(depsMap.get(ITERATE_KEY))}break}}}endBatch()
}