作者:啥也不会的码农
https://juejin.cn/post/7465330375663386650
背景
vue3最新版本目前已更新至3.5了,很多同学经过这几年的使用,相信对vue3的常用api都已经烂熟于心了。
但每每被问到源码时,还是虽表面强装镇定,实则内心慌的一批。。。就比如我们经常使用的reactive,很多同学最后就只会憋出一句:reactive的原理是proxy,然后……,就没有然后了
今天我就带着大家将reactive方法一撸到底。
总览
话不多说,直接上图,接下来将带着大家跟着这张图结合源码搞懂reactive的核心源码。

reactive
上面这张图分为上中下三部分,我们一部分一部分进行拆解,首先是最上面部分,这其实就是reactive函数的核心代码
假如我们有一个如下的example.js文件:
<script setup>
import { reactive,effect } from 'vue'
const obj = reactive({
name: '法外狂徒张三'
})
effect(() => {
document.getElementById('app').innerText = obj.name
})
</script>
当我们写下这段代码的时候,实际上是调用了vue中的reactive函数。我们可以在vue源码的packages\reactivity\src\reactive.ts
中找到reactive函的实现:
可以看到reactive函数的实现非常简单,就仅仅返回了一个createReactiveObject
方法执行后的结果。
我们看到,createReactiveObject
函数最终是会执行new Proxy生成一个proxy实例,如果不了解Proxy的同学可以自行去MDN[1]中学习,然后将这个proxy代理对象和target以键值对的方式建立联系,后续当同一个target对象再次执行reactive函数时,直接从proxyMap中获取,最终返回这个proxy代理对象。
所以,整个reactive
函数确实只完成了一件事,那就是生成并返回proxy代理对象
,这也是大多数同学探索vue实现响应式原理的终止点。
baseHandlers
reactive
函数生成的对象之所以能够实现响应式,是因为Proxy
劫持了target对象的读取和写入操作,即Proxy
的第二个参数:baseHandlers
。 接下来,进入中间部分:
我们看看vue源码对baseHandlers
的实现,进入packages\reactivity\src\baseHandlers.ts
中我们可以看到以下代码(不重要的代码都被我删除了):
从createReactiveObject
函数的参数,我们可以知道,Proxy
构造函数中的第二个参数其实是MutableReactiveHandler
实例,而MutableReactiveHandler
继承了BaseReactiveHandler
,因此该实例对象中会包含着一个get
和set
函数,这也是vue完成响应式原理的核心部分。
在get
函数中,除了返回一个`Reflect.get`[2]的结果,还调用了一个track
函数,track
函数的实现在packages\reactivity\src\dep.ts
中:

track
函数的作用是收集依赖。它最终会构造一个类型为WeakMap[3]的targetMap
,其键是我们传入的那个target对象
,值是一个Map[4]类型的depsMap
,depsMap
中存放的才是target对象
的key
和dep
的对应关系。而dep
中存放的就是收集到的依赖。这么说起来有点绕,直接上图:

而在set
中的trigger
函数执行时,所有存储在dep
中的依赖都会被挨个调用。
effect
我们可以看到,dep
中的依赖是一个个的ReactiveEffect实例
,而这个实例又是从何而来呢?这就要靠我们的effect
函数了。

effect
函数需要传递一个函数作为参数,这个函数被称之为副作用函数
。
在effect
函数中,会调用一个ReactiveEffect构造函数
生成ReactiveEffect实例
,这个实例会作为依赖被收集。实例中有一个run
方法,并且在run
方法执行时会调用effect
函数传入的参数,即,副作用函数。从而触发proxy代理对象中的get行为,将这个ReactiveEffect实例
作为依赖收集到dep
中
总结
最后总结一下reactive函数的执行流程:首先,当我们调用reactive函数并传入一个target对象时,reactive内部会调用createReactiveObject函数生成并返回一个proxy代理对象。这个proxy代理对象中get方法会收集并以键值对的方式存储依赖,当改变对象的某个属性时,触发proxy的set函数,set函数中的trigger函数会从之前存储的对象中循环调用所有依赖。
阅读原文:原文链接
该文章在 2025/2/19 13:10:29 编辑过