Vite multi environment configuration: make the project have higher customization ability

Posted by MmmVomit on Tue, 08 Mar 2022 10:40:51 +0100

Business background

In recent years, with the development of front-end engineering architecture, front-end projects can also have module capabilities such as back-end engineering. As the saying goes, "the greater the capability (the greater), the greater the responsibility (the greater)", the current front-end engineering should not only meet the business needs, but also be accompanied by more complex environmental adaptation problems, such as:

  • The api will vary according to the domain name environment;
  • The packaging strategies of online environment and test environment are different, "such as isolating sourceMap and shielding vue|react devtools on the line...";
  • The front-end spa component makes different logic according to different environments;

The boss is eager to put all application terminals into one project, and what micro front-end and uniapp multi terminal solutions are coming one after another... However, no matter what scheme is, it is inseparable from one core point: environmental variables and multi environment adaptation. So today, let's talk about how to realize a set of multi environment adaptation scheme with strong expansion ability in Vite.

Business form of multi environment scenario

Let's first understand how the front-end engineering architecture process is required in multiple environments?

As shown in the figure above, during project start-up / Construction:

  1. Environment variable injection: generally through the command parameter mode, which can be found in package JSON configuration;
  2. Multimodal file: Vite reads the configuration file according to the environment variables and extracts the file parameters for feature differentiation. This is also known as Vite's environment modelļ¼›
  3. Environment collector: simply understood as a function, what it does is to integrate the characteristic parameters of the second step into one place and do some specific logic, and then generate the final parameters of the client through the plug-in and spit them out;
  4. Customization of client environment differences: the client (that is, the front-end files such as. vue,. ts and. tsx in the project) obtains the environment parameters and makes some specific differentiation logic;
  5. Build and release: then, the project is packaged according to the environmental characteristic files produced in the above steps, and finally pushed to the server to complete the production of the whole front-end project.

The above is the general process. Next, we will subdivide each step to explain how to realize it. For your understanding, the author specially opened a new meeting this time GitHub project To store all the implementation code of this article. Interested students can take it down and practice it šŸŒ¹šŸŒ¹.

Implementation of Vite multi Environment Scheme

Multimodal file configuration

Custom environment variables

Vite passed Multimode To configure the characteristic environment variables in different startup scenarios, you can create a custom mode file, as follows:

The project has created four modes, which are compatible with release, beta, test and local environment. Each mode has its own specific environment variables, such as env. The internal of local is as follows:

# .env._local
# Transparent client parameters
VITE_NODE_ENV=local
VITE_OWNER=Tom
VITE_POSITION=Guangzhou, Tianhe

# Private parameters, which can only be obtained in vite server,
# If your project contains such sensitive variables. Files should be added to your gitignore to avoid them being checked in by git.
MODE_KEY=PRIVATE_KEY_LOCAL

According to the agreed rules of VITE, only "VITE_" The first variable will be captured on the client. The capture method is: import meta. env. {parameter name}.

As for non "VITE_" The variable at the beginning is a private property and will not be passed out. If your project contains such sensitive variables. Files should be added to your They are checked into git to avoid being ignored by GIT.

After completing the above configuration, we only need to JSON adds the corresponding startup command to let Vite get which mode to run the project:

{
  "name": "vite-mul-env-learn",
  "version": "0.0.0",
  "scripts": {
    "dev:local": "vite --mode _local",
    "dev:test": "vite --mode test",
    "build:beta": "vite build --mode beta",
    "build:release": "vite build --mode release",
    "lint": "eslint --fix --ext .js,.vue,ts src"
  }
}

Vite default environment variable

