news 2026/5/14 0:46:45

48-mini-vue 实现 watchEffect

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
48-mini-vue 实现 watchEffect

实现 watchEffect

  1. 测试
import{reactive}from"@guide-mini-vue/reactivity"import{nextTick}from"../src/scheduler"import{expect}from"vitest"import{watchEffect}from'../src/apiWatch'describe('api: watch',()=>{it("effect",async()=>{conststate=reactive({count:0})letdummywatchEffect(()=>{dummy=state.count})expect(dummy).toBe(0)state.count++awaitnextTick()expect(dummy).toBe(1)})})
  1. 功能点
  • 经过 watchEffect 包裹的 fn,在初始化时会先执行一遍 fn
  • 组件更新渲染前,调用包裹的 fn 函数
  1. 实现
// renderer.tsfunctionsetupRenderEffect(instance,vnode,container,anchor){instance.update=effect(()=>{let{proxy}=instanceif(!instance.isMounted){constsubTree=instance.subTree=instance.render.call(proxy,proxy)patch(null,subTree,container,instance,anchor)vnode.el=subTree.el instance.isMounted=true}else{const{next,vnode}=instanceif(next){next.el=vnode.elupdateComponentPreRender(instance,next)}const{proxy}=instanceconstsubTree=instance.render.call(proxy,proxy)constprevSubTree=instance.subTree instance.subTree=subTreepatch(prevSubTree,subTree,container,instance,anchor)}},{scheduler(){// ✅ 注意:这里是相关位置,虽然我们这块没有改动queueJobs(instance.update)}})}return{createApp:createAppAPI(render)}// scheduler.tsletqueue:any[]=[]constactivePreFlushCbs:any[]=[]// ✅letisFlushPending=falseconstp=Promise.resolve()exportfunctionnextTick(fn?:any){returnfn?p.then(fn):p}exportfunctionqueueJobs(job){if(!queue.includes(job)){queue.push(job)}queueFlush()}functionqueueFlush(){if(isFlushPending)returnisFlushPending=truenextTick(flushJobs)}exportfunctionqueuePreFlushCb(cb){// ✅ 收集 watchEffect 依赖activePreFlushCbs.push(cb)flushJobs()}functionflushJobs(){isFlushPending=false// 组件渲染之前flushPreFlushCbs()// ✅ 执行 watchEffect 收集依赖// 组件渲染letjobwhile(job=queue.shift()){job&&job()}}functionflushPreFlushCbs(){// ✅for(leti=0;i<activePreFlushCbs.length;i++){activePreFlushCbs[i]()}}
  1. 功能点
// apiWatch.spec.tsit("stopping the watcher (effect)",async()=>{conststate=reactive({count:0})letdummyconststop:any=watchEffect(()=>{dummy=state.count})expect(dummy).toBe(0)stop()state.count++awaitnextTick()expect(dummy).toBe(0)})
  1. 实现
// ✅ apiWatch.tsimport{ReactiveEffect}from"../../reactivity/src/effect";import{queuePreFlushCb}from'./scheduler'exportfunctionwatchEffect(fn:any){// 执行fn,需要放到组件渲染之前functionjob(){effect.run()}consteffect=newReactiveEffect(fn,()=>{queuePreFlushCb(job)})effect.run()}
  1. 功能点
// apiWatch.spec.tsit("stopping the watcher (effect)",async()=>{conststate=reactive({count:0})letdummyconststop:any=watchEffect(()=>{dummy=state.count})expect(dummy).toBe(0)stop()state.count++awaitnextTick()expect(dummy).toBe(0)})
  1. 实现
