Vue.js设计与实现(1-2)

34 阅读5分钟

框架设计概览#框架设计的核心要素

一、框架设计的核心要素:提升用户的开发体验

在框架设计和开发过程中,提供友好的警告信息至关重要:不仅能够帮助用户快速定位问题,节省用户的时间,还能够让框架收获良好的口碑,让用户认可框架的专业性。

  • warn函数,底层调用的是 console.warn() 函数,需要尽可能提供有用的信息,会需要收集当前发生错误的组件栈信息。

浏览器允许我们编写自定义得 formatter,从而自定义输出形式:友好化输出信息

  • 以 Chrome 为例,打开 DevTools 的设置,然后勾选 “Console” → “Enable custom formatters”,如下图: image.png 然后刷新浏览器并查看控制台,会发现输出内容变得非常直观,效果等价于直接打印输出对应标识的.value的值,如下图: image.png

二、框架设计的核心要素:控制框架代码的体积

在实现同样功能的情况下,用的代码越少越好:体积会越小,最后浏览器加载资源的时间也就越少。

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> 标签引入框架并使用

  1. IIFE格式资源:format: 'iife'

IIFE:Immediately Invoked Function Expression,立即调用的函数表达式。

<script src="/path/to/vue.js"></script>
  1. 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.jswebpack 等打包工具使用的:__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.jswebpack 等打包工具中通过 __VUE_OPTIONS_API__ 来适配

六、框架设计的核心要素:错误处理

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

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

// main.js

app.config.errorHandler = () => {
  // 错误处理程序
}

七、框架设计的核心要素:良好的TypeScript支持

使用 TS 的好处有很多:

  • 代码即文档
  • 编辑器自动提示
  • 一定程度上能够避免低级 bug
  • 代码的可维护性更强

使用 TS 编写代码与对 TS 类型支持友好是两件事。