watchEffect的两种错误用法

最近在看vue3官方文档watchEffect部分时,发现了一个tip:watchEffect 仅会在其同步执行期间,才追踪依赖。在使用异步回调时,只有在第一个 await 正常工作前访问到的属性才会被追踪。对于这句话不是很理解,自己先用写了个小例子试了一下,发现果然如此:修改count的值时,触发了副作用,但是修改msg时没有触发副作用执行。

javascript 复制代码
import { ref, watchEffect } from 'vue'
const count = ref(0)
const msg = ref('hello')
watchEffect(async () => {
console.log('count:', count.value)
await new Promise(resolve => setTimeout(resolve, 100))
console.log('msg:', msg.value)})
// 测试:修改 count 会触发副作用(同步阶段依赖)
count.value = 1 // 触发 watchEffect,打印 count:1 和 msg:hello

// 测试:修改 msg 不会触发副作用(异步阶段未被追踪)
msg.value = 'world' // 无反应

带着疑问,开始寻找答案!

首先要知道watchEffect收集依赖的原理:watchEffect的参数是一个函数 fn,watchEffect会把函数执行一次,并创建响应式 Effect,收集依赖。简化为下面的代码:

javascript 复制代码
let activeEffect = null
function watchEffect(fn) {
    activeEffect = fn
    fn()
    activeEffect = null
}

也就是说,在fn执行期间,activeEffect的值是 fn,fn 执行完毕后,activeEffect的值是null。activeEffect是 Vue3 响应式设计的关键,vue3在对象 get 执行时,收集依赖,在 set 执行时执行依赖。

javascript 复制代码
const effects = []
const count = {
    _value: 1,
    get value() {
        if(activeEffect) {
          effects.push(activeEffect)
        }
        return this._value
    }
    set value(val) {
        this._value = val
        effects.map((cb) => cb())
    }
}
  1. fn 函数在执行过程中,所有访问的响应式数据会触发 get 拦截器,get 函数里会对activeEffect是否有值做判断,此时,activeEffect的值正是 fn,于是就把 fn 放入依赖队列里,watchEffect的依赖收集完成。

  2. 但是当遇到 await 时,函数会 暂停执行,并将后续代码放入微任务队列。此时,当前 fn 的同步执行已结束,activeEffect 会被重置。

  3. await 后的代码恢复执行时,activeEffect 已不再指向当前 watchEffect 的 fn了,这也就解释了watchEffect 仅会在其同步执行期间,才追踪依赖。

了解原理以后,这是由 JavaScript 异步机制和 Vue3 响应式的 activeEffect 设计决定的。

然而还有一种情况也会出现watchEffect不执行副作用的情况:

javascript 复制代码
import { ref, watchEffect } from 'vue'
const count = ref(0)
let flag = false
watchEffect(() => {
    if(flag) {
      console.log('count的值改变了', count.value)
    }
})

setTimeOut(() => {
   flag = true
}, 1000)


setTimeOut(() => {
   count.vaue++
}, 2000)

2秒后,控制台并没有打印信息。这是为什么呢?

watchEffect在执行时,flag 是 false,因此并没有执行 console.log('count的值改变了', count.value) 这句代码,没有访问响应式数据count,count的 get 拦截器也不会触发,所以并没有收集依赖。即使后面修改count的值,也不会触发副作用函数执行。

如果在开发过程中,发现watchEffect的副作用函数没有执行时,就要检查一下是不是上述两种情况了!

团队介绍

智慧家技术平台-应用软件框架开发 」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

相关推荐
岁岁种桃花儿10 小时前
Vue核心语法第十一篇:绑定样式
前端·javascript·vue.js
程序员黑豆17 小时前
Java类型推断完全指南:从var到菱形运算符,掌握使用限制与最佳实践
java·前端·ai编程
To_OC18 小时前
踩了个 TS 的坑之后,我终于把 type 和 interface 掰明白了
前端·react.js·typescript
GreenTea18 小时前
深度解读 Anthropic 多智能体报告:更强的模型 ≠ 更好的协调
前端·后端·算法
浮生望18 小时前
前端API工程化:用 Mock 数据与 Axios 配置实现独立于后端的并行开发
前端
万少19 小时前
给 DeepSeek Harness 装个"应用商店":一条命令,595 个插件随你逛
前端·javascript·后端
波波00719 小时前
C# 15 重磅新特性: 带标签 break 与 continue:重新定义嵌套循环控制流
服务器·前端·c#
Brown.alexis20 小时前
es6知识点1-自备使用
前端·ecmascript·es6
小爬的老粉丝21 小时前
JavaScript 纯前端预览 WPS:先识别容器,再路由解析器
前端·javascript·wps