在设计框架时,框架会给用户提供诸多特性(或功能),例如我们提供 A、B、C 三个特性给用户,同时还提供了 a、b、c 三个对应的特性开关, 用户可以通过设置 a、b、c 为 true 或 false 来代表开启或关闭对应的特性,这将会带来很多益处。
对于用户关闭的特性,我们可以利用 Tree-Shaking 机制让其不包含在最终的资源中。
该机制为框架设计带来了灵活性,可以通过特性开关任意为框架添加新的特性,而不用担心资源体积变大。 同时,当框架升级时,我们也可以通过特性开关来支持遗留 API,这样新用户可以选择不使用遗留 API,从而使最终打包的资源体积最小化。
那怎么实现特性开关呢?其实很简单,原理和上文提到的 __DEV__
常量一样,本质上是利用 rollup.js 的预定义常量插件来实现。 拿 Vue.js 3 源码中的一段 rollup.js 配置来说:
{
__FEATURE_OPTIONS_API__: isBundlerESMBuild ? `__VUE_OPTIONS_API__` : true,
}
其中 __FEATURE_OPTIONS_API__
类似于 __DEV__
。在 Vue.js 3 的源码中搜索,可以找到很多类似于如下代码的判断分支:
// support for 2.x options
if (__FEATURE_OPTIONS_API__) {
currentInstance = instance
pauseTracking()
applyOptions(instance, Component)
resetTracking()
currentInstance = null
}
当 Vue.js 构建资源时,如果构建的资源是供打包工具使用的(即带有 -bundler 字样的资源),那么上面的代码在资源中会变成:
// support for 2.x options
if (__VUE_OPTIONS_API__) { // 注意这里
currentInstance = instance
pauseTracking()
applyOptions(instance, Component)
resetTracking()
currentInstance = null
}
其中 __VUE_OPTIONS_API__
是一个特性开关,用户可以通过设置 __VUE_OPTIONS_API__
预定义常量的值来控制是否要包含这段代码。 通常用户可以使用 webpack.DefinePlugin 插件来实现:
// webpack.DefinePlugin 插件配置
new webpack.DefinePlugin({
__VUE_OPTIONS_API__: JSON.stringify(true) // 开启特性
})
最后详细解释 __VUE_OPTIONS_API__
开关有什么用。在 Vue.js 2 中,我们编写的组件叫作组件选项 API:
export default {
data() {}, // data 选项
computed: {}, // computed 选项
// 其他选项
}
但是在 Vue.js 3 中,推荐使用 Composition API 来编写代码,例如:
export default {
setup() {
const count = ref(0)
// 相当于 Vue2 中的 computed 选项
const doubleCount = computed(() => count.value * 2)
}
}
但是为了兼容 Vue.js 2,在 Vue.js 3 中仍然可以使用选项 API 的方式编写代码。但是如果明确知道自己不会使用选项 API, 就可以使用 __VUE_OPTIONS_API__
开关来关闭该特性,这样在打包的时候 Vue.js 的这部分代码就不会包含在最终的资源中,从而减小资源体积。