React不eject如何通过.env区分构建内置/在线引用字体版本
CRA项目无eject实现公网/离线局域网双模式字体构建方案
不需要拆分webpack配置,不需要多html入口,不需要运行时动态插DOM,完全基于CRA原生能力+环境变量就能实现,代码零侵入,两种构建产物功能完全一致。
具体实现步骤
1. 准备两份字体样式文件
- 先把你当前SASS中引入的Google Fonts链接在浏览器里打开,把返回的所有
@font-face规则复制下来,对应规则里引用的woff2/woff字体文件全部下载到项目src/assets/fonts/目录下(放在src目录下才能被webpack处理,不要放public)。 - 新建本地字体样式文件
src/styles/fonts-local.scss,把刚才复制的@font-face规则粘进去,只把规则里的远程字体url替换成本地字体文件的相对路径即可,字体名、font-weight、font-display等所有属性保持和原来完全一致,保证样式效果不偏差。 - 新建CDN字体样式文件
src/styles/fonts-cdn.scss,内容就是你原来写的Google Fonts引入语句即可。
2. 配置环境变量做构建时切换
CRA原生支持自定义环境变量,不需要改任何构建配置:
- 根目录新建默认公网构建的配置文件
.env.production,写入REACT_APP_FONT_SOURCE=cdn,平时执行普通build命令时默认读这个配置。 - 根目录新建离线局域网构建的配置文件
.env.lan,写入REACT_APP_FONT_SOURCE=local,专门给离线构建用。 - 删掉你原来在全局样式里硬编码的Google Fonts引入,改成在项目入口文件
src/index.js里做静态条件引入:
// 其他import逻辑之前加 if (process.env.REACT_APP_FONT_SOURCE === 'local') { import './styles/fonts-local.scss'; } else { import './styles/fonts-cdn.scss'; }
这里的判断是构建时生效的:react-scripts打包时会直接把
process.env.REACT_APP_FONT_SOURCE替换成当前环境的常量值,没用到的那份样式和对应资源会被Tree Shaking完全剔除,不会出现在最终产物里。如果选local模式,所有本地字体文件会被webpack自动加上hash指纹,打包到build目录的static/media下,引用路径自动处理为相对路径,不需要手动改任何public下的资源配置。
3. 新增离线构建命令
先安装一个零配置的环境变量读取工具当开发依赖:npm i env-cmd -D,不需要做任何额外配置。
然后在package.json的scripts块里加一行:
"build:lan": "env-cmd -f .env.lan react-scripts build"
后续使用逻辑非常简单:
- 公网部署就跑原来的
npm run build,产物和你现在的版本完全一致,走Google Fonts CDN,不会把字体打进包,包体积不变。 - 离线局域网部署就跑
npm run build:lan,产物自动内置所有字体资源,不需要外网就能正常加载字体,渲染效果和CDN版完全一致。
为什么不推荐其他方案
- 运行时动态插入link/样式的方案:不管选哪种模式,两份逻辑都会被打进bundle,冗余代码多;放在public目录的字体不会被webpack做hash缓存处理,后续更新容易出缓存问题,还可能出现字体加载闪烁。
- 多HTML入口的方案:两份入口文件维护成本高,后续改meta、公共脚本等内容需要同步改两份,容易出现版本不一致。
- 上述方案全程不需要eject,不需要修改webpack配置,业务代码里的字体相关写法完全不用改,两种构建模式的逻辑在编译阶段完全分离,没有多余运行时开销。
内容的提问来源于stack exchange,提问作者Alex Kogan
相关产品推荐
相关产品推荐

