Skip to content

external

  • 类型:string | RegExp | (string | RegExp)[] | ((id: string, parentId: string | undefined, isResolved: boolean) => NullValue<boolean>)

    ¥Type: string | RegExp | (string | RegExp)[] | ((id: string, parentId: string | undefined, isResolved: boolean) => NullValue<boolean>)

  • 可选:是 ✅

    ¥Optional: Yes ✅

指定哪些模块应被视为外部模块而不进行打包。外部模块将在输出中保留为导入语句。

¥Specifies which modules should be treated as external and not bundled. External modules will be left as import statements in the output.

示例

¥Examples

字符串模式

¥String pattern

js
export default {
  external: 'react',
};

正则表达式

¥Regular expression

js
export default {
  external: /node_modules/,
};

模式数组

¥Array of patterns

js
export default {
  external: ['react', 'react-dom', /^lodash/],
};

深入探讨

¥In-depth

⚠️ 除非必须,否则不要使用函数

¥⚠️ Don't use function unless you have to

使用函数形式会带来显著的性能开销,因为 Rolldown 是用 Rust 编写的,并且必须为依赖图中的每个模块从 Rust 调用 JavaScript 函数。

¥Using the function form has significant performance overhead because Rolldown is written in Rust and must call JavaScript functions from Rust for every module in your dependency graph.

性能影响:

¥Performance Impact:

  • 每个模块都会触发一次 Rust 到 JS 的调用

    ¥Each module triggers a Rust-to-JS call

  • 跨语言调用开销高

    ¥Cross-language call overhead is high

  • 可能会显著降低大型项目的构建速度

    ¥Can significantly slow down builds in large projects

尽可能使用静态模式:

¥Use static patterns when possible:

js
// ❌ Avoid: Function with performance overhead
export default {
  external: (id) => {
    return !id.startsWith('.') && !id.startsWith('/');
  },
};

// ✅ Prefer: Static pattern (much faster)
export default {
  external: [
    'react',
    'react-dom',
    'vue',
    /^lodash/,
    /^@mui/,
  ],
};

何时使用函数:

¥When to use function:

  • 你需要基于 parentIdisResolved 的真正动态逻辑。

    ¥You need truly dynamic logic based on parentId or isResolved

  • 逻辑无法用静态模式表达

    ¥The logic cannot be expressed with static patterns

  • 你可以接受性能方面的权衡

    ¥You're okay with the performance trade-off

⚠️ 不要使用 /node_modules/ 匹配 npm 包

¥⚠️ Don't use /node_modules/ to match npm packages

使用 /node_modules/ 外部化 npm 包存在问题,因为 Rolldown 在解析过程中会匹配两次模块 ID。

¥Using /node_modules/ to externalize npm packages is problematic because Rolldown matches module IDs twice during resolution.

import Vue from 'vue' 示例:

¥Example with import Vue from 'vue':

  1. 首次匹配(未解析的 ID):'vue'

    ¥First match (unresolved ID): 'vue'

    • 模式 /node_modules/ 不匹配

      ¥Pattern /node_modules/ does NOT match

    • 这是裸包名称

      ¥This is the bare package name

  2. 第二次匹配(解析后的 ID):'/path/to/project/node_modules/vue/dist/vue.runtime.esm-bundler.js'

    ¥Second match (resolved ID): '/path/to/project/node_modules/vue/dist/vue.runtime.esm-bundler.js'

    • 模式 /node_modules/ 匹配

      ¥Pattern /node_modules/ DOES match

    • 这是完整的解析文件路径

      ¥This is the full resolved file path

问题:

¥The Problem:

由于模式仅匹配已解析的 ID,Rolldown 会生成带有绝对路径的导入:

¥Since the pattern only matches on the resolved ID, Rolldown generates imports with absolute paths:

js
// ❌ Bad result: Absolute path in output
import Vue from '/Users/somebody/project/node_modules/vue/dist/vue.runtime.esm-bundler.js';

这会破坏可移植性,并且无法按预期工作。

¥This breaks portability and doesn't work as intended.

更好的替代方案:

¥Better alternatives:

  • 使用精确的包名称

    ¥Use exact package names

js
export default defineConfig({
  external: ['vue', 'react', 'react-dom'],
});
  • 使用包名称模式

    ¥Use package name patterns

js
export default {
  external: [/^vue/, /^react/, /^@mui/],
};
  • 匹配裸标识符

    ¥Match bare identifiers

用于匹配所有裸模块 ID(不以 ./ 开头)的模式 (visualize):

¥Pattern (visualize) to match all bare module IDs (not starting with . or /):

js
export default {
  external: /^[^./]/,
};