框架设计概览#框架设计的核心要素
一、框架设计的核心要素:提升用户的开发体验
在框架设计和开发过程中,提供友好的警告信息至关重要:不仅能够帮助用户快速定位问题,节省用户的时间,还能够让框架收获良好的口碑,让用户认可框架的专业性。
- warn函数,底层调用的是
console.warn()
函数,需要尽可能提供有用的信息,会需要收集当前发生错误的组件栈信息。
浏览器允许我们编写自定义得 formatter,从而自定义输出形式:友好化输出信息。
- 以 Chrome 为例,打开 DevTools 的设置,然后勾选 “Console” → “Enable custom formatters”,如下图:
然后刷新浏览器并查看控制台,会发现输出内容变得非常直观,效果等价于直接打印输出对应标识的.value的值,如下图:
二、框架设计的核心要素:控制框架代码的体积
在实现同样功能的情况下,用的代码越少越好:体积会越小,最后浏览器加载资源的时间也就越少。
Vue.js 在输出资源的时候,会输出两个版本,其中一个用于开发环境,如:vue.global.js(包含必要的警告信息),另一个用于生产环境,如:vue.global.prod.js(不包含警告信息)。即:通过构建工具设置预定义的常量 __DEV__
—— 在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境代码的体积。
三、框架设计的核心要素:框架要做到良好的Tree-Shaking
Tree-Shaking
指的就是消除那些永远不会被执行的代码,也就是排除 dead code
。
要实现 Tree-Shaking
,模块必须是 ESM(ES Module)
。因为 Tree-Shaking
依赖 ESM
的静态结构。
如果一个函数调用会产生副作用,就不能将其移除。
副作用:当调用函数的时候,会对外部产生影响。例如:修改了全局变量。
到底会不会产生副作用,只有代码真正运行的时候才知道:JavaScript本身是动态语言。
rollup.js
有提供一个机制,明确代码不会产生副作用,可以移除:/*#__PURE__*/
。
import { foo } from './utils'
/*#__PURE__*/ foo()
注释代码 /*#__PURE__*/
明确告诉 rollup.js
,对于 foo 函数的调用不会产生副作用,可以放心对其进行 Tree-Shaking
。
在编写框架的时候,需要合理使用 /*#__PURE__*/
注释。
通常,产生副作用的代码都是模块内函数的顶级调用。
foo() // 顶级调用
function bar() {
foo() // 函数内调用
}
在 Vue.js 3的源码中,基本都是在一些顶级调用的函数上使用 /*#__PURE__*/
注释。而该注释不仅仅作用于函数,还可以应用于任何语句上。
四、框架设计的核心要素:框架应该输出怎样的构建产物
Vue.js 的构建产物,除了有环境上(__DEV__
)的区分之外,还会根据使用场景的不同而输出其他形式。
不同类型的产物一定有对应的需求背景。
4.1 在 HTML
页面中使用 <script>
标签引入框架并使用
- IIFE格式资源:
format: 'iife'
IIFE:Immediately Invoked Function Expression,立即调用的函数表达式。
<script src="/path/to/vue.js"></script>
- ESM格式资源:
format: 'es'
<script type="module" src="/path/to/vue.esm-browserjs"></script>
-browser
v.s-bundler
无论是 rollup.js
还是 webpack
,在寻找资源时,如果 package.json
中存在 module
字段,会优先使用 module
字段指向的资源来代替 main
字段指向的资源。
{
"main": "index.js",
"modules": "dist/vue.runtime.esm-bundler.js"
}
- 带有
-bundler
字样的ESM
资源是给rollup.js
或webpack
等打包工具使用的:__DEV__
常量通过process.env.NODE_ENV !== 'production'
来适配,支持用户通过webpack
配置自定决定构建资源的目标环境; - 带有
-browser
字样的ESM
资源是直接给<script type="module">
使用的:__DEV__
常量会根据环境适配为字面量true
—— 开发环境,或false
—— 生产环境。
4.2 在 Node.js
中通过 require
语句引用资源
服务端渲染:当进行服务端渲染时,
Vue.js
的代码是在Node.js
环境中运行的,而非浏览器环境。
- cjs(CommonJS)格式资源:
format: 'cjs'
const Vue = require('vue')
五、框架设计的核心要素:特性开关
出于灵活性和兼容性的考虑。
- 对于用户关闭的特性,可以利用
Tree-Shaking
机制让其不包含在最终的资源中。 - 可以通过特性开关任意为框架添加新的特性,而不用担心资源体积变大。同时,当框架升级时,可以通过特性开关来支持遗留
API
,从而使最终打包的资源体积最小化。
利用 rollup.js
的预定义常量插件来实现:__FEATURE_OPTIONS_API__
。在 rollup.js
或 webpack
等打包工具中通过 __VUE_OPTIONS_API__
来适配
六、框架设计的核心要素:错误处理
框架错误处理机制的好坏直接决定了用户应用程序的健壮性,还决定了用户开发时处理错误的心智负担。
框架需要为用户提供统一的错误处理接口,这样用户可以通过注册自定义的错误处理函数来处理全部的框架异常。
// main.js
app.config.errorHandler = () => {
// 错误处理程序
}
七、框架设计的核心要素:良好的TypeScript支持
使用 TS
的好处有很多:
- 代码即文档
- 编辑器自动提示
- 一定程度上能够避免低级
bug
- 代码的可维护性更强
使用 TS
编写代码与对 TS
类型支持友好是两件事。