如何在Rollup打包时将外部导入重写为使用esm.sh的CDN路径
如何在Rollup打包时将外部导入重写为使用esm.sh的CDN路径
我之前刚好处理过几乎一模一样的需求,完全懂你不能用importmap的痛点!下面给你两个靠谱的方案,都能精准把你的第三方模块导入转换成esm.sh的CDN路径,不用依赖额外的外部工具链:
方案一:自定义Rollup插件(最灵活可控)
这个方法通过Rollup的原生插件钩子直接拦截并修改导入路径,适合需要处理多个包、子路径复杂的场景,后续维护也更省心。
直接在你的rollup.config.js里添加这个自定义插件:
export default { input: 'src/main.js', output: { file: 'dist/bundle.js', format: 'es' // 必须设为es模块格式,对应页面的<script type="module"> }, plugins: [ { name: 'rewrite-esm-cdn-imports', // 插件名称,方便调试时识别 resolveId(importee) { // 跳过相对/绝对路径的本地文件导入,只处理第三方包 if (importee.startsWith('.') || importee.startsWith('/')) { return null; // 交给Rollup默认逻辑处理 } // 定义需要替换的包及其对应版本 const packageVersionMap = { 'react-dom': '19.2.4', 'react': '19.2.4', // 可以继续添加其他包,比如'lodash-es': '4.17.21' }; // 拆分包名和子路径,比如把'react-dom/client'拆成['react-dom', 'client'] const [basePackage, ...subPaths] = importee.split('/'); if (packageVersionMap[basePackage]) { // 拼接成esm.sh的CDN路径 let cdnUrl = `https://esm.sh/${basePackage}@${packageVersionMap[basePackage]}`; // 如果有子路径,自动拼接到CDN路径后 if (subPaths.length > 0) { cdnUrl += `/${subPaths.join('/')}`; } // 标记为外部模块,Rollup不会打包它,直接保留CDN导入语句 return { id: cdnUrl, external: true }; } // 未匹配到的包,按默认逻辑处理 return null; } } ] };
这个插件的核心逻辑:
resolveId是Rollup处理模块导入时的第一个钩子,我们在这里精准拦截第三方包的导入请求- 只处理非本地路径的导入,确保本地文件的正常打包流程不受影响
- 通过维护
packageVersionMap统一管理包的版本,不用在代码里到处修改路径 - 自动处理带自路径的导入(比如
react-dom/client),完美适配你的需求场景
方案二:用@rollup/plugin-replace(快速实现简单场景)
如果你的需求很简单,只需要替换几个固定的导入,也可以用@rollup/plugin-replace直接做文本替换,上手更快。
首先安装插件:
npm install @rollup/plugin-replace --save-dev
然后在配置文件中添加如下内容:
import replace from '@rollup/plugin-replace'; export default { input: 'src/main.js', output: { file: 'dist/bundle.js', format: 'es' }, plugins: [ replace({ preventAssignment: true, // 避免意外修改代码中的赋值语句 // 用正则匹配所有导入语句,动态替换目标路径 pattern: /from\s+['"]([^'"]+)['"]/g, replacement: (match, importPath) => { // 定义要替换的导入路径映射 const importMap = { 'react-dom/client': 'https://esm.sh/react-dom@19.2.4/client', 'react': 'https://esm.sh/react@19.2.4' }; // 匹配到目标路径就替换,没匹配到就返回原语句 return importMap[importPath] ? `from '${importMap[importPath]}'` : match; } }) ] };
注意事项:
- 这个方法是基于文本替换实现的,要确保正则能覆盖你所有的导入引号格式(单/双引号),上面的正则已经兼容了两种情况
- 如果你的导入语句有特殊格式(比如换行、多余空格),可能需要微调正则表达式来适配
最后必看的小提示
- 无论用哪个方案,都要确保
output.format设置为es,否则打包出来的文件无法在<script type="module">中正常运行 - 打包完成后,记得打开
dist/bundle.js检查一下,确认导入语句已经被替换成你想要的CDN路径 - 如果需要批量处理大量包,方案一的自定义插件会更省心,只需要在
packageVersionMap里添加条目就行
要是你在配置过程中遇到奇怪的问题,比如某些包没被替换、子路径处理异常,随时说细节,我再帮你调整!




