通过 Vue3 研究框架的设计思路

最近在学习vue3,突然想研究一下源码,所以写了这篇文章。

作为框架设计者的话,一定要对框架的定位和方向拥有全局的把控,这样才能做好后续的模块设计和拆分。

命令式和声明式

从范式上来看,视图层框架通常分为命令式和声明式,它们各有优缺点。作为框架设计者,应该对两种范式都有足够的认知,这样才能做出正确的选择,甚至想办法汲取两者的优点并将其捏合。

命令式框架的一大特点就是关注过程。例如:jQuery。

声明式框架更加关注结果。例如:Vue。

Vue.js 的内部实现一定是命令式的,而暴露给用户的却更加声明式。

  • 命令式更加关注过程,命令式在理论上可以做到极致优化,但是用户要承受巨大的心智负担;
  • 声明式更加关注结果。能够有效减轻用户的心智负担,但是性能上有一定的牺牲,框架设计者要想办法尽量使性能损耗最小化。

性能与可维护性

命令式代码的性能 优于 声明式代码的性能。

既然在性能层面命令式代码是更好的选择,那么为什么 Vue 要选择声明式的设计方案呢?
原因就在于声明式代码的可维护性更强。在采用命令式代码开发的时候,我们需要维护实现目标的整个过程,包括要手动完成 DOM 元素的创建、更新、删除等工作。
而声明式代码展示的就是我们要的结果,看上去更加直观,至于做事儿的过程,并不需要我们关心,Vue 都为我们封装好了。
这就体现了我们在框架设计上要做出的关于可维护性与性能之间的权衡。在采用声明式提升可维护性的同时,性能就会有一定的损失,而框架设计者要做的就是:在保持可维护性的同时让性能损失最小化。

虚拟 DOM 的性能

声明式代码的更新性能消耗 = 找出差异的性能消耗 + 直接修改的性能消耗
因此,如果我们能够最小化找出差异的性能消耗,就可以让声明式代码的性能无限接近命令式代码的性能。而所谓的虚拟 DOM,就是为了最小化找出差异这一步的性能消耗而出现的。
首先应该清楚一件事:那就是采用虚拟 DOM 的更新技术的性能 理论上 不可能比原生 JavaScript 操作 DOM 更高。

image.png

运行时和编译时

  1. 纯运行时的 (没办法分析用户提供的内容)
  2. 运行时 + 编译时的 (可以分析用户提供的内容,看看哪些内容未来可能会改变,哪些内容永远不会改变,就可以做进一步的优化了)
  3. 纯编译时的 (也可以分析用户提供的内容,性能可能会更好,但是这种做法有损灵活性,即用户提供的内容必须编译后才能用)

Vue3 是一个由编译时和运行时两部分组成的框架:

编译时(Compile Time)

编译时包括将 Vue 组件的模板、指令等代码转换为 JavaScript 代码的过程。在 Vue 3 中,编译时会将模板编译为渲染函数,这意味着模板中的代码会在构建时被转换为 JavaScript 函数。这种编译的过程会在开发阶段进行,以提高运行时的性能。
在编译时,Vue 3 还会进行一些静态分析,并生成一些优化指令,以提高组件的渲染效率。

运行时(Runtime)

运行时包括组件实例化、数据绑定、事件处理等在浏览器中实际运行时所执行的代码。
Vue 3 的运行时主要负责执行编译时生成的渲染函数,将组件的状态映射到 DOM,并且负责响应用户的交互行为。

通过将编译时和运行时分离,Vue 3 可以在开发阶段进行更多的优化,以提高运行时的性能。这种分离也使得 Vue 3 更加灵活,允许开发者在编译时进行一些静态的优化和检查,以减少运行时的开销,提高整体的性能。

框架设计核心要素

提升用户的开发体验

提供友好的警告信息至关重要,这有助于开发者快速定位问题,因为大多数情况下“框架”要比开发者更清楚问题出在哪里,因此在框架层面抛出有意义的警告信息是非常必要的。

控制框架代码的体积

比如:在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积。

框架要做到良好的 Tree-Shaking

Tree-Shaking 指的就是消除那些永远不会被执行的代码。

Tree-Shaking 依赖 ESM 的静态结构(模块必须是 ESM(ES Module))。

合理使用 /#PURE/ 注释,主动触发 Tree-Shaking。

框架应该输出怎样的构建产物

  • format: ‘iife’,使用 script 标签直接引入产物
  • format: ‘esm’,输出 ESM 格式的资源
  • format: ‘cjs’,服务端渲染,CommonJS 模块

特性开关

对于用户关闭的特性,我们可以利用 Tree-Shaking 机制让其不包含在最终的资源中。
该机制为框架设计带来了灵活性,可以通过特性开关任意为框架添加新的特性,而不用担心资源体积变大。
当框架升级时,我们也可以通过特性开关来支持遗留 API,这样新用户可以选择不使用遗留 API,从而使最终打包的资源体积最小化。

错误处理

框架的错误处理做得好坏直接决定了用户应用程序的健壮性,同时还决定了用户开发应用时处理错误的心智负担。

框架需要为用户提供统一的错误处理接口,这样用户可以通过注册自定义的错误处理函数来处理全部的框架异常。

vue3 设计思路

声明式地描述 UI

Vue.js 3 来说,相应的解决方案是:

使用与 HTML 标签一致的方式来描述 DOM 元素,例如描述一个div 标签时可以使用 <div></div>
使用与 HTML 标签一致的方式来描述属性,例如 <div id="app"></div>
使用 : 或 v-bind 来描述动态绑定的属性,例如 <div:id="dynamicId"></div>
使用 @ 或 v-on 来描述事件,例如点击事件<div @click="handler"></div>
使用与 HTML 标签一致的方式来描述层级结构,例如一个具有span 子节点的 div 标签<div><span></span></div>

Vue.js 是一个声明式的框架。声明式的好处在于,它直接描述结果,用 户不需要关注过程。

Vue.js 采用模板的方式来描述 UI,但它同样支持使用虚拟 DOM 来描述 UI。虚拟 DOM 要比模板更加灵活,但模板要比虚拟 DOM 更加直观。

渲染器

渲染器的作用是,把虚拟 DOM 对象渲染为真实 DOM 元素。

渲染器的工作原理是,递归地遍历虚拟 DOM 对象,并调用原生 DOM API 来完成真实 DOM 的创建。

渲染器的精髓在于后续的更新,它会通过 Diff 算法找出变更点,并且只会更新需要更新的内容。

组件的本质

组件就是一组 DOM 元素的封装, 它可以是一个返回虚拟 DOM 的函数,也可以是一个对象,但这个对象下必须要有一个函数用来产出组件要渲染的虚拟 DOM。

模板的工作原理

Vue.js 的模板会被一个叫作编译器的程序编译为渲染函数,无论是使用模板还是直接手写渲染函数,对于一个组件来说,它要渲染的内容最终都是通过渲染函数产生的,然后渲染器再把渲染函数返回的虚拟 DOM 渲染为真实 DOM,这就是模板的工作原理,也是 Vue.js 渲染页面的流程。

Vue.js 是各个模块组成的有机整体

组件的实现依赖于渲染器,模板的编译依赖于编译器,并且编译后生成的代码是根据渲染器和虚拟 DOM 的设计决定的,因此 Vue.js 的各个模块之间是互相关联、互相制约的,共同构成一个有机整体。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值