Vite is in a special import meta. Expose environment variables on Env {objects. Here are some built-in variables that can be used in all cases:

  • import.meta.env.MODE: {string} application running pattern.
  • import.meta.env.BASE_URL: {string} the basic URL when deploying the app. He by base configuration item decision.
  • import.meta.env.PROD: {boolean} whether the application is running in the production environment.
  • import.meta.env.DEV: {boolean} whether the application runs in the development environment (always opposite to {import.meta.env.PROD).
  • import.meta.env.SSR: {boolean} whether the application is running in the server rendering environment.

In addition, dev and PROD correspond to package JSON is determined by starting dev and build commands, while SSR is determined by corresponding to the middlewareMode variable set during Vite startup:

const { createServer: createViteServer } = require('vite')

const vite = await createViteServer({
    server: { middlewareMode: 'ssr' }
  })

Transparent transmission of environment variables through plug-ins

In many cases, our environment variable is not just a simple string, but the final result can be obtained through secondary calculation in vite service, which is somewhat similar to the effect of calculated in Vue or useMemo and useCallback in React. For non static environment variables like this, we need to use the plug-in capability to enable them to return to the client. There are many plug-ins. Here we recommend vite plugin environment, which is roughly like this:

You can provide a list of environment variable names to expose to your client code:

import { defineConfig } from 'vite'
import EnvironmentPlugin from 'vite-plugin-environment'

export default defineConfig({
  plugins: [
    EnvironmentPlugin(['API_KEY', 'DEBUG']),
  ],
})

And then use them as:

const apiKey = process.env.API_KEY

On this basis, we can also make joint judgment with the pattern file:

import { defineConfig, ConfigEnv, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from 'path';
import EnvironmentPlugin from 'vite-plugin-environment';
import { fetchEnv } from './server/envUitls';

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }: ConfigEnv) => {
  const env = loadEnv(mode, __dirname);
  const { proxy } = fetchEnv(env.VITE_NODE_ENV); // Set domain name and port

  return {
    base: './',
    plugins: [
      vue(),
      EnvironmentPlugin({
        PROXY: proxy
      })
    ]
  };
});

const env = loadEnv(mode, __dirname); Available env._local is all non private parameters. Next, the program can calculate the final environment variables according to the mode parameters and return to the client through the plug-in.

fetchEnv method can be understood as an environment collector, in which logic can be written to uniformly integrate environment parameters.

Client environment difference customization

This is well understood. It is nothing more than obtaining environment variables by specifying methods to conditionally render vue or React components. Here is a demo:

<script setup lang="ts">
import { ref } from 'vue';
import { proxy } from '@/api/proxy';

interface IEnv extends ImportMetaEnv {
  VITE_NODE_ENV: string;
  VITE_OWNER: string;
  VITE_POSITION: string;
}
const viteEnv: IEnv = import.meta.env;
</script>

<template>
  <div class="app">
    <img alt="Vue logo" src="./assets/logo.png" />
    <section class="main">
      <div class="card">
        <h3>ā‘ Parameters passed in through the environment file</h3>
        <div class="tips">Description: only"VITE_"Start parameter</div>
        <div>project owner: {{ viteEnv.VITE_OWNER }}</div>
        <div>owner Location:{{ viteEnv.VITE_POSITION }}</div>
        <div>project mode: {{ viteEnv.VITE_NODE_ENV }}</div>
      </div>
      <div class="card">
        <h3>ā‘”Parameters passed by environment plug-in</h3>
        <div class="tips">
          Description: passed vite-plugin-environment The parameters passed by the plug-in are generally the parameters after secondary calculation. If it is a static parameter value, it will be directly through the schemeā‘ Just pass it back.
        </div>
        <p>Service request domain:{{ proxy }}</p>
      </div>
      <div class="card">
        <h3>ā‘¢Vite Environment built-in parameters</h3>
        <div class="tips">
          explain: Vite Default parameters, reference
          <a href="https://cn.vitejs.dev/guide/env-and-mode.html#env-variables"
            >Vite environment variable</a
          >
        </div>
        <p>Is it SSR pattern:{{ viteEnv.SSR }}</p>
        <p>Local development mode:{{ viteEnv.DEV }}</p>
        <p>Build mode:{{ viteEnv.PROD }}</p>
        <p>Current startup command read mode For:{{ viteEnv.MODE }}</p>
        <p>Basic when deploying applications URL: {{ viteEnv.BASE_URL }}</p>
      </div>
    </section>
  </div>
</template>

<style lang="less" scoped>
.app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
.main {
  display: flex;
  .card {
    margin: 10px;
    padding: 10px;
    width: 300px;
    text-align: left;
    background-color: #dbf1e7;
    font-size: 14px;
    h3 {
      margin-bottom: 0;
    }
    .tips {
      margin-bottom: 10px;
      font-size: 12px;
      color: #898989;
    }
  }
}
</style>

design sketch

Thinking about the business scenarios to be solved

In addition to the most intuitive scenario described in the "business background" module of this article, you can also do many high-level operations related to project engineering.

If the project construction operation is carried out on a remote server, the service api can be linked to generate construction packages of different versions and modes before the construction and packaging. SSR logic can even be put here to achieve the effect of "thousands of people and thousands of faces".

End

This article ends here. If the article is useful to you, you can support it for three times. If you have better opinions, you are welcome to make corrections~