当前位置: 首页> 房产> 政策 > 公司网站介绍模板 html_西安市建设工程信息网诚信信息平台官网_今天刚刚发生的新闻最新新闻_陕西百度推广的代理商

公司网站介绍模板 html_西安市建设工程信息网诚信信息平台官网_今天刚刚发生的新闻最新新闻_陕西百度推广的代理商

时间:2025/7/11 15:37:16来源:https://blog.csdn.net/triggerV/article/details/146332986 浏览次数:1次
公司网站介绍模板 html_西安市建设工程信息网诚信信息平台官网_今天刚刚发生的新闻最新新闻_陕西百度推广的代理商

案例

通过以下这个案例来进行理解:

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title><script src="../../dist/vue.global.js"></script></head><body><div id="app"></div><script>const { reactive, effect } = Vueconst obj = reactive({name: 'first',age: 18})effect(() => {document.querySelector('#app').innerHTML = obj.name})setTimeout(() => {obj.name = 'last'}, 2000)</script></body>
</html>

首先引入 reactive 和 effect 两个函数,创建了一个响应式对象obj,并使用 effect 注册了一个副作用函数,该函数将 obj.name 的值渲染到页面上,2 秒后,我们修改了 obj.name 的值,页面也随之更新


createReactiveObject方法

reactive函数在packages/reactivity/src/reactive.ts文件下,它的主要作用是将普通对象转换为响应式对象

在这里插入图片描述

reactive函数实际执行的是 createReactiveObject 方法,该方法也在该文件下

  • target:我们需要传入的对象
  • isReadonly:要创建的代理是否为只可读
  • baseHandlers 是对基本类型的劫持,即 [Object, Array]
  • collectionHandlers 是对集合类型的劫持,即 [Set, Map, WeakMap, WeakSet]
  • proxyMap:WeakMap 类型,用于缓存已经创建的代理对象

如果 target 不是对象类型,直接返回原值,只有对象类型的数据才能被响应式系统处理;如果 target 已经是 Proxy 类型,并且 isReadonly 为 false,直接返回目标对象本身,因为 Proxy 对象已经被处理过,无需重复处理

在这里插入图片描述

该函数实际做了 proxyMap 缓存处理,最终返回一个 proxy 实例对象。这里主要关注new Proxy这段代码,第一个参数target为传进来的对象,即{ name: 'first', age: 18 },第二个baseHandlers参数即传入的 mutableHandlers 对象,该对象定义在packages/reactivity/src/baseHandlers.ts中:

在这里插入图片描述

该对象定义了 get、set 等方法,从而对传入的数据进行依赖收集和依赖触发,先看下结果后面再对这部分逻辑进行分析,reactive函数执行完毕,obj 得到了一个 proxy 的实例对象,接下来执行effect方法

在这里插入图片描述

effect方法

该方法定义在packages/reactivity/src/effect.ts文件中,它用于创建副作用函数,能够自动追踪其内部依赖的响应式数据变化,并在数据变更时重新执行,它是watchcomputed等 API 的底层实现基础

