Vue一次编译多环境部署方案:解决测试与生产一致性难题

在企业级前端项目中,测试环境和生产环境必须使用完全一致的编译包,确保测试通过的代码在生产环境中运行时没有差异。这不仅提升部署效率,更保障投产的可靠性与可控性。单纯通过重新打包来满足不同环境配置虽然可行,但可能引入 hash、依赖版本或配置差异,导致线上出现不可预测的问题。

1️⃣ 背景与需求

项目中我遇到的主要挑战:

  • 不同环境接口服务地址与部署目录不一致
    本地运行:http://localhost:8000
    测试环境:http://ip:port/test/
    生产环境:https://domain/xxx/
  • 测试环境验证的包必须与生产环境运行的包完全一致,无重新打包差异。
  • 需要运行时动态读取环境配置(API 地址、basePath 等)。
  • 静态资源路径需动态调整,确保 JS/CSS 正确加载。

2️⃣ 解决方案

传统做法是为不同环境分别打包,这虽能解决不同环境配置的问题,但无法保证生产环境的代码与测试环境完全一致(node版本或依赖组件版本可能不同)。因此测试通过的代码在生产环境中仍可能出现异常。

由于打包工具在编译时需要知道basePath,而用户希望在编译后动态提供basePath,这就形成了“先有鸡还是先有蛋”的困扰。我曾尝试修改<base> 标签、使用相对路径等方法解决问题:

  • hash 模式下,相对路径可行。
  • history 模式下,相对路径会随 URL 改变,导致资源加载失败,尤其是打开二级页面或直接访问二级页面时问题明显。

经过几天思考,最终想到一种 动态加载配置 的方法:

2.1 config.json 动态配置

手动创建config.json,将环境相关参数配置其中,例如:

{
  "apiBaseUrl": "https://www.luoxudong.com/api",
  "basePath": "/",
  "env": "production"
}

在不同环境下部署时,只需将该文件放在当前环境根目录(避免使用相对路径),也可将其作为模板放入 public 目录,在自动化打包流程中替换变量。

2.2 关闭 Vue CLI 自动注入

这是方案核心步骤之一。默认情况下,Vue CLI 会在 index.html 模板中自动注入 JS/CSS。由于静态资源路径可能不正确,这会导致资源加载失败。

module.exports = {
  publicPath: './',
  chainWebpack: config => {
    config.plugin('html').tap(args => {
      args[0].inject = false; // 禁用 JS/CSS 自动注入
      return args;
    });
  }
}

2.3 重写 index.html 模板,手动注入

优化index.html模板,打开网站时动态加载config.json配置,并保存在js全局变量中;手动拼接js/css的完整路径并注入到head标签中,同时动态设置 <base> 标签,实现静态资源路径的动态调整:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>我的项目</title>

  <script>
    const DEFAULT_CONFIG = {
       apiBaseUrl: '<%= process.env.VUE_APP_API_BASE_URL %>',
       basePath: '<%= process.env.VUE_APP_BASE_PATH %>',
       env: '<%= process.env.VUE_APP_ENV %>'
    };

    fetch('/config.json')
      .then(res => res.json())
      .then(cfg => {
        const CONFIG = { ...DEFAULT_CONFIG, ...cfg };
        window.CURRENT_CONFIG = CONFIG;

        const baseTag = document.querySelector('base') || document.createElement('base');
        baseTag.href = CONFIG.basePath || DEFAULT_CONFIG.basePath;
        if (!document.querySelector('base')) document.head.appendChild(baseTag);

        const cssFiles = <%= JSON.stringify(htmlWebpackPlugin.files.css) %>;
        cssFiles.forEach(file => {
          const link = document.createElement('link');
          link.rel = 'stylesheet';
          link.href = CONFIG.basePath + file;
          document.head.appendChild(link);
        });

        const jsFiles = <%= JSON.stringify(htmlWebpackPlugin.files.js) %>;
        jsFiles.forEach(file => {
          const script = document.createElement('script');
          script.src = CONFIG.basePath + file;
          //设置async=false模拟defer行为
          script.async = false;
          script.defer = true;
          document.head.appendChild(script);
        });
      })
      .catch(err => {
        console.error('配置加载失败,使用默认配置', err);
        window.CURRENT_CONFIG = DEFAULT_CONFIG;
      });
  </script>
</head>
<body>
  <div id="app"></div>
</body>
</html>

2.4 动态解析basePath

动态解析设置<base> 标签,使相对路径资源自动解析到指定目录,保证单一编译包在测试、预发布和生产环境均能正确加载静态资源。

2.5 Vue 中引用动态环境变量

config.json 中的参数加载完成后保存到 window.CURRENT_CONFIG,后续在 Vue 组件或 JS 逻辑中可以直接访问,无需重新打包。

3️⃣ 实践经验与注意事项

  • 关闭自动注入后,确保手动加载 CSS/JS,否则会导致依赖加载错误。
  • 动态注入 JS 时需设置同步加载 (script.async = false),避免加载顺序错误。
  • 需判断 config.json 是否存在及内容是否正确,如加载失败则使用默认环境变量覆盖,保证运行时配置有效。
  • 必须在加载 config.json 后再加载 JS/CSS,否则资源路径仍然错误。
  • CI/CD 自动化部署时只需替换 config.json,无需重新打包,保证测试与生产环境一致。

4️⃣ 总结

  • 保证测试环境验证的包与生产环境完全一致,消除重新打包带来的差异。
  • 实现单包多环境部署,无需重复打包。
  • 动态 basePath 支持不同目录部署。
  • 兼容 CI/CD 自动化流程,提升开发和运维效率。

该方法非常适合企业级前端项目,特别是对投产一致性要求高、跨环境验证频繁的场景。

为了方便大家理解和验证本文方案,我整理了一套完整的示例 Demo,可通过右侧工具栏下载附件获取完整源码,在本地或服务器环境中部署体验。

转载请注明出处:https://www.luoxudong.com/1225.html

发表回复

登录... 后才能评论