You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Vite的TypeScript React Monorepo中实现HMR热模块替换

React Monorepo迁移Vite后HMR失效问题及解决方案需求

问题背景

我有一个基于TypeScript的React Monorepo,正在从Webpack迁移到Vite,但Vite里无法正常实现HMR(热模块替换)。我推测是因为我们单独构建各个包导致的。我需要找到可行的解决方案,但必须保留单独构建包的能力——这是为了保证Jest/ESLint的性能。

项目结构

\apps
  \main
\packages
  \domainA
    \foo
      \package.json
      \build
      \src

当前包构建与配置

每个包通过tsc --project tsconfig.lib.json命令构建到build目录,package.json配置如下:

"name": "@ig/foo",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"files": [
    "/build"
]

当前现象

启动主应用后,修改/packages/domainA/foo/src/index.ts时,包会自动构建(已配置监听器),但页面会完全重载,无法触发HMR。

约束与尝试

  • 直接把包的入口改为"main": "./src/index.ts"会降低Jest/ESLint的性能,不符合需求,但可以接受让Vite在开发环境直接指向源文件的方案。
  • 尝试过配置Vite别名,但无效:
resolve: {
  alias: [{
    find: '@ig/foo',
    replacement: '../packages/domainA/foo/src/index.ts',
  }]
}

当前Vite配置

import react from '@vitejs/plugin-react';
import fs from 'fs';
import path, { resolve } from 'path';
import { defineConfig } from 'vite';
import mkcert from 'vite-plugin-mkcert';
import svgrPlugin from 'vite-plugin-svgr';

export default defineConfig({
    // optimizeDeps: {
    //     include: ['@infogrid/solution-views-occupancy'],
    // },
    build: {
        outDir: 'build/public',
        sourcemap: true,
        rollupOptions: {
            input: {
                main: resolve(__dirname, 'index.html'),
                base: resolve(__dirname, 'index_base.html'),
            },
         }
     },
     server: {
        port: Number(process.env.PORT),
        // setting to true allows external ip
        host: true,
     },
     plugins: [
        react({ fastRefresh: true }), // Primarily used for HMR
        svgrPlugin({ svgrOptions: { icon: true } }), // Turns svgs into react components
        mkcert(), // Allows for HTTPS during local development
     ]
});

解决方案

方案1:开发环境指向源文件,生产/工具链用构建产物

核心思路是在Vite开发模式下将包别名指向源文件,生产模式保留构建产物入口;同时通过package.json的exports字段区分环境,不影响Jest/ESLint的性能。

步骤1:修改包的package.json

新增exports字段,给不同环境指定入口:

"name": "@ig/foo",
"main": "./build/index.js",
"types": "./build/index.d.ts",
"files": ["/build"],
"exports": {
  ".": {
    "development": "./src/index.ts",
    "production": "./build/index.js"
  },
  "./src/*": "./src/*"
}

步骤2:调整Vite配置

区分开发/生产模式配置,确保开发时监听源文件、不预优化包:

export default defineConfig(({ mode }) => ({
  // ...原有其他配置
  resolve: {
    preserveSymlinks: true,
    alias: mode === 'development' ? [
      {
        find: '@ig/foo',
        replacement: path.resolve(__dirname, '../packages/domainA/foo/src/index.ts'),
      }
    ] : []
  },
  optimizeDeps: {
    exclude: mode === 'development' ? ['@ig/foo'] : []
  },
  server: {
    // ...原有其他配置
    watch: {
      include: ['../packages/**/*.{ts,tsx,js,jsx}']
    }
  }
}));

步骤3:调整包的TS配置

确保packages/domainA/foo/tsconfig.lib.json支持ES模块输出,让Vite能直接处理源文件:

{
  "compilerOptions": {
    "module": "ESNext",
    "target": "ESNext",
    "jsx": "react-jsx",
    // ...其他原有配置
  }
}

方案2:用Monorepo插件简化配置

如果手动配置别名太繁琐,可使用vite-plugin-monorepo自动处理源文件指向:

  1. 安装插件:npm install vite-plugin-monorepo -D
  2. 修改Vite配置:
import monorepo from 'vite-plugin-monorepo';

export default defineConfig(({ mode }) => ({
  // ...原有其他配置
  plugins: [
    monorepo({
      packages: ['../packages/**']
    }),
    // ...原有其他插件
  ],
  optimizeDeps: {
    exclude: mode === 'development' ? ['@ig/foo'] : []
  }
}));

插件会自动将Monorepo包指向源文件,无需手动配置别名,同时不影响生产环境使用构建产物。

关键注意事项

  • 开发模式下必须通过optimizeDeps.exclude排除包,避免Vite预优化导致HMR失效。
  • 开启preserveSymlinks可避免Vite解析包时出现路径问题。
  • 包的TS配置必须输出ES模块,否则Vite无法处理源文件。

内容的提问来源于stack exchange,提问作者Ian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 09:20:42