You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在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里添加条目就行

要是你在配置过程中遇到奇怪的问题,比如某些包没被替换、子路径处理异常,随时说细节,我再帮你调整!

火山引擎 最新活动