vite中多环境变量务必了解的知识
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';
}
}
你可以自己手动补上。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据