第二章 框架设计的核心要素
2.1 控制框架代码的体积
我们在开发过程中,可以在控制台看见一些警告信息/报错信息等,方便我们快速定位问题,那么这些信息是从哪里来的呢?
if (__DEV__ && !res) {
warn(
`Failed to mount app: mount target selector "${container}" returned null.`
)
}
__DEV__是一个布尔值,在开发环境中时为true,在生产环境中为false(在构建资源的时候会被移出),这样就做到了在开发环境中为用户提供友好的警告信息的同时,不会增加生产环境的代码体积。
框架的大小是衡量框架的标准之一。
2.2 Tree-Shaking (摇树)
webpack会在生产环境打包时“摇”掉无用的,死的代码(dead code)。
Vue.js 中的很多内置组件,比如Transtion组件,如果在项目中根本不用这个组件,那么我们最终构建资源是不需要包含这部分代码的。如何做到?——Tree-Shaking
什么是Tree-Shaking,指的就是消除那些永远不会被执行的代码。
想要实现Tree-Shaking,模块需要满足ESM规范。
- import只能作为顶层语句出现
- import的模块名只能是字符串常量
- import不可变的
书中以rollup.js为例说明,在此我们以webpack为例说明:
新建src文件夹,新建utils.js 和 index.js。下面是两个文件的相关内容:
// utils.js
export const tree1 = (obj) => {
console.log('this tree1')
obj && obj.foo
}
export const tree2 = () => {
console.log('this tree2')
}
// index.js
import { tree1 } from './utils'
console.log(tree1())
console.log('hello world')
打包结果main.js
(()=>{"use strict";console.log('this tree1');var o;console.log(void o),console.log("hello world")})();
我们在index中引用tree1函数,打包后发现main.js文件中不包含tree2函数,是因为tree-shaking起作用了,由于我们没有使用到tree2,他作为dead code被删除了。
从代码观察发现,tree1的执行也没什么必要,因为只是读取了对象的值,但却没有把它作为dead code删除,这就涉及到了tree-shaking的第二个关键点——副作用。如果一个函数调用会产生副作用,那么就不能把它移除。
副作用就是——当调用函数的时候会对外部产生影响,例如修改了全局变量,可能从上述代码中难以看出,但是如果obj是通过Proxy创建的代理的对象,那么当读取属性的时触发get(),有可能会在这里有其他数据修改或者副作用,具体情况就要在代码真正运行的时候才知道了。
但这时也会有新的问题,就是有些模块导入但是没有使用,webpack还是对一些副作用的模块进行了分析和评估,这会导致打包的是时间增加,Tree-Shaking 本身基于 ESM,并且 JavaScript 是一门动态语言,通过纯静态分析的手段进行 Tree-Shaking 难度较大,所以我们可以通过webpack的sideEffects配置项来设置。
// 所有文件都没有副作用,全都可以 tree-shaking,即告知 webpack,它可以安全地删除未用到的 export
"sideEffects": false
//所有文件都有副作用,全都不可 tree-shaking
"sideEffects": true
// 除了数组中包含的文件外有副作用,所有其他文件都可以 tree-shaking,但会保留符合数组中条件的文件
{
"sideEffects": [
"*.css",
"*.less"
]
2.3 特性开关
对于用户关闭的特性,我们可以利用 Tree-Shaking 机制让其不包含在最终的资源中。
那怎么实现特性开关呢?其实很简单,原理和上文提到的__DEV__常量一样,通过判断__VUE_OPTIONS_API__是否为true进而判断是否打开该特性开关。
const app = createApp({
// 在此处定义组件选项
}, {
// 使用 Composition API
__VUE_OPTIONS_API__: false
})
如果明确知道不使用选项API(例如computed/data/watch等),就可以通过设置__VUE_OPTIONS_API__: false关闭特性开关,最终打包时不会包含在最终的资源中,从而减小体积。
该机制为框架设计带来了灵活性,可以通过特性开关任意为框架添加新的特性,而不用担心资源体积变大。
总结
这一章主要介绍了开发体验、tree-shaking对框架设计的重要性,提供良好的警告信息可以有助于开发者快速定位问题。tree-shaking可以用于打包文件的时候移除dead code,根据不同的环境构建不同的资源产物,从而减少代码打包的体积。此外还给用户提供了各种特性开关,__VUE_OPTIONS_API__可以用于设置Vue.js3中option API是否开启。