阅读源码带来的收获。
- (1)ES6 的
Proxy、Reflect
组合语义化语法; - (2)
TypeScript
的深度用法; - (3)
Vue3
响应式原理; - (4)依赖收集和触发过程;
- (5)Vue3 的
reactivity
模块的常用 API 实现。
阅读前所需要掌握的知识点。
-
了解
TypeScript
的使用 Typescript tslang -
对于Vue2.x时候的
Object.defineProperty
响应式处理来说,Proxy
具备了更加强大的能力,新增、删除、迭代
操作也能够监听到,而无需多余API进行新增、删除。 -
而使用了
TypeScript
重构了整个项目,配合monorepo
实现了模块的划分,其中响应式相关的原理都存放到了reactivity
模块中。
1. reactivity 入口
reactivity 入口部分可以说只是做出了相关的导出操作
export { ref, toRefs /* ... */ } from "./ref";
export { reactive, readonly /* ... */ } from "./reactive";
export { computed /* ... */ } from "./computed";
export { effect, stop, trigger, track /* ... */ } from "./effect";
-
核心部分在上面导出的几个 API。
-
我们可以通过 reactive 去开始解读源码,从中展开到个个 API 实现。
1.1 reactive.ts 解读
- (1)首先使用了
TypeScript
的函数重载,对类型进行的声明; - (2)然后通过判断有没有
ReactiveFlags.IS_READONLY
来调用function createReactiveObject
创建 reactive 对象; - (3)通过判断是否可以被代理、是否有缓存、是否可以被代理进行处理;
- (4)是否被代理的核心是调用
Object.prototype.toString.call
验证类型; - (5)最后进行代理,缓存后返回被代理的对象。
import { mutableHandlers /* ... */ } from "./baseHandlers";
import { mutableCollectionHandlers /* ... */ } from "./collectionHandlers";
export const reactiveMap = new WeakMap<Target, any>();
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T>;
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target;
}
return createReactiveObject(
target, // 响应式的目标对象
false, // 是否只读
mutableHandlers, // reactive基本对象的代理处理
mutableCollectionHandlers, // Map、Set等对象的代理
reactiveMap // 缓存Map
);
}
import { isObject, toRawType /* ... */ } from "@vue/shared";
// 拿到对象(WeakMap、Map、WeakSet、Set、Object、Array)的Type
function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value));
}
function createReactiveObject(
target: Target, // 创建响应式的目标对象(可以是数组、对象、Map、Set...)
isReadonly: boolean, // 是否是只读模式
baseHandlers: ProxyHandler<any>, // 基本的Proxy代理函数
collectionHandlers: ProxyHandler<any>, // Map、Set等的Proxy代理函数
proxyMap: WeakMap<Target, any> // 代理的proxy缓存Map
) {
// 非object情况直接返回
if (!isObject(target)) {
return target;
}
// 目标对象上存在 ReactiveFlags.RAW (调用过reactive/readonly函数)
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target;
}
// 目标对象被代理过存在缓存中
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// 获取类型,判断是否可以被代理
const targetType = getTargetType(target);
if (targetType === TargetType.INVALID) {
return target;
}
// 通过拿到的类型验证是否是COLLECTION(Map、Set...)还是基本的类型(Object、Array)
const proxy = new Proxy(
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
);
// 设置缓存并导出代理后的对象
proxyMap.set(target, proxy);
return proxy;
}
-
reactive.ts
其他readonly、shallowReactive、shallowReadonly
也是类型的调用方式,在createReactiveObject
的时候传递对应的处理对象、缓存 Map。 -
而
isReadonly、isReactive
是巧妙的验证对象上面添加的ReactiveFlags
里面枚举的类型。
// ReactiveFlags 是一个 TypeScript 的枚举类型
export const enum ReactiveFlags {
SKIP = "__v_skip",
IS_REACTIVE = "__v_isReactive",
IS_READONLY = "__v_isReadonly",
RAW = "__v_raw",
}
export function isReactive(value: unknown): boolean {
if (isReadonly(value)) {
return isReactive((value as Target)[ReactiveFlags.RAW]);
}
return !!(value && (value as Target)[ReactiveFlags.IS_REACTIVE]);
}
export function isReadonly(value: unknown): boolean {
return !!(value && (value as Target)[ReactiveFlags.IS_READONLY]);
}
-
reactive() 小总结:
- 在
reactive.ts
中主要是进行了验证、缓存的操作,处理了重复代理的问题、空间换时间
的方式进行优化。 - Proxy 的代理核心在
baseHandlers.ts
和collectionHandlers.ts
导出引用; - 调用了
@vue/shared
模块导出的isObject、toRawType
等共享方法,这个模块的常用utils可以运用到我们实际项目开发
当中;
- 在
-
接下来就是我们的 Proxy 代理核心
baseHandlers.ts
。
1.2 baseHandlers.ts 解读
reactive
调用createReactiveObject
的时候传递了mutableHandlers
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
deleteProperty,
has,
ownKeys,
};
- 代理了以上五个操作、对增删改查、验证、枚举操作进行处理
1.2.1 get 函数
- (1)
createGetter
通过判断,对自身定义的属性进行处理返回、对数组原型
函数push、pop
等进行处理; - (2)处理以上操作之后,通过 key 调用
Reflect.get
拿到结果; - (3)
ref
情况进行自动解构,直接返回数据源; - (4)后面还验证是否需要进行依赖收集、是否要对返回的对象数据进行深层代理。
import {
isObject,
hasOwn,
isSymbol,
/* ... */
isArray,
isIntegerKey,
/* ... */
} from "@vue/shared";
// 执行高阶函数 createGetter 拿到对应的get函数
const get = /*#__PURE__*/ createGetter();
function createGetter(isReadonly = false, shallow = false) {
// target(被代理的对象)、key、receiver(代理后的对象)
return function get(target: Target, key: string | symbol, receiver: object) {
// key 为 vue内部注入的ReactiveFlags,返回相关的true/false
if (key === ReactiveFlags.IS_REACTIVE) {
return !isReadonly;
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly;
} else if (
// 响应式情况, 通过ReactiveFlags.RAW获取数据,直接返回目标对象
// 这个操作在toRaw函数、isReactive都有体现
key === ReactiveFlags.RAW &&
receiver === // 并且代理对象是否被缓存过
(isReadonly
? shallow
? shallowReadonlyMap
: readonlyMap
: shallow
? shallowReactiveMap
: reactiveMap
).get(target)
) {
return target;
}
const targetIsArray = isArray(target);
// 非只读 & 数组 & 属于重写的['push', 'pop', 'shift', 'unshift', 'splice'] | ['includes', 'indexOf', 'lastIndexOf']
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
// 直接拿相关的函数
return Reflect.get(arrayInstrumentations, key, receiver);
}
// 获取数据
const res = Reflect.get(target, key, receiver);
// 验证是否存在相关不需要 Track 依赖收集
if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) {
return res;
}
if (!isReadonly) {
// 非只读状态下收集依赖
track(target, TrackOpTypes.GET, key);
}
// 浅代理情况直接返回
if (shallow) {
return res;
}
// 对属于ref情况,判断是否存在 __v_isRef & 非数组 或非数值key,直接解构拿到.value
if (isRef(res)) {
// ref unwrapping - does not apply for Array + integer key.
const shouldUnwrap = !targetIsArray || !isIntegerKey(key);
return shouldUnwrap ? res.value : res;
}
// 如果获取的数据是对象,进行深层次只读/代理
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;
};
}
1.2.2 set 函数
- (1)
createSetter
对oldValue是ref、newValue是非ref
情况进行自动赋值处理,简化了 reactive 里面嵌套 ref 情况的赋值操作(前面在获取的时候进行了解构); - (2)设置 newValue 之前,进行判断
key
是否存在,进行对应的触发TriggerOpTypes
进行标明,在没有变化情况下不做处理。
// 一样通过createSetter创建set函数
const set = /*#__PURE__*/ createSetter();
function createSetter(shallow = false) {
// target(被代理的对象)、key、value、receiver(代理后的对象)
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
// 拿到修改/添加之前的值
let oldValue = (target as any)[key];
if (!shallow) {
// 拿到源值(toRaw这样处理是为了防止获取的值是一个Proxy,进行解构拿到被代理前的对象)
value = toRaw(value);
oldValue = toRaw(oldValue);
// 属于老值是ref、新值非ref,直接赋值(get情况进行解构,set的时候进行自动赋值到ref的value上)
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value;
return true;
}
} else {
// in shallow mode, objects are set as-is regardless of reactive or not
}
const hadKey =
isArray(target) && isIntegerKey(key)
? Number(key) < target.length
: hasOwn(target, key);
// 进行设置新的value
const result = Reflect.set(target, key, value, receiver);
// don't trigger if target is something up in the prototype chain of original
if (target === toRaw(receiver)) {
// 设置之前没有这个key,就是添加操作,否则是修改操作(触发对应的trigger,就是触发收集的依赖)
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value);
} else if (hasChanged(value, oldValue)) {
// 判断有无变化(相同情况不触发依赖)
trigger(target, TriggerOpTypes.SET, key, value, oldValue);
}
}
return result;
};
}
1.2.3 deleteProperty 函数 和 has、ownKey函数
function deleteProperty(target: object, key: string | symbol): boolean {
const hadKey = hasOwn(target, key);
const oldValue = (target as any)[key];
const result = Reflect.deleteProperty(target, key);
if (result && hadKey) {
trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue);
}
return result;
}
-
deleteProperty函数
就做了删除对应的 value、然后判断是否存在来触发依赖。 -
has、ownKeys
函数调用较为简单、直接进行依赖收集,等到 set 情况进行触发依赖。 -
collectionHandlers.ts
里面的处理比较类似,对相关的操作做出类似的处理,调用原型上面的function(get、set、has)进行原生处理
。
1.3 effect.ts 解读
effect.ts
里面有 track(依赖收集)函数、trigger(依赖触发函数)和 effect 副作用函数。- 其中核心的是
track和trigger
,而effect
作为为外部提供依赖监听触发的函数,可以运用在 vue 的更新流程中和日常使用。
1.3.1 effect function 实现
- (1)处理多层
effect
包裹 callback; - (2)创建
reactiveEffect
,判断options.lazy
是否默认执行一次 effect。
// 定义了effect函数的类型
export interface ReactiveEffect<T = any> {
(): T; // 一个函数的申明
_isEffect: true;
id: number; // 对应自身的id
active: boolean; // 是否被激活了
raw: () => T; // 被调用的目标对象源(effect(fn)调用的fn)
deps: Array<Dep>; // 依赖项(也是一个effect Set集)
options: ReactiveEffectOptions; // 调用effect的第二个参数选项
allowRecurse: boolean;
}
// 存储当前调用的effect函数,用于track进行收集
const effectStack: ReactiveEffect[] = [];
// 如其名,激活中的effect函数
let activeEffect: ReactiveEffect | undefined;
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
// 传递进来的fn已经是effect过的function,则直接拿这个fn的raw(也就是上次被fn的回调函数)
if (isEffect(fn)) {
fn = fn.raw;
}
// 调用创建ReactiveEffect
const effect = createReactiveEffect(fn, options);
// 默认调用一次
if (!options.lazy) {
effect();
}
// 返回被创建的ReactiveEffect
return effect;
}
- (3)effect 处理的只有创建一个 effect 对象,除了执行,也可以用来暂停处理;
- (4)同时设置下当前被
激活的 effect、用于 track 进行收集
,相对简单明了; - (5)stop 的作用就是
清空依赖、修改active为false
。
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
// 非激活状态,直接执行fn这个callback,不做后续操作
// 这种情况下在stop函数被调用时候存在,这时候进行get、has操作则不会收集到对应的依赖
if (!effect.active) {
return fn()
}
// 不存在情况,需要保存effect到栈里面缓存
if (!effectStack.includes(effect)) {
// 清除正在运行的effect依赖
cleanup(effect)
try {
// 禁止收集依赖,收集上次的track state
enableTracking()
effectStack.push(effect)
// 设置当前激活
activeEffect = effect
return fn() // 调用fn回调函数,默认收集一次依赖
} finally {
effectStack.pop() // 只想完毕推出栈
// 复原到上次的track state 和 active effect
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
// 配置一些变量到effect fn上做下次处理
effect.id = uid++
effect.allowRecurse = !!options.allowRecurse
effect._isEffect = true
effect.active = true
effect.raw = fn
effect.deps = []
effect.options = options
return effect
}
1.3.2 track function 依赖收集实现
-
(1)
WeakMap
和Map
的区别在于,key 必须是对象,而且还是对象的浅引用
(删除原对象 key 不受影响); -
(2)在 Vue3 中、是直接使用 WeakMap 的 key 为被代理前的对象;
- WeakMap 的 value 则是一个 key 的 Map。
- key 的 Map 内部又存储了 Set 集合的 ReactiveEffect 函数。
-
(3)
track
通过判断全局变量shouldTrack 和 activeEffect
,进行依赖的 Map 和 Set 的验证,最终存储activeEffect 到 depsMap中对应key: value(Set),然后把 value(Set) 放入 activeEffect.deps 里面
。
/*
主要存储形式是 WeakMap<target, new Map<key, new Set<ReactiveEffect>>>
*/
type Dep = Set<ReactiveEffect>;
type KeyToDepMap = Map<any, Dep>;
const targetMap = new WeakMap<any, KeyToDepMap>();
export function track(target: object, type: TrackOpTypes, key: unknown) {
// 当前状态是禁止收集依赖 | 被激活的activeEffect没有(就是没有调用过effect、ref等API情况)
if (!shouldTrack || activeEffect === undefined) {
return;
}
// 被代理的key map,也就是依赖收集的map
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
// depsMap中拿到Set集,没有也重新设置
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
// 当前key不存在这个依赖函数,则添加,并且在依赖函数上面添加这个dep集合,用于后续的清理操作
if (!dep.has(activeEffect)) {
dep.add(activeEffect);
activeEffect.deps.push(dep);
// 开发模式下调用effect(fn, option的onTrack函数,用于测试和调试)
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack(/* ... */);
}
}
}
1.3.3 trigger function 依赖触发实现
- (1)通过判断什么类型,拿到对应的 effect,放入 Set 集合中,处理一些特殊的 effect;
- (2)最后
run
函数执行,判断是否有scheduler函数
来执行scheduler
还是effect
自身。
export function trigger(
target: object,
type: TriggerOpTypes,
key?: unknown,
newValue?: unknown,
oldValue?: unknown,
oldTarget?: Map<unknown, unknown> | Set<unknown>
) {
const depsMap = targetMap.get(target)
if (!depsMap) {
// never been tracked
return
}
// 创建一个依赖函数集,用于收集相关的依赖,等到run时执行触发
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
// 收集非当前已经激活的effect
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
// 对各自type、Array进行处理添加到effects集合中。
if (type === TriggerOpTypes.CLEAR) {
// collection being cleared
// trigger all effects for target
depsMap.forEach(add)
} else if (key === 'length' && isArray(target)) {
depsMap.forEach((dep, key) => {
if (key === 'length' || key >= (newValue as number)) {
add(dep)
}
})
} else {
// schedule runs for SET | ADD | DELETE
if (key !== void 0) {
add(depsMap.get(key))
}
// 迭代操作的key操作依赖
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
// 对象在ownKeys情况设置的依赖、add、delete、set情况都有
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) {
// 新增索引到数组之后触发的length改变依赖
// new index added to array -> length changes
add(depsMap.get('length'))
}
break
case TriggerOpTypes.DELETE:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) {
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
}
break
case TriggerOpTypes.SET:
if (isMap(target)) {
add(depsMap.get(ITERATE_KEY))
}
break
}
}
const run = (effect: ReactiveEffect) => {
if (__DEV__ && effect.options.onTrigger) {
effect.options.onTrigger(/* ... */)
}
// 最终运行依赖,当依赖存在scheduler函数,则执行提供的scheduler函数,否则执行effect函数
if (effect.options.scheduler) {
effect.options.scheduler(effect)
} else {
effect()
}
}
// 运行需要执行的依赖项
effects.forEach(run)
}
1.4 ref.ts 解读
1.4.1 ref function 实现
- (1)首先定义 TypeScript 函数重载,调用
createRef
; - (2)在
createRef
中验证 rawValue 是不是 ref 来进行创建 ref 实例并返回;
export function ref<T extends object>(value: T): ToRef<T>;
export function ref<T>(value: T): Ref<UnwrapRef<T>>;
export function ref<T = any>(): Ref<T | undefined>;
export function ref(value?: unknown) {
return createRef(value);
}
function createRef(rawValue: unknown, shallow = false) {
// ref情况直接返回
if (isRef(rawValue)) {
return rawValue;
}
// 创建一个Ref对象实例
return new RefImpl(rawValue, shallow);
}
- (3)先验证是否是 shallow,非 shallow 则进行
调用convert深度代理(对象情况)
; - (4)然后在
.value
获取的时候收集依赖,赋值时调用convert
并触发依赖
。
const convert = <T extends unknown>(val: T): T =>
isObject(val) ? reactive(val) : val;
class RefImpl<T> {
private _value: T; // 存储我们定义的rawValue
public readonly __v_isRef = true; // 标识是一个ref
constructor(private _rawValue: T, public readonly _shallow: boolean) {
// 判断是不是浅层渲染还是深层次渲染(深层次渲染进行验证、对象情况通过reactive代理)
this._value = _shallow ? _rawValue : convert(_rawValue);
}
// .value时候,触发track操作收集依赖,key为value
get value() {
track(toRaw(this), TrackOpTypes.GET, "value");
return this._value;
}
// .set时候验证是否值有变化,并且再次验证是否需要继续代理
// 最后执行依赖触发操作
set value(newVal) {
if (hasChanged(toRaw(newVal), this._rawValue)) {
this._rawValue = newVal;
this._value = this._shallow ? newVal : convert(newVal);
trigger(toRaw(this), TriggerOpTypes.SET, "value", newVal);
}
}
}
1.4.2 toRefs function 实现
- (1)
toRefs
方法,把代理过后的对象/数组每一项都进行 toRef 并返回; - (2)
toRef
方法则判断 isRef()来进行 objectRef 转换,旨意在被解构之后还能保留响应式
状态。
export function toRefs<T extends object>(object: T): ToRefs<T> {
if (__DEV__ && !isProxy(object)) {
console.warn(
`toRefs() expects a reactive object but received a plain one.`
);
}
const ret: any = isArray(object) ? new Array(object.length) : {};
// 处理生成新的数值(一个ObjectRef实例)
// `for in` 这种操作触发了Proxy的`ownKeys`迭代操作
for (const key in object) {
ret[key] = toRef(object, key);
}
return ret;
}
class ObjectRefImpl<T extends object, K extends keyof T> {
public readonly __v_isRef = true
// 保存这个object和对应的key到.value操作上,并且设置成ref情况
constructor(private readonly _object: T, private readonly _key: K) {}
get value() {
return this._object[this._key]
}
set value(newVal) {
this._object[this._key] = newVal
}
}
export function toRef<T extends object, K extends keyof T>(
object: T,
key: K
): ToRef<T[K]> {
// ref情况直接返回,非ref情况则创建ObjectRefImpl
return isRef(object[key])
? object[key]
: (new ObjectRefImpl(object, key) as any);
}
1.5 computed.ts 解读
computed function 实现
-
(1)判断参数,先处理get、set函数、最后调用
new ComputedRefImpl
创建实例;- 当传递的是
getter
,默认给setter
一个空函数。 - 当传递的是
options
,则直接提供赋值给getter、setter
。
- 当传递的是
-
(2)在
ComputedRefImpl的constructor
时;- 创建一个effect、把
getter 当成 callback
传入。 - 传递
scheduler
函数,调用时通过trigger
依赖触发。
- 创建一个effect、把
-
(3)在get时候调用
this.effect
拿到新的值(前提是数据更新了),最终track收集computed
的依赖项; -
(4)则set情况直接调用setter。
export type ComputedGetter<T> = (ctx?: any) => T
export type ComputedSetter<T> = (v: T) => void
export interface WritableComputedOptions<T> {
get: ComputedGetter<T>
set: ComputedSetter<T>
}
// 进行函数重载,因为computed的参数可能是一个getter函数,也可以是一个包含get、set的对象
export function computed<T>(getter: ComputedGetter<T>): ComputedRef<T>
export function computed<T>(
options: WritableComputedOptions<T>
): WritableComputedRef<T>
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let setter: ComputedSetter<T>
// 如果是function则是一个get函数
if (isFunction(getterOrOptions)) {
getter = getterOrOptions
setter = __DEV__
? () => {
console.warn('Write operation failed: computed value is readonly')
}
: NOOP
} else {
getter = getterOrOptions.get
setter = getterOrOptions.set
}
// 创建ComputedRefImpl实例
// 通过判断是函数/没有options.set函数为只读模式
return new ComputedRefImpl(
getter,
setter,
isFunction(getterOrOptions) || !getterOrOptions.set
) as any
}
class ComputedRefImpl<T> {
private _value!: T
private _dirty = true
public readonly effect: ReactiveEffect<T>
public readonly __v_isRef = true;
public readonly [ReactiveFlags.IS_READONLY]: boolean
constructor(
getter: ComputedGetter<T>,
private readonly _setter: ComputedSetter<T>,
isReadonly: boolean
) {
// 进入创建一个异步执行的effect
this.effect = effect(getter, {
lazy: true, // 异步、不立即执行
scheduler: () => {
// 等到trigger时候,触发effect.options.scheduler
if (!this._dirty) {
// 设置_dirty,依赖被调用过了,这时候才能去获取新的value
this._dirty = true
trigger(toRaw(this), TriggerOpTypes.SET, 'value')
}
}
})
this[ReactiveFlags.IS_READONLY] = isReadonly
}
get value() {
// the computed ref may get wrapped by other proxies e.g. readonly() #3376
// 等到.value调起的时候拿值
const self = toRaw(this)
if (self._dirty) {
self._value = this.effect() // 调用effect拿到getter执行结果
self._dirty = false
}
// 收集依赖项
track(self, TrackOpTypes.GET, 'value')
return self._value
}
set value(newValue: T) {
// 赋值的时,调用setter
this._setter(newValue)
}
}
总体流程如图所示
总结:
- reactivity模块核心在于
reactive、track、trigger、effect
四个API、各类操作都离不开。 - 通过设置对应的 __v_xxx 变量来确定是否是一个
reactive、ref、readonly、raw
等,从而进行各种的处理。isRef、isReactive、isReadonly、toRaw...
都围绕了这些变量。- 通过这些变量中的
__v_raw
可以在Proxy代理的get函数里面直接返回对象源。
reactive函数
内部处理了ref的解构和赋值,简化使用者使用的难度- Vue3的响应式原理就在于对
Proxy的运用和依赖收集触发
操作。- 在其他地方通过effect+响应式原理进行更新操作。
- 具备了
组件级的effect
可能、当响应式数据发生变化,则调用到effect的callback
对内部vnode进行diff。
- effect原则上就是定义
activeEffect
提供给track进行收集,等到trigger时触发。 - 而ref的核心是一个class的实例,里面使用了计算属性
get value()、set value(newValue)
,调用了track、trigger
做到响应副作用
。 - computed函数也是返回一个class实例、在
constructor时创建一个effect
、设置_dirty来防止重复计算触发getter
,当依赖被触发之后才调用get获取新的value
。 - 平时项目中,我们可以
直接使用vue提供的share模块
,直接导出使用相关的工具方法。