当前位置: 首页 > Web前端 > vue.js

学习Vue3核心概念,与面试官斗智斗勇(一)收集触发依赖

时间:2023-03-31 15:00:15 vue.js

本文是基于阅读源码的理解写成的。如有不妥欢迎指正交流学习。最近也在帮助想进入前端行业的朋友学习。如需交流学习,可加微信gdgzyw。聊天,学习,打游戏都不错~学习源码最快的方法就是理解概念后写一个简化版的功能。所以我们要先搭建环境,这里我们采用测试驱动的方式。初始化项目初始化package.json并安装依赖yarninit-yyarnadd-D@babel/core@babel/preset-env@babel/preset-typescript@types/jestbabel-jestjest添加脚本启动jestpackage.json{//..."scripts":{"test":"jest"},//...}根目录创建babel.config.jsmodule.exports={presets:[['@babel/preset-env',{targets:{node:'current'}}],'@babel/preset-typescript'],}创建tsconfig.json文件{"compilerOptions":{"target":"es2016","lib":["DOM","es6"],"module":"commonjs","types":["jest"],"esModuleInterop":true,"forceConsistentCasingInFileNames":true,"strict":true,"noImplicitAny":false,"skipLibCheck":true}}至此,我们的项目已经完成初始化。如果需要使用git来管理,可以自己gitinit。编写测试用例创建src/reactivity/tests/effect.spec.ts文件describe('effect',()=>{it('happypath',()=>{constbank=reactive({money:100,});letmyMoney;effect(()=>{myMoney=bank.money*2;});expect(myMoney).toBe(200);bank.money=50;expect(myMoney).toBe(100);});});创建scr/reactivity/tests/reactive.spec.tsdescribe('reactive',()=>{it('happypath',()=>{constorigin={money:100};constbank=reactive(origin);expect(bank).not.toBe(origin);expect(bank.money).toBe(100);});});现在我们运行yarntest测试用例不工作。对应的函数我们还没有创建。让我们正式开始我们的编码会议。编写响应式函数通过上面的测试用例,我们可以知道我们接收到了一个对象的值,并进行了拦截。所以我们可以直接返回一个proxy的代理对象。exportfunctionreactive(obj){returnnewProxy(obj,{get(target,key){returnReflect.get(target,key)},set(target,key,value){returnReflect.set(target,key,value)},})}Reflect.get(target,key)等同于target[key]那么我们可以创建src/reactivity/index.ts文件index.tsexport*from'./reactive'统一导出然后我们去reactive在.spec.ts中引入我们的函数import{reactive}from'../index'并运行测试。纱线测试反应提示PASS。至此,发现其中的一面已经贯穿。然后我们可以开始编写另一个单元测试。和写效果函数一样,我们观察一下测试用例的参数。可以发现他接受的是回调,所以我们的参数是回调函数。那我们就想想怎么把我们上一个反应函数和这个回调函数关联起来。定义用于分组对象的tagetMap变量。为对象中每个键的依赖分组定义depsMap变量。对象是通过反应式定义的。获取时,我们将对象添加到targetMap中的Map中作为分类。然后创建一个Set,将key作为类别保存在Set中。回顾一下我们片面的过程,我们首先定义了一个反应对象。然后我们在effect函数中执行回调函数,在回调函数中我们会读取reactive的值,从而触发get操作。所以我们需要在get操作中做依赖收集。定义一个ReactiveEffect类来收集我们的回调函数src/reactivity/effect.tsletactiveEffectclassReactiveEffect{privatereadonly_fn:anyconstructor(fn){this._fn=fn}run(){activeEffect=thisthis._fn()}}exportfunctioneffect(fn){const_effect=newReactiveEffect(fn)_effect.run()}在初始化的时候,我们把回调函数保存到_fn,当我们执行run方法的时候。会触发我们的回调函数。定义一个track函数完成采集依赖于这个操作src/reactivity/effect.tsconsttargetMap=newMap()exportfunctiontrack(target,key){letdepsMap=targetMap.get(target)if(!depsMap){depsMap=newMap()targetMap.set(target,depsMap)}让deps=depsMap.get(key)if(!deps){deps=newSet()depsMap.set(key,deps)}deps.add(activeEffect)如果此时我们需要设置reactive的值,就会触发set操作。所以触发依赖的操作需要在集合中进行。定义触发函数来触发所有依赖项进入reactive文件,将track和trigger写入相应的operation。src/reactivity/index.ts//...export*from'./effect'src/reactivity/reactive.tsimport{target,trigger}from'./index'exportfunctionreactive(obj){returnnewProxy(obj,{get(target,key){track(target,key)returnReflect.get(target,key)},set(target,key,value){constres=Reflect.set(target,key,value)触发器(target,key)returnres},})}回到我们的单边,将这些库导入src/reactivtiy/tests/effect.spec.tsimport{effect,reactive}from'../index'//。..至此我们已经实现了收集依赖和触发依赖的核心逻辑。我们现在可以运行yarntest来检查。结束语本文是本系列的开篇,后续会陆续分享相关内容。慢慢完善我们对vue3的理解。欢迎关注我,与我深度交流。