当前位置: 首页 > 科技观察

不看源码如何学习petite-vue源码

时间:2023-03-15 08:35:39 科技观察

大家好,我是Kason。周末没事做,准备找个代码量小的优秀库学习一下。最后,我选择了最近发布的petite-vue,原因如下:代码量小(只有5.8kb),源代码高度模块化(相对于React),易于阅读和基于Vite构建,并且您可以通过执行yarndev开始调试源代码虚拟DOM和编译时解决方案可以作为阅读Vue源代码的基础。底层的响应式更新原则同样适用于Mobx和SolidJS等库。一次看多本很有收获,但是周末的时间太宝贵了,而且我已经4年没用过Vue了。,怎样才能高效学习源码呢?最好是“学源码不看源码”。接下来,我将以petite-vue为例,演示学习源码的正确姿势。Petite-vue可以理解为:一个简单的Vue,用真正的DOM替换了Vue模板。例如下面的Demo:add1

{{count}}

div及其后代是真正的DOM标签,所以页面初始化如下:然后执行下面代码完成petite-vue的初始化:createApp({count:0}).mount()此时页面:阅读框架源码,避免一上手就从入口函数一路调试,它很容易混淆。正确的做法是像剥洋葱一样一层层剥:这好像是大蒜?那么,让我们先从Performance面板来看“首屏渲染”的调用栈:调用栈大致分为蓝框和红框。部分,先看左边的蓝框:从createContext和reactive关键字来看,应该是创建响应式上下文。至于响应性的含义,我们还不知道。再看右边红框中的部分:从调用栈的深度和页面渲染的效果来看,我们猜测这部分所做的工作包括:遍历DOM,完成数据的双向绑定和viewsInitialrendering接下来我们来验证一下猜测。请注意,到目前为止,我们还没有看到一行源代码来验证在遍历DOM调用堆栈中多次调用了walk和walkChildren。大概率是遍历工作执行的具体方法。让我们确认一下。登录源码walk方法:exportconstwalk=(node:Node,ctx:Context):ChildNode|null|void=>{console.log('walk',node);//...}排除换行符"\n》对应的文本节点,打印顺序如下:walkdivwalkwalk"add1"walk

0

walk"0"从打印结果来看,这是一个"depth-firsttraversal”(如果有子节点,则遍历子节点,如果没有子节点,则遍历兄弟节点)显然,petite-vue挂载采用“深度优先遍历”,处理每个与“context”相关的DOM节点状态”穿越。Demo中context包含state{count:0}:createApp({count:0}).mount()

{{count}}

遍历后变为

0

。确定双向绑定的粒度接下来,我们需要确认双向绑定的范围,即:触发更新后,会重新遍历哪些范围的DOM,并进行相应的DOM操作?打开Performance后,点击触发更新:可以看到没有walk、walkChildren(或者类似的遍历过程),只调用了reactEffect的一个方法来更新DOM。这意味着挂载时的深度优先遍历在状态和更新DOM的方法之间建立了一对一的对应关系。因为确定了对应关系,所以不需要额外的遍历过程来确定需要改变的DOM。更新状态后,只需要找到更新与其相关的DOM的方法并执行即可。例如:用下面的函数连接count状态:functionsetCount(value){p.textContent=value;}每次count变化时调用setCount(count)来更新p对应的DOM。因此,petite-vue的工作原理主要包括两点:挂载时对DOM的深度优先遍历,以及statefulDOM的状态建立与更新DOM方法的关系(如

{{count}}

)当一对一关系更新时,找到更新状态对应的DOM的方法并执行。可以看到,即使不深入源码,也能对工作流程有一个大概的了解。如果想更进一步,比如理解“关系是如何建立的”(涉及到“响应式更新”),那么就需要深入源码。这里推荐VueMastery的Vue3Reactivity课程,可以补充“响应式更新”的知识。小结本文介绍了复杂框架源码的阅读方法——从抽象到具体。从mount和update时的调用栈推导出整体工作流程从整体工作流程中发现核心知识——响应式更新掌握了整体工作流程和响应式更新后,阅读自己感兴趣的部分,以免陷入海量的代码。你没用吗?