最可靠方式是在页面加载时用JS动态创建link标签:先检查是否已存在同href的link避免重复,再创建并插入head,同时监听onload/onerror处理加载状态;构建阶段无法解析环境变量,必须运行时读取环境标识(如process.env.NODE_ENV或import.meta.env.MODE)并拼接CSS路径。

构建阶段用 JS 动态创建 link 标签最可靠
HTML 的 <link> 标签本身不解析环境变量,href 属性写 ./styles/${process.env.NODE_ENV}.css 会原样输出、404。唯一可控的时机是构建后、页面加载时——在入口 JS 中读取已注入的环境标识,再手动创建并插入 link 标签。
常见错误现象:直接在 HTML 模板里写 <link href="%REACT_APP_ENV%.css"> 却没配好构建工具替换逻辑,结果生成的是字面量 %REACT_APP_ENV%.css;或在 CSS 文件里用 @import 尝试拼接路径,完全无效。
- Webpack 项目:确保
DefinePlugin已注入process.env.NODE_ENV,且值为字符串字面量(如'production'),避免运行时访问process报错 - Vite 项目:优先用
import.meta.env.MODE或import.meta.env.PROD,它们是编译期常量,比import.meta.env.VITE_ENV更稳定 - React(CRA)项目:必须用
REACT_APP_*前缀的变量,例如REACT_APP_CSS_ENV,否则构建时不注入
document.createElement('link') 必须加去重和错误处理
多次执行入口脚本(比如热更新、SSR 二次 hydrate、或用户刷新后又触发)会导致重复插入相同 link,引发样式覆盖混乱甚至 FOUC。同时,外部 CSS 加载失败不会抛异常,但会静默失效。
正确做法是先检查是否已存在同 href 的 link,再创建;并监听 onload/onerror 做基础反馈:
立即学习“前端免费学习笔记(深入)”;
const loadCss = (url) => {
if (document.querySelector(`link[href="${url}"]`)) return;
const link = Object.assign(document.createElement('link'), {
rel: 'stylesheet',
href: url,
onload: () => console.log('CSS loaded:', url),
onerror: () => console.warn('CSS load failed:', url)
});
document.head.appendChild(link);
};
loadCss(process.env.NODE_ENV === 'development' ? '/css/dev.css' : '/css/prod.css');纯前端运行时检测域名/端口只适合简单项目
当无法修改构建流程(如 legacy 项目、静态托管、或 CMS 嵌入场景),可用 location.hostname 和 location.port 做轻量判断。但它绕过了构建工具的环境语义,容易误判。
典型误判场景:
- 本地联调用
ngrok或内网穿透,hostname是随机域名,不是localhost - 开发服务器跑在
:8080,但测试环境也用了:8080,端口无法区分 - CI/CD 预览链接带 hash(如
pr-123.example.com),未纳入判断逻辑
若必须用此法,建议组合多个条件:
const isDev =
location.hostname === 'localhost' ||
location.hostname === '127.0.0.1' ||
location.port === '3000' ||
location.href.includes('preview=dev');CSS 文件内容要保持结构一致,只变视觉参数
不同环境的 CSS 文件不能一个有 .btn { border: 1px solid red; },另一个完全没定义该规则——这会导致生产环境按钮丢失边框,且难以定位。
推荐做法:
- 抽离
base.css放通用布局、重置、组件骨架 -
dev.css和prod.css只覆盖颜色、阴影、动画时长、字体粗细等非结构性属性 - 优先用 CSS 自定义属性(
:root { --primary-color: #007bff; }),环境切换只需换一个:root块,大幅降低维护成本
最容易被忽略的一点:dev.css 里加的调试样式(比如 * { outline: 1px solid rgba(255,0,0,0.3); })必须确认不会因打包配置疏漏而漏进生产包——Webpack 的 mode: 'production' 会自动移除 console,但对 CSS 无影响。


















