在开发组件库或者插件,常常会须要辨别多种环境构建,从而实现:

  • 提供各种体积版本:全量版、精简版、根底版等;
  • 提供各种环境版本:web 版、nodejs 版等等;
  • 提供各种标准版本:esm 标准版本、cjs 标准版本、UMD 标准版本等等。

那么如何可能不便实现下面性能呢?这种场景就适宜应用 Feature Flags,在构建过程中,通过开关的启用和敞开,对构建代码的过程进行动静设置,从而更好的实现 Tree Shaking。

Tree Shaking 是一种通过打消最终文件中未应用的代码来优化体积的办法。

本文会从 Vue 源码(版本号:3.0.11)中应用的 Feature Flags 进行构建的过程开始介绍,而后通过简略示例进行学习,最初介绍 rollup、webpack 和 Vite 中的实现。

一、什么是 Feature Flags

Feature Flag(又名 Feature Toggle、Flip等)是一种容许控制线上性能开启或者敞开的形式,通常会采取配置文件的形式来管制。

http://fex.baidu.com/blog/201...

能够了解为在代码中增加一个开关,当开关开启,则逻辑会执行上来,否则不会执行,通常代码表现形式为 if语句,举个简略示例:

const flags = true;const test = () => flags && console.log('Hello Feature Flags');

flagstrue则执行输入,否则不会。
当初咱们想管制日志会不会输入,只需扭转 flags的值即可,test办法逻辑不必批改。

能够将 Feature Flag 翻译成个性标记。

二、Vue 源码实现 Feature Flags

2.1 应用示例

从上一节对个性标记的介绍后,大家应该对其有点了解,接下来从 Vue3 源码中看一个应用示例:

// packages/compiler-core/src/errors.tsexport function defaultOnWarn(msg: CompilerError) {  __DEV__ && console.warn(`[Vue warn] ${msg.message}`)}

这里的 __DEV__就是一个 Feature Flag,当 __DEV__值为 true时,会输入前面的日志,否则不会输入。
在 Vue3 源码中还存在很多其余个性标记,比方:

  • __COMMIT__
  • __TEST__
  • __GLOBAL__
  • ...

还有很多,有趣味的小伙伴能够在 Vue3 源码中找找。

2.2 如何定义个性标记

下面只是带大家看了下源码中如何应用,那么接下来看看__DEV__这些个性标记是如何定义的。
Vue3 中应用了 @rollup/replace 依赖,实现构建时,替换文件中指标字符串内容,比方构建开发环境的包的过程中,将 __DEV__替换为 true
还是以下面示例代码为例介绍:

// 本地开发环境 __DEV__ 为 true,通过 @rollup/replace 依赖打包后如下:export function defaultOnWarn(msg: CompilerError) {  true && console.warn(`[Vue warn] ${msg.message}`)}// 生成环境中 __DEV__ 为 false,通过 @rollup/replace 依赖打包后如下:export function defaultOnWarn(msg: CompilerError) {}

构建后 defaultOnWarn办法内的 console.warn语句就被 Tree Shaking 移除掉了。

三、上手 Feature Flags

这一节通过将别离应用 rollup、webpack 和 Vite 实现三个 Feature Flags 的 Demo。其外围原理就是在构建阶段的时候,曾经明确的 Feature Flags 值的内容会被替换成具体的值,而后进行 Tree Shaking。

三个示例的全副代码能够到上面仓库查看:

首先咱们先创立一个 index.js文件,输出上面测试内容:

// index.js const name = 'pingan8787';const age = 18;const featureFlags = () => {    console.warn(name)    __DEV__ && console.log(name)}featureFlags();

咱们须要实现的指标是:当 __DEV__变量的值为 true 时,打包后的 index.js 将不蕴含 __DEV__ && console.log(name)这一行代码。
那么开始看看如何实现:

3.1 rollup 实现

在 rollup 中,须要应用 @rollup/replace 包实现构建时替换文本,咱们先装置它:

npm install @rollup/plugin-replace --save-dev

而后在 rollup.config.js中应用:

import replace from '@rollup/plugin-replace';export default {    input: 'index.js',    output: {        file: './dist/index.js',        format: 'cjs'    },    plugins: [        replace({            __DEV__: true        })    ]};

接下来通过 rollup打包命令,能够看到输入内容如下:

const name = 'pingan8787';const age = 18;const featureFlags = () => {    console.warn(name)    __DEV__ && console.log(name)}featureFlags();

能够看到 __DEV__true时代码并没有 Tree Shaking,再试试改成 false,输入如下:

'use strict';const name = 'pingan8787';const featureFlags = () => {    console.warn(name);};featureFlags();

这边 __DEV__ && console.log(name)就被移除了,实现 Tree Shaking。
照着雷同原理,再看看 webpack 和 Vite 的实现:

3.2 webpack 实现

webpack 中自带了 DefinePlugin能够实现该性能,具体能够看 DefinePlugin 文档 ,上面看看 webpack.config.js配置:

// webpack.config.jsconst path = require('path')const webpack = require('webpack')module.exports = {    entry: './index.js',    output: {        path: path.resolve(__dirname, 'dist'),        filename: 'index.js',    },    mode: 'production',    plugins: [        new webpack.DefinePlugin({            __DEV__: JSON.stringify(true),        })    ],};

因为这是应用 mode: 'production'模式,所以打包进去的代码会压缩:

(()=>{const n="pingan8787";console.warn(n),console.log(n)})();

能够看出 __DEV__曾经不存在,但 console.log(n)还存在,这时候把 __DEV__改成 false再看看打包后果:

console.warn("pingan8787");

只剩下这句,其余都被 Tree Shaking 掉了。

3.3 Vite 实现

Vite 默认也是反对自定义全局变量,实现该性能,能够看文档 define option。
通过 pnpm create vite创立一个简略 Vite 我的项目,并删除多余内容,并在 main.js中退出咱们的测试代码:

import { createApp } from 'vue'import App from './App.vue'const name = 'pingan8787';const age = 18;const featureFlags = () => {    console.warn(name)    __DEV__ && console.log(name)}featureFlags();createApp(App).mount('#app')

并且在 vite.config.js中设置 __DEV__

// vite.config.jsexport default defineConfig({  plugins: [vue()],  define: {    __DEV__: true  }})

而后执行 pnpm build构建我的项目,能够看到压缩后的代码还存在 __DEV__ && console.log(name)

接下来批改 __DEV__的值为 false,再从新打包,能够看到代码曾经被 Tree Shaking 了:

到这里咱们就应用 rollup、webpack 和 Vite 别离实现了一遍 Feature Flags 了。

四、总结

本文通过简略例子和 Vue3 源码,与大家介绍了 Feature Flags 的概念和简略的实现,最初别离应用 rollup、webpack 和 Vite 别离实现了一遍 Feature Flags。

在理论业务开发中,咱们能够通过设计各种 Feature Flags,让代码可能更好的进行 Tree Shaking。

参考文章

  1. Feature Flag 性能公布管制