如何在同一仓库内让新模块用 Vite、旧模块用 Grunt

解读

面试官真正想考察的是:

  1. 你是否理解双构建体系共存带来的路径解析、依赖隔离、产物输出、脚本命令、CI/CD 五类冲突
  2. 能否给出可落地的渐进式迁移方案,而不是“一刀切”重写
  3. 对国内常见场景(如私有 Nexus、企业微信/钉钉机器人通知、 Jenkins/蓝绿发布)是否有工程化经验

知识点

  • Monorepo 策略:pnpm workspace / Yarn workspace / Nx / Lerna,锁定node_modules结构,避免依赖版本漂移
  • 路径别名隔离:Vite 用resolve.alias,Grunt 用grunt-contrib-requirejspathsbrowserifyaliasify,保证新旧模块互相不穿透
  • 产物输出隔离:Vite 默认dist,Grunt 传统build;统一在gitignore里忽略,并在package.jsonfiles字段白名单发布
  • 脚本命令命名规范:国内团队普遍采用npm run dev:vitenpm run dev:grunt区分,CI 阶段用npm run build:all串行或并行
  • 环境变量开关:借助cross-env设置BUILDER=vite|grunt,让postbuild钩子做差异化上传(OSS、CDN、Docker 镜像)
  • 共享底层能力:把ESLint/Stylelint/Prettier抽成**@scope/lint-config包,供两种构建消费,保证代码质量网关**唯一
  • 灰度发布:在Nginx层按Cookie/Header分流,旧模块mapgrunt-legacy.xxx.js,新模块mapvite-modern.xxx.js,实现0 downtime切换
  • 性能监控:接入Sentry阿里 ARMS,在sourcemap上传阶段区分project=legacyproject=vite,方便回滚

答案

“我去年在某头部电商金融项目做过类似改造,核心思路是**‘目录隔离+脚本双轨+产物双出口+CI 并行’**:

  1. 目录隔离
    Monorepo根目录下建packages/legacypackages/modern,分别放 Grunt 与 Vite 代码;tsconfigeslintpostcss配置全部下沉到各自包,根目录只做最小化继承,避免幽灵依赖

  2. 脚本双轨
    package.json定义:

    "scripts": {
      "dev:legacy": "pnpm --filter legacy run grunt:dev",
      "dev:modern": "pnpm --filter modern run vite",
      "build:all": "pnpm --parallel --filter './packages/**' run build"
    }
    

    本地开发时,终端 1npm run dev:legacy(监听8080),终端 2npm run dev:modern(监听5173),Nginxproxy_pass路径前缀转发

  3. 产物双出口
    统一在CI里把legacy/distmodern/dist拷贝到release/${BUILD_NUMBER}/static下,Jenkins调用ossutil一次性上传,CDN路径带v=legacy|modern版本号,回滚只需切换CDN 目录软链

  4. 渐进式迁移
    先迁移活动页这类低频迭代模块,验证2 个 SprintPV 下跌后,再迁移核心下单链路;每迁移一个模块,就把该模块从Gruntfile.jsfiles 数组里删除,Vite 配置里新增rollupOptions.input,保证构建产物不会重复打包

  5. 风险兜底
    Nginx层加feature-toggle

    map $cookie_builder $js_root {
      default   //static-legacy.example.com;
      vite      //static-modern.example.com;
    }
    

    一旦Vite 产物出现白屏,运维把Cookie 开关切回Grunt1 分钟内完成回滚

通过这套方案,我们把70 万行遗产代码首屏 JS 体积1.8 MB降到480 KB本地冷启动45 s降到3 s线上故障率保持0,并且零裁员完成技术栈过渡。”

拓展思考

  • 微前端是否值得引入:若旧模块是jQuery+artTemplate,新模块是Vue3+TS,可用qiankun运行时集成,但沙箱隔离会带来30% 性能损耗,在ToB 后台场景可接受,ToC 高并发场景建议构建时集成
  • 双构建导致sourcemap上传量翻倍,可只在主干分支保留完整 map功能分支hidden-source-map,减少Sentry 套餐费用
  • 国内私有 npm常出现vite 插件同步延迟,可搭cnpm镜像并配置VITE_PLUGIN_PROXY=https://registry.npmmirror.com**,避免**CI 拉包失败
  • 未来彻底下线 Grunt判定指标:连续3 个版本legacy 目录提交,且监控大盘显示Grunt 产物请求量<1%,即可在迭代评审会上提出下线计划删除相关仓库、脚本、域名、CDN 目录,完成技术债清零