(可以这样理解:副作用函数就像是一个“观察者”,它观察着某些数据的变化,一旦数据变化,它就会采取行动

fn 是需要被追踪的副作用函数,若 fn 已被 effect 包装过,则提取其原始函数。声明一个构造函数ReactiveEffect的实例对象_effect,执行构造函数中的run方法

在这里插入图片描述

ReactiveEffect构造函数如下:

  • active:表示该副作用是否活跃,默认为true
  • deps:一个数组,用于存储该副作用函数所依赖的依赖项
  • parent:表示该副作用的父副作用,默认为undefined

run()执行流程:若activefalse,直接执行fn但不收集依赖。设置activeEffect = this,标记当前活跃的副作用,运行 fn() ,触发响应式数据的 get 拦截器,完成依赖收集

stop():标记active = false,若当前正在执行自身(activeEffect === this),标记deferStop,待执行结束后清理;调用cleanupEffect(this)从所有依赖集合中移除当前副作用

export class ReactiveEffect<T = any> {active = truedeps: Dep[] = []parent: ReactiveEffect | undefined = undefined// 省略constructor(public fn: () => T,public scheduler: EffectScheduler | null = null,scope?: EffectScope) {recordEffectScope(this, scope)}run() {if (!this.active) {return this.fn()}let parent: ReactiveEffect | undefined = activeEffectlet lastShouldTrack = shouldTrackwhile (parent) {if (parent === this) {return}parent = parent.parent}try {this.parent = activeEffectactiveEffect = thisshouldTrack = true// 省略// 执行 fn 函数 即传入的匿名函数//  () => {//     document.querySelector('#app').innerHTML = obj.name//  }return this.fn()} finally {// 省略if (this.deferStop) {this.stop()}}}stop() {// stopped while running itself - defer the cleanupif (activeEffect === this) {this.deferStop = true} else if (this.active) {cleanupEffect(this)if (this.onStop) {this.onStop()}this.active = false}}
}

这里接收一个 fn 方法即传入的匿名函数,然后设置 active、deps、parent 等属性:

在这里插入图片描述

之后执行_effect.run(),即执行构造函数ReactiveEffectrun方法,需要关注activeEffect = this,此时被赋值为:

在这里插入图片描述

然后执行fn函数,即执行传入的匿名函数,之后执行document.querySelector('#app').innerHTML = obj.name触发objget方法

get/set

get方法上述中被定义在packages/reactivity/src/baseHandlers.ts文件中,实际触发的是createGetter函数,主要关注**track(target, TrackOpTypes.GET, key)** 这段代码,它是对数据的依赖收集, 也是get方法的核心

const get = /*#__PURE__*/ createGetter()function createGetter(isReadonly = false, shallow = false) {return function get(target: Target, key: string | symbol, receiver: object) {// 省略const targetIsArray = isArray(target)// 省略// Reflect API// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect// Reflect.get() 等同于 res = target[key]// Reflect 用来替代直接调用 Object 的方法const res = Reflect.get(target, key, receiver)// 省略if (!isReadonly) {// 核心,添加依赖收集track(target, TrackOpTypes.GET, key)}if (shallow) {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}
}

track函数被定义在packages/reactivity/src/effect.ts文件中,这里的targetMapWeakMap对象,该对象是一个弱引用类型

以传入的对象{ name: 'first', age: 18}作为keyvalue 值为Map对象,之后设置depsMapkey 当前为namevalueSet对象,最后执行trackEffects(dep, eventInfo)

在这里插入图片描述

trackEffects是用于依赖收集的核心函数,将当前活跃的副作用函数 activeEffect 与依赖集合 dep 关联起来,从而实现当依赖数据变化时能够触发副作用函数的重新执行

在这里插入图片描述

此时再看下targetMap对象数据:

在这里插入图片描述

这样就完成了数据的依赖收集,之后就可以通过指定对象指定属性获取到对应的 fn 方法。而依赖收集本质上就是targetMapReactiveEffect之间的关联

createGetter执行完毕返回对应的值,当前为 first:

在这里插入图片描述

两秒后执行obj.name = 'last',触发set方法,该方法定义在packages/reactivity/src/baseHandlers.ts中,主要关注trigger(target, TriggerOpTypes.SET, key, value, oldValue)这行代码,键存在且新值与旧值不同,触发 set 类型的依赖更新

在这里插入图片描述

trigger方法

该方法被定义在packages/reactivity/src/effect.ts文件中:

export function trigger(target: object,type: TriggerOpTypes,key?: unknown,newValue?: unknown,oldValue?: unknown,oldTarget?: Map<unknown, unknown> | Set<unknown>
) {// 根据传入的对象 获取 对应的 Map 对象const depsMap = targetMap.get(target)if (!depsMap) {// never been trackedreturn}let deps: (Dep | undefined)[] = []if (type === TriggerOpTypes.CLEAR) {// collection being cleared// trigger all effects for targetdeps = [...depsMap.values()]} else if (key === 'length' && isArray(target)) {depsMap.forEach((dep, key) => {if (key === 'length' || key >= (newValue as number)) {deps.push(dep)}})} else {// schedule runs for SET | ADD | DELETEif (key !== void 0) {// 根据属性获取对应的 ReactiveEffectdeps.push(depsMap.get(key))}// also run for iteration key on ADD | DELETE | Map.SETswitch (type) {// 省略// 当前为 set 类型case TriggerOpTypes.SET:if (isMap(target)) {deps.push(depsMap.get(ITERATE_KEY))}break}}const eventInfo = __DEV__? { target, type, key, newValue, oldValue, oldTarget }: undefinedif (deps.length === 1) {if (deps[0]) {if (__DEV__) {triggerEffects(deps[0], eventInfo)} else {triggerEffects(deps[0])}}} else {const effects: ReactiveEffect[] = []for (const dep of deps) {if (dep) {effects.push(...dep)}}if (__DEV__) {triggerEffects(createDep(effects), eventInfo)} else {triggerEffects(createDep(effects))}}
}

根据指定对象获取到对应的Map对象,此时depsMap为:

在这里插入图片描述

之后再根据指定属性获取对应的ReactiveEffect,再添加到deps中,此时deps为:

在这里插入图片描述

后面我们只需关注triggerEffects(deps[0], eventInfo) 这行代码, triggerEffects函数也packages/reactivity/src/effect.ts文件中:

在这里插入图片描述

可以看出triggerEffects函数实际先获取到 effect 数组,遍历数组执行每个 effect.run() ,实际执行的是 fn 方法,该方法是最初依赖收集时传入的匿名函数,之后再次触发getter方法,从而进行赋值,至此整个依赖触发完成

在这里插入图片描述

总结

1)reactive 函数与 createReactiveObject 方法

reactive函数实际执行了createReactiveObject方法,该方法主要创建了一个Proxy实例对象,给代理对象添加了gettersetter行为

gettersetter方法定义在mutableHandlers对象中,用于拦截对象属性的获取和设置操作

2)get 方法与依赖收集

get方法实际执行了createGetter方法,在这个过程中,track函数被调用,用于进行依赖收集

依赖收集的过程是构建一个WeakMaptargetMap)对象,完成指定对象obj的指定属性nameeffect的依赖收集工作

3)effect 函数与 ReactiveEffect 实例

effect函数实际创建了一个ReactiveEffect实例,该构造函数接收一个fn函数(即传进来的匿名函数),该回调函数必须暴露getter行为

ReactiveEffectrun函数中,给activeEffect赋值,并执行fn函数

4)依赖收集的具体过程

当访问obj.name时,getter被触发,激活track方法,该方法构建WeakMaptargetMap)对象,记录下当前活跃的副作用函数与obj.name的依赖关系

5)set 方法与依赖触发

set方法实际执行了createSetter方法,触发trigger函数进行依赖触发

trigger函数从之前targetMap依赖收集的对象中获取,根据key(即name)获取到对应的副作用函数,然后执行fn函数,从而完成一个依赖触发的过程

6)依赖触发的具体过程

当修改obj.name时,setter被触发,激活trigger方法,它通知所有依赖于obj.name的副作用函数重新执行,从而更新视图


reactive缺陷:

  • 解构后不支持响应性
  • 不支持基本类型,只能是对象
关键字:公司网站介绍模板 html_西安市建设工程信息网诚信信息平台官网_今天刚刚发生的新闻最新新闻_陕西百度推广的代理商

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: