当前位置: 首页 > news >正文

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()}}
http://www.jsqmd.com/news/417704/

相关文章:

  • 解析国货防脱洗发水品牌怎么选:长发小寨以核心酶抑制率立标杆 - 资讯焦点
  • 2026年2月陶瓷台面实验台实力厂家,高端实验台面实力品牌 - 品牌鉴赏师
  • 2、哪些 EA 是**坑**,哪些**真能活久一点**,你一看就懂。
  • 2026大型企业适用 Jira 替代软件怎么选?深度评测五大主流方案 - 资讯焦点
  • 整骨师资格证怎么考取? - 中媒介
  • 国家卫健委认证整骨培训 - 中媒介
  • 陕西搬家公司推荐:2026 年一站式搬家服务靠谱服务商筛选 - 深度智识库
  • 凯氏定氮仪推荐品牌:进口与国产实力厂家盘点 - 品牌推荐大师
  • 整骨安全操作实训 - 中媒介
  • 突破数据困境:罕见病AI模型的泛化能力测试革命——软件测试工程师在医疗AI质量保障中的新战场
  • 从纯文本到精美文档:TXT 转 PDF 的三种高效方案 - E
  • 6款免费AI论文工具:一键极速生成超长篇幅,帮你省下80%写作时间 - 麟书学长
  • 别再瞎找了!8个降AI率平台深度测评与推荐
  • github基础使用手册
  • 执业整骨师培训标准 - 中媒介
  • 2026年东莞整形医院排名前三揭晓:资质合规 + 项目资深!​ - 资讯焦点
  • 告别低效繁琐!降AI率工具 千笔·专业降AI率智能体 VS WPS AI
  • 实操整骨教学课程 - 中媒介
  • 东莞整形医生比较好的有哪些?这几位技术口碑双在线,擅长项目全解析! - 资讯焦点
  • 学霸同款!断层领先的AI论文工具 —— 千笔ai写作
  • 重磅!城市智能体建设官方指南发布,全域数字化转型有了标准化框架
  • 整骨创业班课程内容 - 中媒介
  • 2026年市场营销与品牌整合营销服务商推荐:一墨堂(ENKO Brand) - 品牌推荐官
  • 益生菌品牌优选,破解代谢慢,唤醒肠道燃脂活力 - 资讯焦点
  • 临床整骨案例解析课 - 中媒介
  • 2026户外庭院遮阳伞哪家好?陕西必得以全系户外桌椅/沙发/家具矩阵,重新定义庭院遮阳 - 深度智识库
  • 喜报 | 苏州锐百顺涂层顺利通过FDA现场审核, APS五十周年再添里程碑 - 资讯焦点
  • 文档解析在RAG中起什么作用?
  • 2026西安真实口碑认证:锦岳搬家领衔,用细节圈粉西安人 - 深度智识库
  • 揭秘2026年三大云电脑王者,谁才是真香选择 - 资讯焦点