vite中环境变量获取方式

vite在处理环境变量时和之前webpack并不一样,它只会将环境变量文件中VITE_为前缀的变量加载到import.meta.env对象上去,而且并不支持动态key的形式:

import.meta.env[key]   //无效的

因为import.meta.env.xxx打包时会被替换成字符串,比如有这么一个环境变量:VITE_NAME="aaa"

const name = import.meta.env.VITE_NAME;
//打包后
const name = "aaa";

所以如果你是一个动态的key,那就没法进行正确的替换,这点需要注意。

并且在vite环境中,我们没法在业务代码中直接使用process.env这个变量对象,vite并没有将其注入到全局中去,反倒是在vite.config.ts配置文件中,我们还可以访问到它,但是在配置文件中反倒无法使用import.meta.env,也无法通过process.env访问到环境变量文件中的自定义的环境变量。

为此就产生了一个问题:如何在vite.config.ts中获取到环境变量?

常见的做法如下:

import { defineConfig, loadEnv } from "vite";

export default ({mode}) => {
  process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };

  return defineConfig({
    ...
  });
}

process.env得到的是node环境下预设的环境变量,它并不会加载配置的环境变量文件中的变量,哪怕你的文件名是.env这种优先级最先的环境变量文件。

process.cwd()得到的是启动 Node.js 进程时所在的目录。

vite的环境变量文件基于以下格式:

.env                # 所有情况下都会加载
.env.local          # 所有情况下都会加载,但会被 git 忽略
.env.[mode]         # 只在指定模式下加载
.env.[mode].local   # 只在指定模式下加载,但会被 git 忽略

mode是运行vite时指定的,如:

vite build --mode [指定的模式]

然后我们可以通过导出一个函数的形式,从函数的参数中拿到指定的mode,然后配合loadEnv加载对应的环境变量文件,loadEnv返回一个对象。

我们看下官方对loadEnv函数的类型声明:

function loadEnv(
  mode: string,
  envDir: string,
  prefixes: string | string[] = 'VITE_',
): Record<string, string>

可以看到这个函数接收三个参数,第一个是mode模式,第二个envDir是env文件的路径,第三个prefixes是环境变量前缀,默认值是VITE_,也就是说默认只会获取环境变量文件中VITE_开头的内容,如果你希望通过这个函数获取到所有变量,可以这么干:

loadEnv(mode, process.cwd(), "")

传入个空字符串将前缀抵消就行了。

HTML文件中使用环境变量

挂载在import.meta.env中的变量都可以通过特殊语法:%EVN_NAME%的方式使用:

<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>

看上去很美好,实际上你只能用于字符串平替,想做个if判断啥的,基本没戏,所以这里提供一个加强版的做法。

首先我们可以去看下官方处理html的插件:

github: vite-plugin-html

可以看到功能介绍中就明确说明支持ejs模板功能,那按道理我们就能使用ejs的变量注入,往下看果然有一个inject属性,里面有个data对象,我们将需要注入的变量挂载到data对象下即可。

import { createHtmlPlugin } from  "vite-plugin-html";
import { defineConfig, loadEnv } from "vite";

export default ({mode}) => {
  process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };

  return defineConfig({
    plugins: [
      createHtmlPlugin({
        minify: true,
        /** 注入环境变量 */
        inject: {
          data: {
            env: process.env
          }
        }
      })
    ]
  });
}

在html中使用:

<% if( env.VITE_ENV === "development") { %>
  <h2><%= VITE_ENV %></h2>
<% } %>

这样我们就能通过特定的环境变量实现一些特殊脚本的引入,当然还有更多操作可以自行处理。

不懂语法可以简单看下ejs的官方文档:ejs使用文档

vite中的mode

vite和webpack一样,都有mode配置选项。

默认情况下,开发服务器 (dev 命令) 运行在 development (开发) 模式,而 build 命令则运行在 production (生产) 模式。

运行dev命令会默认加载.env.development环境变量文件;

运行build命令会默认加载.env.production环境变量文件;

假设所有的环境变量都不配置NODE_ENV,在dev命令下NODE_ENV="development",在build构建命令下NODE_ENV="production"

而vite的mode选项,默认采用NODE_ENV,所以需要注意,如果你指定了环境变量文件,且没有配置NODE_ENV的情况下,你可以根据你的命令来确定当前的打包模式。

define全局常量注入

在使用vue cli或者webpack搭建的项目中,使用process.env获取环境变量是一个常见的做法,但是迁移到vite就会产生问题,上面也说过了,vite不会在全局环境中注入该变量,为此我们可以手动实现注入。

import { defineConfig, loadEnv } from "vite";

export default ({mode}) => {
  process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };

  return defineConfig({
    define: {
      "process.env": JSON.stringify(process.env)
    }
  });
}

官方要求值必须是JSON对象,所以我们将对象JSON.stringify格式化一下即可,注意哪怕你只是一个string值,你也得JSON.stringify格式化,所以只要记住一点,凡是赋值就JSON.stringify转一下

类型声明

对于import.meta.env我们可以走官方的教程:

vite-env.d.ts

/// <reference types="vite/client" />

interface ImportMetaEnv {
  // 这里声明自定义的环境变量类型
  readonly VITE_APP_TITLE: string
  // 更多环境变量...
}

interface ImportMeta {
  readonly env: ImportMetaEnv
}

如果是process.env

vite-env.d.ts

declare namespace NodeJS {
    export interface ProcessEnv {
        // 这里声明自定义的环境变量类型
        readonly VITE_APP_TITLE: string
        // 更多环境变量...
    }
}

如果你的process.env上面也是通过上面那种方式获取并define的,那么其实ImportMetaEnv上的环境变量process.env也有,我们可以通过继承的方式更便捷的实现类型声明:

declare namespace NodeJS {
    export interface ProcessEnv extends NodeJS.ProcessEnv, ImportMetaEnv {}
}

这样即保留了原生类型声明,也继承了ImportMetaEnv

不过需要留意的是默认没有NODE_ENV这个类型声明,虽然 NODE_ENV 是常见的环境变量,但并不是所有 NodeJS 程序都会使用它,因此 TypeScript 并不会默认为它存在。

declare namespace NodeJS {
    export interface ProcessEnv extends NodeJS.ProcessEnv, ImportMetaEnv {
      NODE_ENV: 'development' | 'production';
    }
}

你可以自己手动补上。

分类: vue 项目实战 标签: 环境变量envvitemodedefine

评论

暂无评论数据

暂无评论数据

目录