一、watch 属性是干啥的
在 Vue3 里,watch
属性就像是一个小侦探,专门盯着数据的变化。生活中,我们可能会关注股票价格的变动,一旦价格变了,就考虑是买入还是卖出。在 Vue3 的世界里,当某个数据发生变化时,我们可能需要执行一些操作,比如发送一个网络请求、更新页面上的某个元素,这时候 watch
就派上用场啦。它能帮助我们实现这些副作用逻辑,也就是在数据变化时要做的额外事情。
二、源码分析
简化源码示例
// 从响应式模块引入一些必要的工具函数
import { effect, reactive, track, trigger } from './reactive'
// 定义watch函数,接收两个参数:source和cb
function watch(source, cb) {
// 用来存储旧值和新值
let oldValue, newValue
// 当数据变化时会执行的函数
const job = () => {
// 获取新值
newValue = effect(() => source())
// 如果旧值和新值不一样
if (oldValue!== newValue) {
// 调用回调函数,把旧值和新值传进去
cb(oldValue, newValue)
// 更新旧值为新值
oldValue = newValue
}
}
// 先获取一次初始值,作为旧值
oldValue = effect(() => source())
// 返回一个清理函数,现在里面暂时没写具体清理逻辑
return () => {
// 这里可以添加清理逻辑,比如取消订阅等
}
}
// 创建一个响应式对象
const state = reactive({ count: 0 })
// 使用watch来观察state.count的变化
watch(() => state.count, (oldCount, newCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`)
})
// 改变count的值,触发watch的回调
state.count++
功能解释
watch
函数就像是一个指挥官,它有两个重要的 “手下”:
source
:这是一个 “情报收集员”,它会告诉watch
要观察哪个数据。它可以是一个函数,这个函数返回我们要观察的数据。cb
:这是一个 “行动执行者”,当source
所观察的数据发生变化时,cb
就会被调用,并且会得到变化前后的值,它可以根据这些值去执行相应的操作。
watch
函数还会返回一个清理函数,就像是一个 “收尾工人”,当我们不再需要这个 watch
时,可以用这个清理函数来做一些清理工作,比如取消订阅某个事件,避免占用不必要的资源。
工作原理
- 建立初始状态:
watch
函数一开始会调用effect
函数(这个函数就像是一个 “魔法助手”,能帮助我们追踪数据的变化),让它去执行source
函数,得到数据的初始值,把这个值记为oldValue
。在这个过程中,effect
会在watch
和source
函数用到的数据之间建立一种 “联系”,就像给它们牵了一根线,这样数据一变,watch
就能知道。 - 数据变化触发:当被观察的数据发生变化时(这个变化是通过
trigger
函数触发的,就像是有人拉了一下那根线),job
函数就会被调用。job
函数会再次让effect
去执行source
函数,得到新的值,记为newValue
。 - 比较并执行操作:然后
watch
会比较oldValue
和newValue
,如果它们不一样,就说明数据真的变了,这时候就会调用cb
函数,把oldValue
和newValue
告诉它,让它去执行相应的操作。最后,把oldValue
更新成newValue
,为下一次数据变化做准备。
示例代码解释
import { effect, reactive, track, trigger } from './reactive'
function watch(source, cb) {
let oldValue, newValue
const job = () => {
newValue = effect(() => source())
if (oldValue!== newValue) {
cb(oldValue, newValue)
oldValue = newValue
}
}
oldValue = effect(() => source())
return () => {
// 这里可以添加清理逻辑,比如取消订阅等
}
}
const state = reactive({ count: 0 })
watch(() => state.count, (oldCount, newCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`)
})
state.count++
- 导入工具:代码开头导入了一些工具函数,就像是我们做菜需要的各种调料,这些函数是实现响应式的基础。
- 定义
watch
函数:我们自己定义了watch
函数,让它能完成观察数据变化的任务。 - 创建响应式对象:用
reactive
函数创建了一个响应式对象state
,里面有个count
属性,初始值是 0。这就像是我们有了一个小盒子,里面装着一个数字。 - 使用
watch
观察:用watch
函数来观察state.count
的变化。当state.count
变了,就会在控制台打印出变化前后的值。 - 触发变化:最后让
state.count
自增 1,这就像是我们动了一下那个小盒子里的数字,watch
就会发现这个变化,然后调用回调函数,我们就能在控制台看到输出啦。
三、设计选择讨论
用 effect
建立依赖关系
就像盖房子需要一个好的地基一样,watch
使用 effect
函数来建立依赖关系,这是一个很聪明的设计。有了 effect
,watch
能自动追踪数据的变化,我们不用手动去管理这些复杂的关系,代码变得简单又高效。如果没有 effect
,我们可能要写很多额外的代码来处理数据的追踪,那可就麻烦死了。
返回清理函数
想象一下,我们举办了一场派对,派对结束后需要打扫场地。watch
返回的清理函数就像是打扫场地的工具。在实际开发中,有时候我们不再需要某个 watch
了,比如一个页面关闭了,对应的 watch
也应该停止工作。有了清理函数,我们就可以在里面写代码,比如取消订阅某个事件,避免占用不必要的内存,让程序运行得更流畅。
比较新旧值
这就像是我们去超市买东西,结账时会核对价格,看看是不是和标签上的一样。watch
比较 oldValue
和 newValue
,只有当它们不一样时才会调用回调函数。如果不比较,哪怕数据变化前后的值是一样的,回调函数也会被调用,这就会浪费计算机的资源,让程序变慢。通过比较,我们只在数据真的有变化时才执行操作,提高了程序的性能。
通过上面的分析,相信你对 Vue3 里的 watch
属性有了更清楚的认识啦!