本文档介绍了一种通用的解决方案,用于在 Docker 容器启动时(Runtime)动态注入环境变量到 Vite 构建的静态前端应用中,解决了静态资源构建后无法感知部署环境配置的问题。
public/env-config.js 提供默认配置。index.html 引入 env-config.js,该文件在 Docker 容器启动时由脚本根据环境变量动态生成/覆盖。window.__ENV__ 读取配置,并回退到 import.meta.env。在 public 目录下创建 env-config.js,用于本地开发时的默认值。
public/env-config.js
window.__ENV__ = {
// 本地开发默认值
VITE_API_BASE_URL: "http://localhost:3000",
VITE_APP_TITLE: "Local Dev App"
};
在 index.html 的 <head> 标签中引入该脚本。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<!-- 引入配置文件,注意路径需根据 base 配置调整,如 /admin/env-config.js -->
<script src="/env-config.js"></script>
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
为了让 TS 识别 window.__ENV__,创建类型声明文件。
src/types/env.d.ts
export {};
declare global {
interface Window {
__ENV__?: {
VITE_API_BASE_URL?: string;
VITE_APP_TITLE?: string;
[key: string]: string | undefined;
};
}
}
创建一个工具函数来统一读取配置,优先读取运行时注入的值。
src/utils/env.ts
/**
* 获取环境变量,优先读取运行时配置 (window.__ENV__),其次读取构建时配置 (import.meta.env)
* @param key 环境变量名
* @returns 变量值
*/
export const getEnv = (key: string): string => {
// 1. 尝试从运行时注入的 window.__ENV__ 读取
if (window.__ENV__ && window.__ENV__[key]) {
return window.__ENV__[key]!;
}
// 2. 尝试从构建时 import.meta.env 读取
return import.meta.env[key] || '';
};
// 使用示例
export const API_BASE_URL = getEnv('VITE_API_BASE_URL');
创建 entrypoint.sh,用于在容器启动时提取环境变量并写入文件。
entrypoint.sh
#!/bin/sh
# 配置 Nginx 目录下的目标文件路径
# 如果你的 Vite config 设置了 base: '/admin/',这里可能需要调整路径
CONFIG_FILE="/usr/share/nginx/html/env-config.js"
# 1. 确保文件存在(如果不存在则创建空对象)
if [ ! -f "$CONFIG_FILE" ]; then
echo "window.__ENV__ = {};" > "$CONFIG_FILE"
fi
# 2. 追加换行符,防止拼接错误
echo "" >> "$CONFIG_FILE"
# 3. 动态遍历环境变量
# 筛选以 VITE_ 开头的变量 (根据需要修改 grep 规则)
env | grep -E "^VITE_" | while read -r line; do
# 提取 KEY 和 VALUE
key=${line%%=*}
value=${line#*=}
# 对双引号进行转义,防止破坏 JS 语法
value=$(echo "$value" | sed 's/"/\\"/g')
# 使用追加方式覆盖配置
echo "window.__ENV__['$key'] = \"$value\";" >> "$CONFIG_FILE"
done
# 4. 启动主进程 (通常是 Nginx)
exec "$@"
使用多阶段构建(Multi-stage Build)来构建应用并设置启动脚本。
Dockerfile
# ==========================================
# Build Stage (构建阶段)
# ==========================================
FROM node:20-alpine AS builder
# 启用 pnpm (也可替换为 npm 或 yarn)
RUN corepack enable && corepack prepare pnpm@latest --activate
WORKDIR /app
# 优先复制依赖定义文件以利用 Docker 缓存
COPY package.json pnpm-lock.yaml ./
# 安装依赖
RUN pnpm install --frozen-lockfile
# 复制源代码
COPY . .
# 执行构建
RUN pnpm build
# ==========================================
# Production Stage (生产运行阶段)
# ==========================================
FROM nginx:stable-alpine
# (可选) 安装 curl 用于健康检查
RUN apk add --no-cache curl
# 1. 从构建阶段复制构建产物到 Nginx 目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 2. (可选) 复制自定义 Nginx 配置
# COPY nginx.conf /etc/nginx/conf.d/default.conf
# 3. 复制 entrypoint 脚本
COPY entrypoint.sh /entrypoint.sh
# 4. 赋予执行权限 (关键步骤)
RUN chmod +x /entrypoint.sh
# 5. 设置容器启动入口
ENTRYPOINT ["/entrypoint.sh"]
# 6. 暴露端口
EXPOSE 80
# 7. 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
构建镜像后,在启动容器时通过 -e 传递环境变量,即可动态改变前端配置。
docker run -d -p 8080:80 \
-e VITE_API_BASE_URL="https://api.production.com" \
-e VITE_APP_TITLE="Production App" \
my-vite-app
启动后,访问页面并查看生成的 env-config.js 或在控制台输入 window.__ENV__ 验证结果。
AWS_SECRET_KEY, DB_PASSWORD, OAUTH_CLIENT_SECRET 等)。entrypoint.sh 中严格限制 grep 规则(如仅允许 ^VITE_ 或 ^PUBLIC_),防止意外泄露容器内的其他系统环境变量。