Webpack打包瘦身40%:Tree Shaking与Code Splitting实战!

大家好,我是King。欢迎来到今天的实战分享。
很多朋友刚接触Webpack时,总觉得打包出来的文件又大又慢,页面加载像老牛拉车。其实问题往往出在两个地方:一是打包进了很多没用到的代码,二是所有代码都塞进了一个大文件里。
今天咱们就用大白话,把Tree Shaking和Code Splitting这两个“减体积神器”讲明白。跟着配下来,打包体积减少40%不是梦。
一、Tree Shaking:摇掉没用的“死代码”
说白了,Tree Shaking就是一棵树,你摇一摇,枯枝败叶(没被引用的代码)就掉下来了。比如你写了个工具函数库,只用了其中一个,Webpack就会把其他没用的函数从最终包里“摇掉”。
前提条件:  https://www.71wa.com/quanwei/77.html   
使用ES Modules(import / export)  www.187game.com.cn  
生产模式(mode: "production")下自动开启

// utils.js
export function add(a, b) {
  return a + b;
}
export function mul(a, b) {
  return a * b;
}

// index.js
import { add } from './utils.js';
console.log(add(2, 3));
// mul 这个函数不会出现在最终打包文件里

二、Code Splitting:把大包拆成小块
不是所有代码都要一次性加载。Code Splitting就是把代码拆成多个小包,按需加载。  https://www.687game.com.cn/bbcz/124.html 
比如用户打开首页,只需要首页的代码,那么“个人中心”页面的代码就可以等用户点击了再加载。 https://www.687game.com.cn/hjhy/125.html

// webpack.config.js
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
      },
    },
  },
};

三、完整Webpack配置示例
下面给出一个可直接上手用的配置。注意我们把公共依赖和第三方库单独提取出来。

const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: '[name].[contenthash].js',
    path: path.resolve(__dirname, 'dist'),
    clean: true,
  },
  optimization: {
    usedExports: true,
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      minChunks: 1,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
          name: 'vendors',
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    },
    runtimeChunk: 'single',
  },
};

四、标记可被Tree Shaking的代码  https://www.99sf.com.cn/tougao/78.html  https://www.99sf.com.cn/cqgl/77.html  https://www.99sf.com.cn/game/76.html  
在package.json中增加sideEffects字段,告诉Webpack哪些文件有副作用(比如修改了全局变量),不要乱摇。  

{
  "name": "my-project",
  "sideEffects": ["*.css", "*.global.js"]
}

如果没有副作用,直接设为false,这样Webpack会更放心地删除未引用的导出。

{
  "sideEffects": false
}

五、使用动态导入实现路由级代码拆分
这是目前最实用的拆分方式之一。比如我们有一个按钮,点击后才加载某个功能模块。

// 以前静态导入
// import heavyModule from './heavyModule';

// 现在动态导入
button.addEventListener('click', async () => {
  const heavyModule = await import('./heavyModule');
  heavyModule.doSomething();
});

Webpack会自动把heavyModule及其依赖单独打成一个chunk文件,只有点击按钮时才会下载。
六、预防打包重复代码
多个入口或组件可能引用同一个库(例如lodash)。不处理的话,每个包里都会重复打包。用splitChunks可以把公共部分抽出来。

// webpack.config.js 中更细致的公共分组
optimization: {
  splitChunks: {
    cacheGroups: {
      commons: {
        name: 'commons',
        chunks: 'initial',
        minChunks: 2,
        priority: 10,
      },
    },
  },
},

七、查看打包前后体积对比   www.99sf.com.cn
用Webpack插件来可视化分析,看看哪些模块体积大。 

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin(),
  ],
};

跑完打包后浏览器会自动打开一个关系图,哪里体积大、哪里重复了,一眼就看出来。
八、完整的生产环境配置示例(整合版)

module.exports = {
  mode: 'production',
  devtool: 'source-map',
  optimization: {
    usedExports: true,
    minimize: true,
    concatenateModules: true,
    splitChunks: {
      chunks: 'async',
      minSize: 30000,
      maxAsyncRequests: 5,
      maxInitialRequests: 3,
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: 10,
        },
      },
    },
  },
};

新手常见问题和解答
问1:我按教程配了Tree Shaking,但打包体积没怎么变,怎么回事?
答:最常见的原因有三个。第一,你是不是用了require()?Tree Shaking只对ES Modules有效,CommonJS语法(require/module.exports)没法被静态分析,摇不掉。第二,检查package.json里的sideEffects字段,如果没声明或者错误设为true,Webpack会保守处理。第三,你是不是在import后但代码里没直接使用,却通过Object.assign之类的方式间接引用了?那样也会导致没法去除。建议先用webpack-bundle-analyzer看看实际包含的模块。
问2:动态导入的内容太多,导致网络请求变多了,反而加载变慢怎么办?
答:这个问题很实际。拆分过细确实会增加HTTP请求。可以这样调整:在splitChunks配置里调高minSize(比如从20000调高到50000),这样小于50KB的模块就不会单独拆出去了。另外开启maxAsyncRequests和maxInitialRequests限制并行请求数量。更简单的办法是只用路由级别的代码拆分,不要对很小的工具函数做动态导入。通常“1个大包+3-5个按需包”是比较合理的平衡点。

希望今天这篇大白话能帮你真正把打包体积降下来。配置不在多,关键是把usedExports和splitChunks用好,配合动态导入,你的项目加载速度一定会明显提升。咱们下期见!

更多经验分享:

把Codex技能变MCP服务,分享你的专属工具! - 九世師 - 博客园

JS异步四兄弟:9招让嵌套代码直降一半! - 九世師 - 博客园

Vxe Gantt 高级技巧:动态调整任务顺序! - 九世師 - 博客园

posted @ 2026-05-28 19:07  九世師  阅读(19)  评论(0)    收藏  举报