3
头图

When developing a component library or plug-in, it is often necessary to distinguish multiple environment builds to achieve:

  • Provide various volume versions: full version, lite version, basic version, etc.;
  • Provide various environment versions: web version, nodejs version, etc.;
  • Various spec versions are available: esm spec version, cjs spec version, UMD spec version, etc.

So how can we easily realize the above functions? This scenario is suitable for using Feature Flags. During the construction process, the process of building code is dynamically set by enabling and disabling the switch, so as to better realize Tree Shaking.

Tree Shaking is a way to optimize volume by eliminating unused code in the final file.

This article will start with the construction process of Feature Flags used in the Vue source code (version number: 3.0.11), then learn through simple examples, and finally introduce the implementation in rollup, webpack and Vite.

The code address of this article: https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Vue/Source/FeatureFlags/

1. What are Feature Flags

Feature Flag (also known as Feature Toggle, Flip, etc.) is a way to allow control of online features on or off, usually by means of configuration files.

http://fex.baidu.com/blog/2014/07/feature-flag/

It can be understood as adding a switch to the code. When the switch is turned on, the logic will be executed, otherwise it will not be executed. Usually the code expression is if statement, for a simple example:

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

When flags is true , the output will be executed, otherwise it will not.
Now we want to control whether the log will be output or not, just change the value of flags , and the method logic of test does not need to be modified.

😺 Feature Flag can be translated into feature flag.

2. Vue3 source code implements Feature Flags

2.1 Example of use

After the introduction of the feature flag in the previous section, you should have a little understanding of it. Next, let's look at an example of use from the Vue3 source code:

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

The __DEV__ here is a Feature Flag. When the value of __DEV__ is true , the following log will be output, otherwise it will not be output.
There are many other feature flags in the Vue3 source code, such as:

  • __COMMIT__
  • __TEST__
  • __GLOBAL__
  • ...

There are many more, and interested friends can find them in the Vue3 source code.

2.2 How to define feature flags

The above is just to show you how to use it in the source code, then let's see how the feature flags of __DEV__ are defined.
Vue3 uses the @rollup/replace dependency. When building, replace the target string content in the file. For example, when building the package of the development environment, replace __DEV__ with true .
Or take the example code above as an example:

// 本地开发环境 __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) {
}

After the build, the console.warn statement in the defaultOnWarn method was removed by Tree Shaking.

3. Get started with Feature Flags

In this section, we will use rollup, webpack and Vite to implement the demos of three Feature Flags respectively. The core principle is that during the construction phase, the content of the specified Feature Flags value will be replaced with a specific value, and then Tree Shaking will be performed.

The full code of the three examples can be viewed in the following repository:

First, let's create a index.js file and enter the following test content:

// index.js 

const name = 'pingan8787';
const age = 18;

const featureFlags = () => {
    console.warn(name)
    __DEV__ && console.log(name)
}

featureFlags();

The goal we need to achieve is: when the value of the __DEV__ variable is true , the packaged index.js will not contain the line of code __DEV__ && console.log(name) .
So start to see how to implement:

3.1 rollup implementation

In rollup, you need to use the @rollup/replace package to replace text at build time, let's install it first:

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

Then in rollup.config.js use:

import replace from '@rollup/plugin-replace';

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

Next, through the rollup packaging command, you can see the output as follows:

const name = 'pingan8787';
const age = 18;

const featureFlags = () => {
    console.warn(name)
    __DEV__ && console.log(name)
}

featureFlags();

It can be seen that when __DEV__ is true , the code does not have Tree Shaking, and then try changing it to false . The output is as follows:

'use strict';

const name = 'pingan8787';

const featureFlags = () => {
    console.warn(name);
};

featureFlags();

Here __DEV__ && console.log(name) has been removed to implement Tree Shaking.
Following the same principle, look at the implementation of webpack and Vite:

3.2 webpack implementation

Webpack comes with DefinePlugin to implement this function. For details, please refer to the DefinePlugin document . Let’s take a look at webpack.config.js configuration:

// webpack.config.js

const 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),
        })
    ],
};

Because this is using mode: 'production' mode, the packaged code will be compressed:

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

It can be seen that __DEV__ no longer exists, but console.log(n) still exists. At this time, change __DEV__ to false and look at the packaging result:

console.warn("pingan8787");

Only this sentence is left, and the others have been dropped by Tree Shaking.

3.3 Vite Implementation

Vite also supports custom global variables by default, to achieve this function, you can see the document define option .
Create a simple Vite project with pnpm create vite , delete the redundant content, and add our test code in 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')

and set vite.config.js in __DEV__ :

// vite.config.js

export default defineConfig({
  plugins: [vue()],
  define: {
    __DEV__: true
  }
})

Then execute pnpm build to build the project, you can see that __DEV__ && console.log(name) still exists in the compressed code.

Next, modify the value of __DEV__ to false , and then repackage it. You can see that the code has been Tree Shaking:

3.4 Attention

If the editor prompts that the __DEV__ variable does not exist, you can create a new type declaration file to solve it, for example, create a new global.d.ts file to define the content as follows:

// Global compile-time constants
declare var __DEV__: boolean

So far, we have implemented Feature Flags using rollup, webpack and Vite respectively.

4. Summary

This article introduces the concept and simple implementation of Feature Flags through simple examples and Vue3 source code, and finally implements Feature Flags using rollup, webpack and Vite respectively.

In actual business development, we can design various Feature Flags to make the code better for Tree Shaking.

Reference article

  1. Feature Flag Feature Release Control

pingan8787
3.2k 声望4.1k 粉丝

🌼 前端宝藏小哥哥