如何在不看源码情况下学petite-vue源码

开发 前端
本文介绍了复杂框架源码的阅读办法—即从抽象到具体。从mount时与update时的调用栈推导出整体工作流程,从整体工作流程中发现核心知识— 响应式更新。

[[410856]]

大家好,我是卡颂。

周末没啥事,准备找个优秀且代码量不多的库学习下。最终选择了最近发布的petite-vue,原因如下:

  • 代码量少(只有5.8kb),且源码模块化程度高(相比于React),易读
  • 基于Vite构建,执行yarn dev就能开始调试源码
  • 没有虚拟DOM、编译时方案,可以作为读Vue源码的铺垫
  • 底层的响应式更新原理同样适用于Mobx、SolidJS等库,一次阅读多份收获
[[410857]]

但是周末时间这么宝贵,而且我都4年没用过Vue了,如何才能高效学习源码呢?

最好是「在不看源码的情况下把源码学了」。

接下来,我就以petite-vue为例为大家示范学源码的正确姿势。

怎么快怎么来

可以将petite-vue理解为:用真实DOM取代Vue模版的简易Vue。

比如如下Demo:

  1. <script type="module"
  2.   import { createApp } from '../src' 
  3.   createApp({count: 0}).mount() 
  4. </script> 
  5.  
  6. <div v-scope> 
  7.   <button @click="count++">add 1</button> 
  8.   <p>{{count}}</p> 
  9. </div>   

div及其子孙节点是真实的DOM标签,所以页面初始化时如下:

图片

接着执行如下代码,完成petite-vue初始化:

  1. createApp({count: 0}).mount() 

此时页面:

图片

读框架源码切忌一上手就从入口函数一路调试,很容易就懵逼了。正确的方式是像剥洋葱一样一层一层剥开:

[[410860]]

这好像是大蒜?

所以,让我们先从Performance面板看看「首屏渲染」的调用栈:

调用栈大体分为蓝框、红框两部分,先看左边蓝框部分:

图片

通过createContext与reactive关键词判断大概是创建响应式上下文。至于响应式的含义,我们还不清楚。

接着看右边红框部分:

图片

从调用栈深度、页面渲染的效果我们猜测,这部分做的工作包括:

  • 遍历DOM
  • 完成数据与视图的双向绑定
  • 初始化渲染

接下来,我们来验证猜想。

注意,到目前为止,我们一行源码都还没看

验证遍历DOM

调用栈中walk与walkChildren被调用多次,大概率他们就是具体遍历工作执行的方法,让我们确认下。

在源码walk方法中打上log:

  1. export const walk = (node: Node, ctx: Context): ChildNode | null | void => { 
  2.   console.log('walk', node); 
  3.   // ...   

排除换行符"\n "对应的文本节点,打印顺序如下:

  1. walk div 
  2. walk <button>add 1</button> 
  3. walk "add 1" 
  4. walk <p>0</p> 
  5. walk "0" 

从打印结果看,这是个「深度优先遍历」(如果有子节点就遍历子节点,没有子节点就遍历兄弟节点)

显然,petite-vue mount时采用「深度优先遍历」,并对遍历到的每个与「上下文状态」相关的DOM节点进行处理。

在Demo中,上下文包含状态{count: 0}:

  1. createApp({count: 0}).mount() 

在遍历后<p>{{count}}</p>变为<p>0</p>。

确定双向绑定的粒度

接下来我们需要确认双向绑定的作用范围,即:

触发更新后,多大范围的DOM会被重新遍历并执行相应DOM操作?

打开Performance后,点击<button>add 1</button>触发更新:

可以看到,没有任何walk、walkChildren(或类似遍历过程),只调用了reactiveEffect一个方法就更新了DOM。

这意味着mount时的深度优先遍历建立了状态与更新DOM的方法之间一一对应的关系。

因为对应关系确定了,就不再需要额外的遍历过程确定需要变化的DOM。

当更新状态后,只需要找到与他有关系的更新DOM的方法执行就行。

比如:将count状态与如下函数建立联系:

  1. function setCount(value) { 
  2.   p.textContent = value; 

每当count变化后调用setCount(count)就能更新p对应DOM。

所以,petite-vue的工作原理,主要包括两点:

  1. mount时深度优先遍历DOM,对有状态的DOM(比如<p>{{count}}</p>)建立状态与更新DOM的方法之间一一对应的关系
  2. update时找到该状态对应的更新DOM的方法并执行

可以看到,即使不深入源码,也能大体了解工作流程。

如果你想更进一步,比如了解「关系是如何建立的」(涉及到「响应式更新」),那么就需要深入源码了。

这里推荐Vue Mastery的Vue 3 Reactivity课程,可以补齐「响应式更新」这块知识。

总结

本文介绍了复杂框架源码的阅读办法 —— 即从抽象到具体。

  1. 从mount时与update时的调用栈推导出整体工作流程
  2. 从整体工作流程中发现核心知识 —— 响应式更新

当掌握整体工作流程与响应式更新后,再阅读自己感兴趣的部分才不至于陷入庞大的代码量中。

你,学废了么?

 

责任编辑:姜华 来源: 魔术师卡颂
相关推荐

2022-08-24 15:08:19

模型数据技术

2020-08-11 10:25:38

数据成本数据大数据

2023-03-02 08:19:43

不加锁程序实时性

2021-12-20 07:58:59

GitHub源码代码

2018-12-07 11:15:20

设置Windows 10命令

2018-07-31 16:20:12

Windows 10Windows密码

2017-07-05 18:25:00

2022-11-15 11:02:21

2019-09-03 09:55:48

DevOps云计算安全

2020-10-13 09:27:18

电脑Windows软件

2019-09-10 08:00:00

微软Account网站浏览器

2021-02-19 09:44:00

云计算IT服务IT团队

2010-04-06 10:00:52

CentOS系统

2023-04-07 15:01:26

数据中心运营商

2018-06-27 23:10:12

数据中心IT管理运维

2021-09-22 07:57:23

Vue3 插件Vue应用

2022-09-05 10:01:19

VueReact

2019-07-26 11:51:20

云计算IT系统

2021-11-12 21:15:47

前端技术编程

2017-12-05 13:25:40

PHP开发服务器内存
点赞
收藏

51CTO技术栈公众号