[Vuejs] 1247 - how does vue3 implement Feature Flags?

Posted by bonzie on Sat, 05 Mar 2022 05:56:19 +0100

When developing component libraries or plug-ins, it is often necessary to distinguish between multiple environments to achieve:

  • Various "volume" versions are available: full volume version, simplified version, basic version, etc;
  • Provide various "environment" versions: web version, nodejs version, etc;
  • Various "specification" versions are provided: esm specification version, cjs specification version, UMD specification 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 turning on and off 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 process of building Feature Flags used in Vue source code (version number: 3.0.11), then learn through simple examples, and finally introduce the implementation in rollup, webpack and Vite.

1, What is Feature Flags

Feature flag (also known as Feature Toggle, Flip, etc.) is a way to control the opening or closing of online functions, which is usually controlled by 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 on, the logic will be executed, otherwise it will not be executed. The code is usually expressed as an if statement, for 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 be. Now we want to control whether the log will be output. Just change the value of flags, and the test method logic does not need to be modified.

😺 Feature Flag can be translated into "Feature Flag".

Address of all following sample codes: https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Vue/Source/FeatureFlags/

2, Vue source code implements Feature Flags

2.1 use examples

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

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

Here__ DEV__ It's a Feature Flag, when__ DEV__ When the value is true, the subsequent 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 other interested partners that can be found in the Vue3 source code.

2.2 how to define characteristic marks

The above just shows you how to use it in the source code, so let's take a look__ DEV__ How are these property flags defined. [@ rollup / replace] is used in Vue3( https://github.com/rollup/plugins/tree/master/packages/replace )Dependency. When building, replace the content of the target string in the file. For example, in the process of building the package of the development environment, the__ DEV__ Replace with true. Let's take the above example code as an example:

// Local development environment__ DEV__  It is true. After @ rollup/replace dependency packaging, it is as follows:
export function defaultOnWarn(msg: CompilerError) {
  true && console.warn(`[Vue warn] ${msg.message}`)
}

// In the build environment__ DEV__  It is false. After @ rollup/replace dependency packaging, it is as follows:
export function defaultOnWarn(msg: CompilerError) {
}

After construction, the console in the defaultOnWarn method The warn statement is removed by Tree Shaking.

3, Get started with Feature Flags

In this section, the Demo of the three Feature Flags will be implemented by using rollup, webpack and Vite respectively. The core principle is that in the construction stage, the content of the defined Feature Flags value will be replaced with a specific value, and then Tree Shaking. All the codes of the three examples can be viewed in the following warehouse:

First, we create an index JS file, 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__ DEV__ When the value of the variable is true, the packaged index JS will not contain__ DEV__ && console. Log (name). So let's start by looking at how:

3.1 rollup implementation

In rollup, you need to use [@ rollup / replace]( https://github.com/rollup/plugins/tree/master/packages/replace )Replace the text when building the package. Let's install it first:

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

Then in 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
        })
    ]
};

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

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

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

featureFlags();

Can see__ DEV__ When it is true, the code does not have Tree Shaking. Try to change it to false, and the output is as follows:

'use strict';

const name = 'pingan8787';

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

featureFlags();

This way__ DEV__ && console. Log (name) is removed and Tree Shaking is implemented. Following the same principle, let's look at the implementation of webpack and Vite:

3.2 webpack implementation

DefinePlugin is built into webpack to realize this function. See the DefinePlugin document for details. 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 the mode: 'production' mode is used, 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__ DEV__ Change it to false and look at the packaging results:

console.warn("pingan8787");

Only this sentence is left, and the others are lost by Tree Shaking.

3.3 Vite implementation

Vite also supports custom global variables by default. To realize this function, you can see the document [define option]( https://github.com/vitejs/vite/blob/a4133c073e640b17276b2de6e91a6857bdf382e1/src/node/config.ts#L72 -L76) . Create a simple vite project through pnpm create vite, delete the redundant content, and in main Add our test code to 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 in vite config. JS__ DEV__:

// vite.config.js

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

Then execute pnpm build to build the project, and you can see that the compressed code still exists__ DEV__ && console. log(name).

Next modify__ DEV__ If the value of is false and repackaged, you can see that the code has been tree shaken:

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

4, Summary

Through simple examples and Vue3 source code, this paper introduces the concept and simple implementation of Feature Flags. Finally, Feature Flags are implemented by roll, webpack and Vite respectively.

In actual business development, we can design various Feature Flags to make the code better able to carry out tree shaping.