本文从案例出发:源码实现(详细注释) + 自己手写,直接梭哈。
文章很干,可以分几天看(慢慢来,别急
Vue3响应式原理
一.ReactiveAPI原理分析
export function reactive(target: object) {
// if trying to observe a readonly proxy, return the readonly version.
// 如果这个对象已经被 readonly代理过了,则直接返回。 被readonly代理过就会添加proxy,取值时会走get方法
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target
}
return createReactiveObject( // 创建响应式对象
target,
false,
mutableHandlers,
mutableCollectionHandlers
)
}
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>
) {
if (!isObject(target)) { // reactive只接受对象
if (__DEV__) {
console.warn(`value cannot be made reactive: ${String(target)}`)
}
return target
}
// target is already a Proxy, return it. 目标已经是被代理过了
// exception: calling readonly() on a reactive object 如果被reactive 处理过的对象还可以继续被readonly处理
if (
target[ReactiveFlags.RAW] &&
!(isReadonly && target[ReactiveFlags.IS_REACTIVE])
) {
return target
}
// target already has corresponding Proxy 对象 和 代理 进行缓存
const proxyMap = isReadonly ? readonlyMap : reactiveMap
const existingProxy = proxyMap.get(target)
if (existingProxy) {
return existingProxy
}
// only a whitelist of value types can be observed.
const targetType = getTargetType(target) // 看对象是否是可扩展的
if (targetType === TargetType.INVALID) { // 不可扩展直接返回
return target
}
const proxy = new Proxy( // 核心 创建proxy, 对集合的处理和普通的对象略有不同
target,
targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers
)
proxyMap.set(target, proxy) // 缓存起来
return proxy
}
注:createReactiveObject实现分为几个步骤:
- 1.如果不是对象就直接返回。
- 2.如果目标对象已经被代理过,被
reactive代理过的对象,还可以被readonly进行包装 - 3.查看缓存中。看对象是否已经被代理过,如果代理过则直接返回
- 4.检测对象是否可扩展。
- 5.针对对象创建代理,并缓存代理对象
- 6.返回代理对象
调试方法:
暂且更改开发时打包的入口文件
scripts/dev.js
const target = args._.length ? fuzzyMatchTarget(args._)[0] : 'reactivity'
案例1:
const { reactive, readonly } = VueReactivity;
let obj = { name: 'vue' };
let proxy1 = readonly(obj);
debugger;
let proxy2 = reactive(proxy1);
console.log(proxy1 === proxy2);
被
readonly代理过的对象,不能在使用reactive代理
案例2:
let obj = { name: 'vue' };
debugger
let proxy1 = reactive(obj);
let proxy2 = reactive(proxy1);
let proxy3 = reactive(obj);
对象不会被重复代理,同时如果已经是代理对象也不会再次进行代理
案例3:
let obj = { name: 'vue' };
let proxy = reactive(obj);
debugger
console.log(obj === toRaw(proxy));
let obj2 = { name: 'vue' };
let proxy2 = reactive(markRaw(obj2));
console.log(proxy2);
通过代理对象可以获取被代理数据,必要时可以使用
markRaw标记对象,可以避免对象的代理操作
二.baseHandlers实现
export const mutableHandlers: ProxyHandler<object> = {
get, // 访问属性代理
set, // 设置对象属性代理
deleteProperty, // 删除属性代理
has, // 访问in操作触发
ownKeys // 调用Object.getOwnPropertyNames()方法时触发
}
1).get访问器
// 不同类型的get 根据shallow readonly 进行判断生成
const get = /*#__PURE__*/ createGetter()
const shallowGet = /*#__PURE__*/ createGetter(false, true)
const readonlyGet = /*#__PURE__*/ createGetter(true)
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true)
function createGetter(isReadonly = false, shallow = false) {
return function get(target: Target, key: string | symbol, receiver: object) {
if (key === ReactiveFlags.IS_REACTIVE) { // 用来判断这个对象是reactive还是readonly
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) { // 如果取的是IS_READONLY
return isReadonly
} else if (
key === ReactiveFlags.RAW && // 可以使用toRaw方法获取被代理过对象对应的原值
receiver === (isReadonly ? readonlyMap : reactiveMap).get(target)
) {
return target
}
const targetIsArray = isArray(target)
// 如果是数组 对 'includes', 'indexOf', 'lastIndexOf' 方法进行处理
if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) {
return Reflect.get(arrayInstrumentations, key, receiver)
}
const res = Reflect.get(target, key, receiver)
if ( // 是内置symbol或者是原型链 查找到的,直接返回
isSymbol(key)
? builtInSymbols.has(key as symbol)
: isNonTrackableKeys(key)
) {
return res
}
if (!isReadonly) {
track(target, TrackOpTypes.GET, key) // 依赖收集
}
if (shallow) {
return res
}
if (isRef(res)) { // 数组访问索引时 无需.value
// ref unwrapping - does not apply for Array + integer key.
const shouldUnwrap = !targetIsArray || !isIntegerKey(key)
return shouldUnwrap ? res.value : 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
}
}
案例1:
let proxyArr = reactive([1, 2, 3]);
// 访问数组的方法时会访问数组的长度
proxyArr.push(5); // 调用数组方法时 有暂停收集的功能和增加收集项的功能
proxyArr[Symbol.hasInstance]; // 访问内置属性不会依赖收集
let r = reactive({
name: ref('vue')
});
console.log(r.name); // reactive 会判断里面是否包含ref,自动拆包
let r1 = reactive([ref(1), 2, 3, 4]); // 这种情况下不会拆包
2).set访问器
function createSetter(shallow = false) {
return function set(
target: object,
key: string | symbol,
value: unknown,
receiver: object
): boolean {
const oldValue = (target as any)[key]
if (!shallow) {
value = toRaw(value) // 如果设置的值是reactive过的 会被转化为普通对象
if (!isArray(target) && isRef(oldValue) && !isRef(value)) {
oldValue.value = value // 老的是ref 新的不是ref 则会给老的ref赋值
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)
const result = Reflect.set(target, key, value, receiver)
// don't trigger if target is something up in the prototype chain of original
// 不触发原型链上的trigger
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
}
}
案例1:
let proxy1 = reactive({ name: 'vue', age: ref(11) });
proxy1.name = reactive({ str: 'v' });
proxy1.age = ref(12);
console.log(proxy1);
如果设置的值是reactive或者ref的情况
案例2:
let obj = {};
let proto = {a:1}
let proxyProto = new Proxy(proto, {
get(target,key,receiver) {
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver){
console.log(proxyProto , receiver == myProxy)
return Reflect.set(target,key,value,receiver)
}
})
Object.setPrototypeOf(obj,proxyProto); //原型链
let myProxy = new Proxy(obj,{
get(target,key,receiver) {
return Reflect.get(target,key,receiver)
},
set(target,key,value,receiver){
console.log(receiver === myProxy)
return Reflect.set(target,key,value,receiver)
}
})
myProxy.a = 100
原型链也是一个proxy,那么通过
Reflect.set方法设置值,会触发原型的set方法
你也尝试分析下
baseHandlers中其他的访问器
三.collectionHandlers实现
// 集合中的方法 map.get map.set, 最终都会调用get方法
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(false, false)
}
export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(false, true)
}
export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = {
get: createInstrumentationGetter(true, false)
}
function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) {
const instrumentations = shallow // 返回不同的getter
? shallowInstrumentations
: isReadonly
? readonlyInstrumentations
: mutableInstrumentations
return (
target: CollectionTypes,
key: string | symbol,
receiver: CollectionTypes
) => {
if (key === ReactiveFlags.IS_REACTIVE) { // 处理 isReactive isReadonly toRaw
return !isReadonly
} else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly
} else if (key === ReactiveFlags.RAW) {
return target
}
return Reflect.get( // 对map 、 set的方法进行处理
hasOwn(instrumentations, key) && key in target
? instrumentations
: target,
key,
receiver
)
}
}
const mutableInstrumentations: Record<string, Function> = {
get(this: MapTypes, key: unknown) {
return get(this, key)
},
get size() {
return size((this as unknown) as IterableCollections)
},
has,
add,
set,
delete: deleteEntry,
clear,
forEach: createForEach(false, false)
}
set、map默认的方法进行代理
1).get访问器
function get( // 取值操作
target: MapTypes,
key: unknown,
isReadonly = false,
isShallow = false
) {
// #1772: readonly(reactive(Map)) should return readonly + reactive version
// of the value
target = (target as any)[ReactiveFlags.RAW]
const rawTarget = toRaw(target)
const rawKey = toRaw(key)
if (key !== rawKey) { // key可能是对象也是被proxy过的
!isReadonly && track(rawTarget, TrackOpTypes.GET, key)
} // 对rawKey进行依赖收集
!isReadonly && track(rawTarget, TrackOpTypes.GET, rawKey)
const { has } = getProto(rawTarget) // 获取has方法
const wrap = isReadonly ? toReadonly : isShallow ? toShallow : toReactive
if (has.call(rawTarget, key)) { // 是否包含key
return wrap(target.get(key)) // 如果是取到的值是对象接着代理
} else if (has.call(rawTarget, rawKey)) {
return wrap(target.get(rawKey))
}
}
2).add访问器
function add(this: SetTypes, value: unknown) {
value = toRaw(value) // 添加的值转为普通值
const target = toRaw(this)
const proto = getProto(target) // 获取目标原型
const hadKey = proto.has.call(target, value) // 是否有此value
target.add(value) // 添加值
if (!hadKey) { // 没有则触发add
trigger(target, TriggerOpTypes.ADD, value, value)
}
return this
}
针对Set 处理,如果新增的才需要触发更新,已经有的就无所谓了。
3).set访问器
function set(this: MapTypes, key: unknown, value: unknown) {
value = toRaw(value)
const target = toRaw(this)
const { has, get } = getProto(target)
let hadKey = has.call(target, key) // 是否有key
if (!hadKey) {
key = toRaw(key)
hadKey = has.call(target, key)
} else if (__DEV__) {
checkIdentityKeys(target, has, key)
}
const oldValue = get.call(target, key)
target.set(key, value) // 添加数据
if (!hadKey) {
trigger(target, TriggerOpTypes.ADD, key, value) // 触发更新 -》 添加逻辑
} else if (hasChanged(value, oldValue)) {
trigger(target, TriggerOpTypes.SET, key, value, oldValue) // 触发修改
}
return this
}
针对map的操作
四.effect函数剖析
1) effect函数实现
const effectStack: ReactiveEffect[] = [] // 创建effect栈 包装effect执行顺序正确
let activeEffect: ReactiveEffect | undefined // 当前正在执行的effect
export function effect<T = any>(
fn: () => T,
options: ReactiveEffectOptions = EMPTY_OBJ
): ReactiveEffect<T> {
if (isEffect(fn)) { // 是否已经是effect,是effect获取对应的原函数
fn = fn.raw
}
const effect = createReactiveEffect(fn, options) // 创建响应式effect
if (!options.lazy) { // 是不是立即执行
effect()
}
return effect
}
let uid = 0
function createReactiveEffect<T = any>(
fn: () => T,
options: ReactiveEffectOptions
): ReactiveEffect<T> {
const effect = function reactiveEffect(): unknown {
if (!effect.active) { // 如果不是active的,默认为true
return options.scheduler ? undefined : fn()
}
if (!effectStack.includes(effect)) {
cleanup(effect) // 每次重新收集依赖
try {
enableTracking() // shouldTrack = true
effectStack.push(effect)
activeEffect = effect
return fn()
} finally {
effectStack.pop()
resetTracking()
activeEffect = effectStack[effectStack.length - 1]
}
}
} as ReactiveEffect
effect.id = uid++ // effect 唯一标识
effect.allowRecurse = !!options.allowRecurse // 运行effect重复执行
effect._isEffect = true // effect是不是effect
effect.active = true // 是否激活
effect.raw = fn // 对应的原函数
effect.deps = [] // effect 对应的属性
effect.options = options
return effect
}
案例1:
// 1.已经是effect函数,再被effect
let reactiveEffect = effect(()=>{
console.log(1);
});
debugger;
let reactiveEffect2 = effect(reactiveEffect);
console.log(reactiveEffect === reactiveEffect2); // false
案例2:
const state = reactive({name:'vue',age:11})
effect(()=>{
console.log('rerender')
if(state.name === 'vue'){
console.log(state.age);
}
})
state.age = 100;
state.name = 'vv';
state.age = 200;
每次属性需要重新收集对应的
effect
五.依赖收集
export function track(target: object, type: TrackOpTypes, key: unknown) {
if (!shouldTrack || activeEffect === undefined) { // 是否要收集依赖
return
}
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 Set()))
}
if (!dep.has(activeEffect)) { // 维护依赖收集结构
dep.add(activeEffect)
activeEffect.deps.push(dep) // 将dep 推入到deps中
if (__DEV__ && activeEffect.options.onTrack) {
activeEffect.options.onTrack({
effect: activeEffect,
target,
type,
key
})
}
}
}
六.触发更新
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
}
const effects = new Set<ReactiveEffect>()
const add = (effectsToAdd: Set<ReactiveEffect> | undefined) => {
if (effectsToAdd) { // 用于去重
effectsToAdd.forEach(effect => {
if (effect !== activeEffect || effect.allowRecurse) {
effects.add(effect)
}
})
}
}
if (type === TriggerOpTypes.CLEAR) { // 是清空,则调用所有effect
// 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) { // 针对key来进行操作
add(depsMap.get(key))
}
// also run for iteration key on ADD | DELETE | Map.SET
switch (type) {
case TriggerOpTypes.ADD:
if (!isArray(target)) {
add(depsMap.get(ITERATE_KEY))
if (isMap(target)) { // 针对map的情况
add(depsMap.get(MAP_KEY_ITERATE_KEY))
}
} else if (isIntegerKey(key)) { // 如果增加了某一项 更新数组
// 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: // 针对map的修改处理
if (isMap(target)) {
add(depsMap.get(ITERATE_KEY))
}
break
}
}
const run = (effect: ReactiveEffect) => {
if (__DEV__ && effect.options.onTrigger) {
effect.options.onTrigger({
effect,
target,
key,
type,
newValue,
oldValue,
oldTarget
})
}
if (effect.options.scheduler) { // 有scheduler选项,会调用scheduler
effect.options.scheduler(effect)
} else {
effect() // 否则直接执行effect
}
}
effects.forEach(run)
}
案例1:
const state = reactive({name:'vue',age:11});
effect(()=>{
debugger
state.name;
});
debugger;
state.name = 'zs'
调试track和effect函数
案例2:
const state = reactive({ name: 'vue', age: 11 });
effect(()=>{ state.name },{
scheduler:(effect)=>{
console.log('更新')
}
})
state.name = 'vv'
effect支持传入scheduler参数
手写丐版响应式原理
一.响应式API实现
const { reactive, shallowReactive, readonly, shallowReadonly } = VueReactivity
let obj = { name: 'vue', age: { n: 11 } }
const state = reactive(obj)
const state = shallowReactive(obj)
const state = readonly(obj)
const state = shallowReadonly(obj)
针对不同的
API创建不同的响应式对象
import {
mutableHandlers,
readonlyHandlers,
shallowReactiveHandlers,
shallowReadonlyHandlers
} from "./baseHandlers"; // 不同的拦截函数
export function reactive(target) {
return createReactiveObject(target, false, mutableHandlers)
}
export function shallowReactive(target) {
return createReactiveObject(target, false, shallowReactiveHandlers)
}
export function readonly(target) {
return createReactiveObject(target, true, readonlyHandlers)
}
export function shallowReadonly(target) {
return createReactiveObject(target, true, shallowReadonlyHandlers)
}
/**
*
* @param target 拦截的目标
* @param isReadonly 是不是仅读属性
* @param baseHandlers 对应的拦截函数
*/
function createReactiveObject(target, isReadonly, baseHandlers) {}
二.shared模块实现
{
"name": "@vue/shared",
"version": "1.0.0",
"description": "",
"main": "index.js",
"module": "dist/shared.esm-bundler.js",
"buildOptions": {
"formats": [
"esm-bundler",
"cjs"
]
},
"keywords": [],
"author": "",
"license": "ISC"
}
配置
tsconfig.json识别引入第三方模块
{
"baseUrl": ".",
"moduleResolution": "node",
"paths": {
"@vue/*": [
"packages/*/src"
]
}
}
使用
yarn install将shared模块注入到node_modules中
三.createReactiveObject实现
Vue3中采用proxy实现数据代理, 核心就是拦截
get方法和set方法,当获取值时收集effect函数,当修改值时触发对应的effect重新执行
import { isObject } from '@vue/shared'
const reactiveMap = new WeakMap();
const readonlyMap = new WeakMap();
function createReactiveObject(target, isReadonly, baseHandlers) {
// 1.如果不是对象直接返回
if(!isObject(target)){
return target
}
const proxyMap = isReadonly ? readonlyMap : reactiveMap; // 获取缓存对象
const existingProxy = proxyMap.get(target);
// 2.代理过直接返回即可
if(existingProxy){
return existingProxy;
}
// 3.代理的核心
const proxy = new Proxy(target,baseHandlers);
proxyMap.set(target,proxy);
// 4.返回代理对象
return proxy;
}
baseHandlers实现
import { isObject } from "@vue/shared";
import { reactive, readonly } from "./reactive";
const get = createGetter();
const shallowGet = createGetter(false, true);
const readonlyGet = createGetter(true);
const shallowReadonlyGet = createGetter(true, true)
const set = createSetter();
const shallowSet = createSetter(true);
/**
* @param isReadonly 是不是仅读
* @param shallow 是不是浅响应
*/
function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
if (!isReadonly) { // 如果是仅读的无需收集依赖
console.log('依赖收集')
}
if (shallow) { // 浅无需返回代理
return res
}
if (isObject(res)) { // 取值时递归代理
return isReadonly ? readonly(res) : reactive(res)
}
return res;
}
}
function createSetter(shallow = false) {
return function set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
return result;
}
}
export const mutableHandlers = {
get,
set
};
export const readonlyHandlers = {
get: readonlyGet,
set(target, key) {
console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
return true;
}
};
export const shallowReactiveHandlers = {
get: shallowGet,
set: shallowSet
};
export const shallowReadonlyHandlers = {
get: shallowReadonlyGet,
set(target, key) {
console.warn(`Set operation on key "${String(key)}" failed: target is readonly.`)
return true;
}
};
四.effect实现
实现响应式effect
export function effect(fn, options: any = {}) {
// 创建响应式effect
const effect = createReactiveEffect(fn, options);
// 默认会让effect先执行一次
if (!options.lazy) {
effect();
}
return effect
}
let uid = 0;
function createReactiveEffect(fn, options) {
// 返回响应式effect
const effect = function reactiveEffect() {
// todo...
}
effect.id = uid++;// 用于做标识的
effect._isEffect = true; // 标识是响应式effect
effect.raw = fn; // 记录原本的fn
effect.deps = []; // 用于收集effect对应的相关属性
effect.options = options;
return effect;
}
利用栈型结构存储effect,保证依赖关系
const state =reactive({name:'vue',age:12,address:'回龙观'})
effect(()=>{ // effect1
console.log(state.name); // 收集effect1
effect(()=>{ // effect2
console.log(state.age); // 收集effect2
});
console.log(state.address); // 收集effect1
})
const effect = function reactiveEffect() {
if (!effectStack.includes(effect)) {
try {
effectStack.push(effect);
activeEffect = effect; // 记录当前的effect
return fn(); // 执行用户传递的fn -> 取值操作
} finally {
effectStack.pop();
activeEffect = effectStack[effectStack.length - 1];
}
}
}
五.track依赖收集
function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
// ...
if (!isReadonly) { // effect函数执行时,进行取值操作,让属性记住对应的effect函数
track(target, TrackOpTypes.GET, key);
}
}
}
const targetMap = new WeakMap();
export function track(target, type, key) {
if(activeEffect === undefined){ // 如果不在effect中取值,则无需记录
return
}
let depsMap = targetMap.get(target);
// WeakMap({name:'vue',age:11},{name:{Set},age:{Set}})
if(!depsMap){ // 构建存储结构
targetMap.set(target,(depsMap = new Map))
}
let dep = depsMap.get(key);
if(!dep){
depsMap.set(key,(dep = new Set));
}
if(!dep.has(activeEffect)){
dep.add(activeEffect);
activeEffect.deps.push(dep);
}
}
六.trigger触发更新
对新增属性和修改属性做分类
function createSetter(shallow = false) {
return function set(target, key, value, receiver) {
const oldValue = target[key];
const hadKey =
isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key);
const result = Reflect.set(target, key, value, receiver);
if (!hadKey) {
// 新增属性
trigger(target, TriggerOpTypes.ADD, key, value)
} else if (hasChanged(value, oldValue)) {
// 修改属性
trigger(target, TriggerOpTypes.SET, key, value, oldValue)
}
return result;
}
}
将需要触发的effect找到依次执行
export function trigger(target, type, key?, newValue?, oldValue?) {
const depsMap = targetMap.get(target);
if (!depsMap) { // 属性没有对应的effect
return
}
const effects = new Set(); // 设置集合
const add = (effectsToAdd) => {
if (effectsToAdd) {
effectsToAdd.forEach(effect => {
effects.add(effect);
})
}
}
if (key === 'length' && isArray(target)) { // 如果修改的是长度
depsMap.forEach((dep, key) => {
// 如果有长度的依赖要更新 如果依赖的key小于设置的长度也要更新
if (key == 'length' || key >= newValue) {
add(dep)
}
});
} else {
if (key !== void 0) {
// 修改key
add(depsMap.get(key));
}
switch (type) {
case TriggerOpTypes.ADD:
if (isArray(target)) {
if (isIntegerKey(key)) { // 给数组新增属性,直接触发length即可
add(depsMap.get('length'))
}
}
break;
default:
break;
}
}
effects.forEach((effect: any) => {
effect();
})
}
七.实现Ref
ref本质就是通过类的属性访问器来实现的,可以将一个普通值类型进行包装。
import { hasChanged, isObject } from "@vue/shared";
import { track, trigger } from "./effect";
import { TrackOpTypes, TriggerOpTypes } from "./operations";
import { reactive } from "./reactive";
export function ref(value) { // ref Api
return createRef(value);
}
export function shallowRef(value) { // shallowRef Api
return createRef(value, true);
}
function createRef(rawValue, shallow = false) {
return new RefImpl(rawValue, shallow)
}
const convert = (val) => isObject(val) ? reactive(val) : val; // 递归响应式
class RefImpl {
private _value;
public readonly __v_isRef = true; // 标识是ref
constructor(private _rawValue, public readonly _shallow) {
this._value = _shallow ? _rawValue : convert(_rawValue)
}
get value() {
track(this, TrackOpTypes.GET, 'value');
return this._value;
}
set value(newVal) {
if (hasChanged(newVal, this._rawValue)) {
this._rawValue = newVal; // 保存值
this._value = this._shallow ? newVal : convert(newVal);
trigger(this, TriggerOpTypes.SET, 'value', newVal);
}
}
}
八.实现toRefs
class ObjectRefImpl{
public readonly __v_isRef = true
constructor(private readonly _object, private readonly _key) {}
get value(){
return this._object[this._key]
}
set value(newVal){
this._object[this._key] = newVal
}
}
export function toRef(object,key){
return new ObjectRefImpl(object,key);
}
export function toRefs(object) {
const ret = isArray(object) ? new Array(object.length) : {};
for (const key in object) {
ret[key] = toRef(object, key)
}
return ret;
}
将对象中的属性转换成ref属性
九.实现Computed
computed的整体思路和
Vue2.0源码基本一致,也是基于缓存来实现的。
import { effect, track, trigger } from './effect';
import { isFunction } from '@vue/shared';
class ComputedRefImpl {
private _value;
private _dirty = true; // 默认是脏值
public readonly effect;
public readonly __v_isRef = true;
constructor(getter, private readonly _setter) {
this.effect = effect(getter, {
lazy: true, // 计算属性特性
scheduler: () => {
if (!this._dirty) { // 依赖属性变化时
this._dirty = true; // 标记为脏值,触发视图更新
trigger(this, 'set', 'value');
}
}
})
}
get value() {
if (this._dirty) {
// 取值时执行effect
this._value = this.effect();
this._dirty = false;
}
track(this, TrackOpTypes.GET ,'value'); // 进行属性依赖收集
return this._value
}
set value(newValue) {
this._setter(newValue);
}
}
export function computed(getterOrOptions) {
let getter;
let setter;
if (isFunction(getterOrOptions)) { // computed两种写法
getter = getterOrOptions;
setter = () => {
console.warn('computed value is readonly')
}
} else {
getter = getterOrOptions.get;
setter = getterOrOptions.set;
}
return new ComputedRefImpl(getter, setter)
}
effect.ts
effects.forEach((effect: any) => {
if(effect.options.scheduler){
return effect.options.scheduler(effect); // 如果有自己提供的scheduler,则执行scheduler逻辑
}
effect();
})
到了这里,你是否有了清晰的认知呢?
点个赞吧~