import{ReactiveEffect}from"../../reactivity/src/effect";import{queuePreFlushCb}from'./scheduler'exportfunctionwatchEffect(fn:any){// 执行fn,需要放到组件渲染之前functionjob(){effect.run()}consteffect=newReactiveEffect(fn,()=>{queuePreFlushCb(job)})effect.run()return()=>{// ✅effect.stop()}}
  1. 功能点
// apiWatch.spec.tsit("cleanup registration (effect)",async()=>{conststate=reactive({count:0})constcleanup=vi.fn()letdummyconststop:any=watchEffect((onCleanup)=>{onCleanup(cleanup)dummy=state.count})expect(dummy).toBe(0)state.count++awaitnextTick()expect(cleanup).toHaveBeenCalledTimes(1)expect(dummy).toBe(1)// stop()// expect(cleanup).toHaveBeenCalledTimes(2)})
  1. 实现
// apiWatch.tsimport{ReactiveEffect}from"../../reactivity/src/effect";import{queuePreFlushCb}from'./scheduler'exportfunctionwatchEffect(source){// 执行fn,需要放到组件渲染之前functionjob(){effect.run()}letcleanup;// ✅constonCleanup=(fn)=>{// ✅cleanup=fn}functiongetter(){// ✅if(cleanup){cleanup()}source(onCleanup)}consteffect=newReactiveEffect(getter,()=>{queuePreFlushCb(job)})effect.run()return()=>{effect.stop()}}
  1. 功能点
// 实现 spec 中的注释点stop()expect(cleanup).toHaveBeenCalledTimes(2)// watchEffect.tsimport{ReactiveEffect}from"../../reactivity/src/effect";import{queuePreFlushCb}from'./scheduler'exportfunctionwatchEffect(source){// 执行fn,需要放到组件渲染之前functionjob(){effect.run()}letcleanup;constonCleanup=(fn)=>{// cleanup = fn// effect.onStop = () => { // ✅// fn()// }// ✅ 上面可以优化为cleanup=effect.onStop=()=>{// ✅fn()}}functiongetter(){if(cleanup){cleanup()}source(onCleanup)}consteffect=newReactiveEffect(getter,()=>{queuePreFlushCb(job)})effect.run()return()=>{effect.stop()}}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 20:31:54

别再瞎找了!8个降AI率平台深度测评与推荐

在如今的学术写作中&#xff0c;AI生成内容的广泛应用让论文查重和AIGC率问题变得愈发突出。对于本科生来说&#xff0c;如何在保持论文逻辑性和原创性的前提下&#xff0c;有效降低AI痕迹和查重率&#xff0c;成为了一项重要挑战。而AI降重工具的出现&#xff0c;为这一难题提…

作者头像 李华
网站建设 2026/4/18 20:31:13

重磅!城市智能体建设官方指南发布,全域数字化转型有了标准化框架

近日&#xff0c;全国数据标准化技术委员会发布《城市全域数字化转型 城市智能体建设及应用指南&#xff08;征求意见稿&#xff09;》&#xff0c;这份由数十家科研机构、科技企业、高校联合起草的技术文件&#xff0c;为城市智能体的设计、开发、建设与运营提供了全流程、系统…

作者头像 李华
网站建设 2026/5/10 14:14:45

Balabolka调用本地Edge晓晓语音全教程(附资源适配指南)

经常用文本转语音&#xff08;TTS&#xff09;的朋友&#xff0c;应该都懂“找一款自然又好用的语音”有多难——在线TTS要联网&#xff0c;离线语音要么音色生硬&#xff0c;要么适配麻烦。最近我终于搞定了一套完美组合&#xff1a;用Balabolka当载体&#xff0c;搭配Natural…

作者头像 李华
网站建设 2026/4/18 20:31:14

阳台光伏远程监控智慧运维系统方案

随着“双碳”目标的深入推进及分布式能源政策的持续利好&#xff0c;光伏应用正从集中式地面电站向户用及城市建筑场景快速渗透。其中&#xff0c;“阳台光伏”作为一种灵活、便捷、低门槛的微型发电形式&#xff0c;在欧洲及中国部分高密度城市社区迅速兴起。它利用公寓阳台、…

作者头像 李华
网站建设 2026/4/18 20:32:09

2026年AI风向标:一文读懂Skills,让你的Agent“开窍”并轻松收藏!

2026年AI领域出现重大变革&#xff0c;Skills的推出使大模型从简单的聊天机器人进化为能实际执行任务的智能体。文章详细介绍了Skills的概念、工作原理、与Command和MCP的区别&#xff0c;以及如何编写高质量的Skills。此外&#xff0c;还推荐了官方和社区的热门Skills资源&…

作者头像 李华
网站建设 2026/4/18 20:31:33

Java竞争激烈,中低端岗真的饱和了吗?

2025已经过去了&#xff0c;但是大家就业压力却没有缓解多少。很多粉丝后台留言&#xff0c;Java程序员面临的竞争太激烈了……我自己也有实感&#xff0c;多年身处一线互联网公司&#xff0c;虽没有直面过求职跳槽的残酷&#xff0c;但经常担任技术面试考官&#xff0c;对程序…

作者头像 李华