Vite 5 error during build: node_modules/rc-util/es/ref.js “isMemo“ is not exported by “node_

背景

项目在开发环境能正常运行,但是导build至生产模式报错
node_modules/rc-util/es/ref.js (3:21): “isMemo” is not exported by "node_modules/react-is/index.js", imported by “node_modules/rc-util/es/ref.js”.

is not exported ,猜测肯定时commonJS和ES6兼容问题,Vite是处理ESModule,对于commonJS需要转换
在这里插入图片描述

解决

起初猜测是版本不匹配(React&AntDesign),尝试了,还是不行
再想着是不是Vite的原因:

最开始我配置转换是用@rollup/plugin-commonjs(哈哈哈,AI帮配置的)
import commonjs from ‘@rollup/plugin-commonjs
export default defineConfig({
base: ‘/’,
plugins: [react(),
commonjs({
include: /node_modules/,
requireReturnsDefault: ‘auto’,
extensions: [‘.js’, ‘.cjs’],
transformMixedEsModules: true,
dynamicRequireTargets: [‘node_modules/rc-util/es/ref.js’]
})],
resolve: {
alias: {
‘@’: path.resolve(__dirname, ‘./src’),
}
},

后来再gpt了一下,尝试用vite-plugin-commonjs,结果就好了
import commonjs from ‘vite-plugin-commonjs’;
export default defineConfig({
base: ‘/’,
plugins: [react(),commonjs()],
})
主打越简便越好

why

概要

在使用 @rollup/plugin-commonjs 打包时,由于该插件基于静态 AST 分析 CommonJS 模块的导出,且从 v11 开始移除了 namedExports 选项,它无法自动检测 react-is 在其 CommonJS 入口文件 index.js 中的诸如 isMemo 等命名导出,因此会报错

'isMemo' is not exported by node_modules/react-is/index.js

(Stack Overflow, GitHub)。
vite-plugin-commonjs 则在 Vite 开发模式下采用纯 JavaScript 的按需拦截与转换方式,支持更宽松的动态 require 解析与导出提取,因而能够正确地将 react-is 的所有导出(包括 isMemo)转换为 ESM,从而避免上述错误。(npm, GitHub)。

问题分析

1. react-is 的 CommonJS 结构

  • react-is 在其 index.js 中通过 module.exports = require('./cjs/react-is.development.js') 等方式将所有 API 输出到一个对象上,其中包含 isMemoisFragmentForwardRef 等多种导出。(Bitbucket)
  • 由于这些导出并非在 index.js 处以静态 exports.foo = … 形式出现,Rollup 的 CommonJS 插件在静态分析时无法识别它们。(GitHub)

2. @rollup/plugin-commonjs 的静态导出检测局限

  • 插件默认依赖静态 AST 来寻找 module.exportsexports.*,对深层或间接导出缺乏动态检测能力。(Stack Overflow)
  • 在 v11 版本中移除了用于手动指定导出的 namedExports 选项(PR #410),进一步减少了对这类场景的兼容;如果不手动回退到旧版或采用插件配置 mapping,就无法提取 isMemo。(GitHub, GitHub)
  • 因此,当 Rollup 尝试从 react-is/index.js 导入 isMemo 时,会报出“未导出”错误,并终止打包。(Stack Overflow)

vite-plugin-commonjs 的工作原理

1. 纯 JS 按需拦截

  • 插件在 Vite 开发服务器中拦截对 CommonJS 模块的请求,按需对源码进行字符串或轻量 AST 转换,而非整体打包。(npm)
  • 它默认对 node_modules 下的依赖(包括 react-is)启用转换,并能够识别模块导出对象上的所有属性,无需额外配置。(npm)

2. 动态 require 支持

  • 支持类似 Webpack 的动态 require('./foo/' + bar) 形式,并提供 dynamic.loose 等选项以控制匹配方式。(npm)
  • 通过执行时或准运行时的方式(而非纯静态分析),能够提取如 isMemo 这类间接或动态导出,从而生成完整的 ESM 接口。(npm)

为什么切换后问题消失

  1. 导出识别更全面
    vite-plugin-commonjs 并不依赖旧版 namedExports,而是对 module.exports 对象执行更灵活的提取——包括遍历所有属性并生成相应的 export 语句。(npm)

  2. 无需额外配置
    相比必须在 Rollup 中显式声明

    commonjs({
      namedExports: {
        'node_modules/react-is/index.js': ['isMemo']
      }
    })
    

    的做法,Vite 插件开箱即用即可支持 react-is 的所有导出。(GitHub)

  3. 性能与体验
    在开发模式下,vite-plugin-commonjs 的按需即时转换更加轻量快速,不影响 HMR 和冷启动速度。(vitejs)

建议

  • 开发环境:继续使用 Vite 默认的 esbuild+vite-plugin-commonjs 预构建方案,无需手动干预。

  • 生产构建:若仍使用 Rollup,可考虑在 build.rollupOptions.plugins 中回退到支持 namedExports 的旧版 @rollup/plugin-commonjs(如 v10),或对 react-is 做显式映射:

    commonjs({
      include: 'node_modules/**',
      namedExports: { 'node_modules/react-is/index.js': ['isMemo'] }
    })
    
  • 升级规划:留意 Rollup 插件未来对 CommonJS 动态导出的支持情况,或考虑将 react-is 替换为 ESM 原生模块版本(若可用)。


以上即为何更换至 vite-plugin-commonjs 后,isMemo 能被正确导出,而使用 @rollup/plugin-commonjs 时却报错的根本原因。

Vite原理

摘要

本文将深入剖析 Vite 的底层原理,涵盖其设计理念、模块加载与依赖预构建机制、开发服务器架构、模块图与热模块替换(HMR)、生产构建流程及插件系统等核心组成部分。(vitejs, 知乎专栏)
通过原生 ESM、esbuild 预构建、基于 Koa/connect 的请求拦截与按需编译,以及在生产环境中集成 Rollup,Vite 实现了极致的冷启动速度与高效的开发体验,同时保证了生产构建的优化与稳定性。(vitejs, vitejs)

设计理念

原生 ESM 与现代浏览器

现代浏览器已原生支持 ES6 模块,Vite 充分利用这一特性,直接在浏览器中按源码加载模块,省略传统打包过程,实现“零打包”开发体验。(掘金, xiaohanglin.site)

依赖预构建

在启动阶段,Vite 会使用 esbuild 对 node_modules 中的第三方依赖进行预构建,将它们转换为快速加载的 ESM 格式,减少浏览器请求次数并加速冷启动。(vitejs, sii.pl)

开发服务器架构

请求拦截与按需编译

Vite 启动一个基于 Koa 或 connect 的开发服务器,拦截所有模块请求,当遇到 import 语句时才实时编译并返回 ESM 代码,无需整体打包,真正实现按需加载。(掘金, 腾讯云 - 产业智变 云启未来)

模块图(Module Graph)

Vite 在内存中维护一个全局模块图,用于追踪模块之间的依赖关系和状态,当文件更新时,只重新加载受影响的模块,提升刷新效率。(vitejs)

热模块替换(HMR)

通过在客户端与服务端之间建立 WebSocket 连接,Vite 能够在源文件发生修改时快速推送更新,仅刷新变更模块,确保热更新速度和反馈即时性。(博客园, 掘金)

生产构建流程

集成 Rollup 打包

在生产模式下,Vite 调用 Rollup 作为底层打包器,利用其成熟的插件生态、Tree-shaking 与代码分割能力,生成可在静态托管环境下高效运行的资源包。(vitejs, vitejs)

代码分割与 Tree-shaking

Rollup 会根据 ESM 语法分析出未使用的代码并剔除,结合动态 import(),进行自动代码分割,实现按需加载和最小化包体积。(vitejs, Vite)

插件机制

Vite 的插件系统基于 Rollup 插件 API 扩展,支持自定义模块解析、加载、转换等各环节的钩子,同时提供虚拟模块(virtual modules)机制,极大增强了可扩展性与灵活性。(vitejs, vitejs)

性能优化策略

冷启动优化

通过依赖预构建与原生 ESM,Vite 启动时只需解析并加载必要模块,可在毫秒级完成冷启动,大幅优于传统打包工具的编译等待。(vitejs, vitejs)

构建速度优化

在开发阶段借助 Rust 编写的 esbuild 实现超高速 JS/TS 转换,在生产构建中分离 JS 与 CSS 处理,进一步提升整体构建速度。(vitejs, 掘金)

小结

Vite 通过创新的原生 ESM 加载、esbuild 预构建、按需编译与请求拦截,以及 Rollup 生产构建与灵活插件系统,实现了快速冷启动、即时 HMR 和高效构建的完美平衡,堪称现代前端开发的理想工具。(xiaohanglin.site, vitejs)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GISer_Jinger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值