「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战」。
前言
回忆前篇 Vue3.0源码学习——响应式原理(一),学习了Vue3.0响应式API的使用以及 reactive
函数的源码,这篇紧跟上一篇将结合单步调试和源码进行探究
单步调试
写一个简单的例子
<body>
<div id="app">
<p>{{ state.counter }}</p>
</div>
<script>
const app = Vue.createApp({
setup() {
const state = Vue.reactive({
counter: 1
})
setInterval(() => {
state.counter++
}, 1000)
Vue.watch(() => state.counter, () => {
})
return {
state
}
}
})
app.mount('#app')
</script>
</body>
复制代码
- 打开调试,首先找到
reactive
函数/packages/reactivity/src/reactive.ts
,单步进入返回的createReactiveObject
函数
- 单步执行查看内部执行,
Proxy
代理中 handeler 是baseHandlers
,就是在reactive
中调用时传入的mutableHandlers
- 找到
mutableHandlers
的位置/packages/reactivity/src/baseHandlers.ts
getter拦截
- 其中的
get
是createGetter
函数,可以看到第一次 get 到了数据
- 此时查看调用栈,可以看到是在之前的例子中定义的
watch
中第一次获取数据
- 单步执行如果数据不是只读
!isReadonly
会走到track
依赖收集函数,然后单步进入看一下内部执行
- 内部生成了一个依赖收集的Map对象
depsMap
,会根据当前数据的key
从全局的targetMap
去取,第一次获取数据没有找到就会新建一个,value值dep
就是收集到的依赖,然后执行到一个依赖跟踪函数trackEffects
将当前数据的依赖dep
传入
trackEffects
会判断依赖中是否有组件更新函数,如果没有就会放进去
- getter操作会执行好几次,收集完了依赖就可以看一下在setter中会有怎样的操作,可以将getter中的断点取消
setter拦截
- 将断点打在
createSetter
函数中,可以看到页面上已经渲染出了数据,因为例子中在setInterval
改变了counter
的值,所有走到了setter中,会执行到trigger
触发组件更新函数
- 单步进入
trigger
看一下,首先会拿到当前数据收集的依赖depsMap
, 在依赖收集dep
中放入组件更新函数
- 然后走到
triggerEffects
触发真正的组建更新函数,会将收集到依赖的地方进行相应的操作
- 继续往下执行可以看到页面也更新了
- 更新流程在之前文章中介绍过 Vue3.0源码学习——更新流程分析
reactive函数执行流程图
根据源码分析和单步调试可以简单整理了一个流程图