moduleSideEffects 是 Rollup treeshake 中控制外部模块副作用假设的配置项,非“强力阻断开关”,而是影响“该不该删”的决策:true 默认保守保留外部模块,false 冒险忽略其副作用,'no-external' 最推荐——仅对本地模块依据 sideEffects 字段判断,对外部模块只按实际导入使用剔除。

Rollup 的 treeshake.moduleSideEffects 参数不是用来“更强力阻断”副作用的开关,而是让 Rollup **更保守、更精确地判断模块是否真有副作用**——它解决的是“该不该删”的决策问题,而非“能不能删”的能力问题。真正起作用的,是配合 package.json 中的 "sideEffects" 字段 + 模块实际结构 + 导入方式,共同构建一个可信赖的副作用声明体系。
moduleSideEffects 是什么?
它是 Rollup 构建配置中 treeshake 选项下的一个子配置项,类型为 boolean | 'no-external'(默认 true),控制 Rollup 是否将**外部依赖(即 node_modules 中的第三方包)视为可能含副作用**:
-
moduleSideEffects: true(默认):Rollup 假设所有外部模块都可能有副作用,哪怕你只 import 了其中某个函数,只要该包没在自己的package.json中声明"sideEffects": false,Rollup 就不敢删它内部其他未被引用的导出,甚至可能保留整个模块的顶层执行逻辑。 -
moduleSideEffects: false:Rollup 完全忽略外部模块的副作用假设——即把它们当作纯 ESM 模块对待,仅依据 import/export 关系做静态分析。⚠️ 风险极高:若某库确实在入口文件里执行了document.body.classList.add('bootstrap')这类操作,禁用后可能被误删,导致运行时崩溃。 -
moduleSideEffects: 'no-external'(推荐):Rollup 仅对**你自己项目内的模块**(src/下)应用副作用判断逻辑(比如读取你项目的package.json的sideEffects字段),而对所有node_modules中的包,**完全不假设其有副作用,也不主动信任其sideEffects字段**——转而严格依赖其导出是否被实际使用。这是最平衡、最可控的策略。
为什么不能靠它“强力阻断”,而要靠组合策略?
第三方包的隐式副作用(如自动注入样式、注册全局指令、修改原型等)往往藏在模块顶层执行代码里,不通过 export 暴露,Rollup 静态分析根本“看不见”。此时:
-
moduleSideEffects: false不是加强控制,而是放弃控制——相当于蒙眼摇树,危险且不可靠; - 真正能“阻断干扰”的,是你主动做三件事:
① 优先选用已声明"sideEffects": false的库(如lodash-es、date-fns);
② 对未声明的库,改用明确的子路径导入(import { debounce } from 'lodash-es/debounce'),避开主入口的副作用代码;
③ 在你自己的package.json中写"sideEffects": ["*.css", "src/init.js"],精准放行真实副作用,其余一律标记为无副作用。
Rollup 配置示例(推荐组合)
以下配置既规避了盲目信任第三方包的风险,又保留了对你自己代码的最大优化空间:
export default {
input: 'src/main.ts',
output: { format: 'es', dir: 'dist' },
treeshake: {
moduleSideEffects: 'no-external', // 关键:外部包不猜,只看是否被引用
propertyReadSideEffects: false, // 可选:避免因读取对象属性(如 utils.xxx)误判为有副作用
unknownGlobalSideEffects: false // 可选:禁用对未声明全局变量的副作用假设
}
};
再配合你项目根目录的 package.json:
{
"sideEffects": ["*.css", "src/polyfills.ts"]
}
这样,Rollup 就会:对你写的 src/utils.ts 严格按 sideEffects: false 处理(可安全摇掉未用函数);对 node_modules/lodash-es,只打包你 import 的那个函数,其余导出和顶层副作用代码(如果存在)——只要没被引用,就直接剔除。


















