人人都会AI编程

无用代码剔除与副作用处理

更新时间:2026-07-11

打包时自动移除未被使用的代码,称为 Tree Shaking。理想情况下,你只引用了一个工具库里的某个函数,最终打包产物里就只包含那个函数,而不是整个库。这个效果的实现需要两个前提:工具库使用 ES Module 格式导出,并且打包工具(Vite/Webpack)能正确识别“哪些导出被用到了”。

然而,实际项目中常出现一个陷阱:看似没用的代码,打包工具不敢删,因为它可能有“副作用”

什么是“副作用”

在 JavaScript 模块中,副作用指模块执行时除了导出值之外,还对全局环境产生了影响。比如:

// utils.js
export function formatDate(date) { /* ... */ }
console.log('utils 模块已加载')      // 副作用:全局日志
window.__globalConfig = { mode: 'prod' } // 副作用:修改全局对象

如果 formatDate 没被任何地方 import,打包工具本可以删掉它。但因为这个文件在顶层有 console.logwindow 赋值,打包工具会“担心”:如果执行这些代码是程序正确运行的前提呢? 为了安全,它会把整个模块都留下来。这就导致一个没被使用的函数,因为同文件里的一句 console.log 而无法被 Tree Shaking。

如何声明“无副作用”

如果确定某个模块的顶层代码不会产生全局影响,可以在 package.json 中声明:

{
  "sideEffects": false
}

这会告诉打包工具:“放心,我项目里所有文件都是纯净的,没被 import 的东西可以直接删。” 但更常见的写法是精确指定哪些文件有副作用:

{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/some-side-effect-file.js"
  ]
}

因为 CSS 文件的导入通常被视为副作用(样式注入本身就是目的),需要显式保留。而像全局注册的自定义指令文件、polyfill 文件,也应当列入 sideEffects,否则它们可能在使用时被误删。

实际项目中的剔除策略

1. 按需引入组件库
使用 Element Plus 等大型组件库时,全量导入会让打包体积激增。推荐按需引入:

// ❌ 全量引入(约1MB+)
import ElementPlus from 'element-plus'
app.use(ElementPlus)

// ✅ 按需引入(只打包用到的组件)
import { ElButton, ElInput } from 'element-plus'

配合 unplugin-vue-components 插件,可以实现自动按需引入,连手动 import 都不需要。

2. 工具库按需加载
Lodash、moment 这类工具库更应谨慎:

// ❌ 引入整个 moment(约 230KB)
import moment from 'moment'

// ✅ 使用 dayjs 替代(2KB),或按需引入
import dayjs from 'dayjs'

对于 Lodash,使用 lodash-es(ES Module 版本)结合具名导入:

// ✅ 只打包 debounce 函数
import { debounce } from 'lodash-es'

3. 检查打包产物
rollup-plugin-visualizer(Vite)或 webpack-bundle-analyzer 生成可视化报告,可以直接看到哪些模块占用了异常体积。发现一个 800KB 的 moment 时,就是该动手替换的信号。

4. 注意开发环境专用代码
console.logalert 等调试代码在开发时保留,生产环境应该去掉。Vite 默认在生产构建时移除 console.logdebugger,但更精细的控制可以用 drop_console: true 配置 Terser 插件。

一句实用原则

写代码时别让模块顶层产生副作用,除非你明确知道它必须保留。 把工具函数、常量放在纯导出文件里,而把全局样式、polyfill、全局注册逻辑集中到少数文件中,并正确配置 sideEffects。这样,打包工具才能真正把“你写但没用到的代码”清理干净,而不是被迫带着一堆死代码上线。