哈喽大家好,我是外卖仔,老久没静下心来卷文章了。随着Vite在前端工具链中比重越来越大,用的人也越来越多,打算出几期Vite的应用和机制研读,让小伙伴们用起来更得心应手。
文中对于Vite环境文件系统模块介绍主要分两块:
为了更直观了解Vite的环境,先讲个笔者之前“修仙”时听到的故事:
随身听
,另外山上每座庙藏宝阁有个U盘
,你把U盘插到随身听上
,就可播出山下到这座庙的路线了。这随身听我有好几个,给你一个便是。
这个故事简单呈现了Vite的多环境功能,其中:
指定环境文件启动流程图:
参考:《Vite环境与模式》
string
root
设置读取环境文件的路径,默认是跟vite.config
文件所在同目录。string | string[]
VITE_
自定义环境变量前缀,默认为VITE_
,符合该前缀的环境变量才会暴露在import.meta.env
中。另外官方有安全合规建议:
SECURITY NOTES
envPrefix
should not be set as''
, which will expose all your env variables and cause unexpected leaking of of sensitive information. Vite will throw error when detecting''
.
🌰项目下有2个环境文件:
内容如下:
# .env.demo
# 透传客户端参数
VITE_NODE_ENV=demo
VITE_OWNER=Outer
VITE_POSITION=.env.demo
# 在envPrefix配置了的前缀参数也可以透传到客户端
MY_PARAM1=自定义参数1
YOUR_PARAM2=自定义参数2
# 私有参数,仅在vite server获取到,
# 假如你的项目包含此类敏感变量。应该将文件添加到你的 .gitignore 中,以避免它们被 git 检入。
MODE_KEY=PRIVATE_KEY_BETA
复制代码
# viteEnv/.env.demo
# 透传客户端参数
VITE_NODE_ENV=demo
VITE_OWNER=Inner
VITE_POSITION=viteEnv/.env.demo
# 在envPrefix配置了的前缀参数也可以透传到客户端
MY_PARAM1=自定义参数1
YOUR_PARAM2=自定义参数2
# 私有参数,仅在vite server获取到,
# 假如你的项目包含此类敏感变量。应该将文件添加到你的 .gitignore 中,以避免它们被 git 检入。
MODE_KEY=PRIVATE_KEY_BETA
复制代码
接下来是添加启动命令,在package.json设置:
{
"scripts": {
"dev:demo": "vite --mode demo",
}
}
复制代码
--mode
参数后面带要指定的环境文件,可以忽略文件名的.env.
前缀。
❓到这里就有问题了,上面2个环境文件都叫demo,该命令启动后到底用哪一个?
这时候我们就可以通过envDir
选项来指定了,因此我们还需要配置一下vite.config.ts:
export default defineConfig(async ({ command, mode }: ConfigEnv) => {
return {
envDir: './viteEnv',
// or
// envDir: './',
envPrefix: ['VITE_', 'MY_', 'YOUR_'],
// 其他配置 ...
}
});
复制代码
控制台输出:
关于envPrefix配置项的用法在上面截图也可以看到了,指定的['VITE_', 'MY_', 'YOUR_']
开头的变量都会expose到客户端。
其实Vite在启动执行的createServer()方法中,关于读取环境文件这块会执行一个函数叫loadEnv
,以下是它的定义。
loadEnv(mode: string, envDir: string, prefixes?: string | string[]): Record<string, string>;
复制代码
我们可以找到源码看下实现,源码路径packages/vite/src/node/config.ts
:
首先函数接受3个参数:mode名称(也就是启动命令传进去的文件名),和上面我们讲的2个配置项的值。进程会先生成4个默认文件路径保存到envFlies变量,2个mode和2个默认。
接下来会结合envDir寻找目标文件,找到后,使用fs配合prefixes变量前缀数组把对应的变量读取出来放置到env中,最后返回给主线程。
整个过程比较清晰,就是通过fs来取对应的文件变量,返回给进程使用。
在服务启动后,客户端便可以通过import.meta.env来获取。 见:env variables
其实,我们也可以在Vite启动服务的RUNTIME时机获取环境变量,直接显式调用loadEnv方法即可:
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, path.resolve(__dirname, 'viteEnv'), [
'VITE_',
'MY_',
'YOUR_'
]);
console.log('env:', env);
return {
envDir: './viteEnv',
// or
// envDir: './',
envPrefix: ['VITE_', 'MY_', 'YOUR_'],
base: './',
plugins: [
vue(),
],
resolve: {
alias: [
{
find: '@',
replacement: '/src'
}
]
}
};
});
复制代码
控制台输出
很多情况下,我们的环境变量不仅仅是简单的字符串,而是通过vite服务中二次计算才能得到最终结果,有点类似Vue中computed
或React中useMemo
、useCallback
的效果。 像这类非静态的环境变量,我们需要借助插件能力来让它们也能够返回客户端,插件很多,这里推荐vite-plugin-environment
,使用大概是这样子的:
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
复制代码
在这个基础上,我们还能配合模式文件进行联合判断:
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); // 设置域名和端口
return {
base: './',
plugins: [
vue(),
EnvironmentPlugin({
PROXY: proxy
})
]
};
});
复制代码
const env = loadEnv(mode, __dirname);
可以获取.env._local
是所有非私密参数,接下来程序可以根据模式参数来计算最终的环境变量,通过插件返回到客户端。
fetchEnv
方法可以理解成环境收集器,里面可以写逻辑让环境参数得到统一整合。
Vite的环境文件系统模块差不多讲到这里了,感谢大家阅览并欢迎纠错