下载资源文件、相关配置

优化

效率分析工具

安装以下 webpack 插件,可以帮助我们分析优化效率:

  • progress-bar-webpack-plugin:查看编译进度
  • speed-measure-webpack-plugin:查看编译速度
  • webpack-bundle-analyzer:打包体积分析

安装:

  1. yarn add -D progress-bar-webpack-plugin
  2. yarn add -D speed-measure-webpack-plugin
  3. yarn add -D webpack-bundle-analyzer

编译进度条

webpack.common.js 配置方式如下:

  1. const chalk = require('chalk')
  2. const ProgressBarPlugin = require('progress-bar-webpack-plugin')
  3. module.exports = {
  4. plugins: [
  5. // 进度条
  6. new ProgressBarPlugin({
  7. format: ` :msg [:bar] ${chalk.green.bold(':percent')} (:elapsed s)`
  8. })
  9. ],
  10. }

编译速度分析

webpack.dev.js 配置方式如下:

  1. const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
  2. const smp = new SpeedMeasurePlugin();
  3. module.exports = smp.wrap({
  4. // ...webpack config...
  5. })

打包体积分析

使用 webpack-bundle-analyzer查看打包后生成的 bundle 体积分析,将 bundle 内容展示为一个便捷的、交互式、可缩放的树状图形式。帮助我们分析输出结果来检查模块在何处结束。
webpack.prod.js 配置方式如下:

  1. const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer");
  2. module.exports = {
  3. plugins: [
  4. // 打包体积分析
  5. new BundleAnalyzerPlugin()
  6. ],
  7. }

package.json中修改启动命令

  1. "analyzer": "cross-env NODE_ENV=production webpack --progress --mode production"

执行编译命令 npm run analyzer,打包结束后自行启动地址为 http://127.0.0.1:8888

开发环境优化

热更新

webpack-dev-server

使用 webpack 内置的 HMR 插件,更新 webpack-dev-server 配置。

webpack.dev.js 配置方式如下:

  1. module.export = {
  2. devServer: {
  3. static: './dist',
  4. hot: true, // 热更新
  5. },
  6. }

react-refresh-webpack-plugin

使用 react-refresh-webpack-plugin 热更新 react 组件。

安装:

  1. yarn add -D @pmmmwh/react-refresh-webpack-plugin react-refresh

webpack.dev.js 配置方式如下:

  1. const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
  2. module.exports = {
  3. plugins: [
  4. new webpack.HotModuleReplacementPlugin(),
  5. new ReactRefreshWebpackPlugin(),
  6. ]
  7. }

Source Map

eval-cheap-module-source-map

  • 本地开发首次打包慢点没关系,因为 eval 缓存的原因,rebuild 会很快
  • 开发中,我们每行代码不会写的太长,只需要定位到行就行,所以加上 cheap
  • 我们希望能够找到源代码的错误,而不是打包后的,所以需要加上 module

加快构建速度

缓存

cache持久化缓存

通过配置 cache 缓存生成的 webpack 模块和 chunk,来改善构建速度。

webpack.common.js 配置方式如下:

  1. module.exports = {
  2. cache: {
  3. type: 'filesystem', // 使用文件缓存
  4. },
  5. }

优化loader、plugins的配置

减少 loader 配置数量

每个的 loader、plugin 都有其启动时间。尽量少地使用工具,将非必须的 loader、plugins 删除。

使用 webpack 资源模块 (asset module) 代替旧的 assets loader
使用 webpack cache 代替 cache-loader、dll、hard-source-webpack-plugin

  • file-loader
  • url-loader
  • raw-loader
  • cache-loader(缓存一些性能开销比较大的 loader 的处理结果)
  • autodll-webpack-plugin
  • hard-source-webpack-plugin(为模块提供了中间缓存,重复构建时间大约可以减少 80%)

为loader指定 include/exclude

loader 指定 include\exclude,减少 loader 应用范围,仅应用于最少数量的必要模块。

  • include:符合条件的模块进行解析
  • exclude:排除符合条件的模块,不解析,exclude 优先级更高

如:

  1. {
  2. test: /\.css$/,
  3. include: paths.appSrc,
  4. use: [
  5. // 将 JS 字符串生成为 style 节点
  6. "style-loader",
  7. // 将 CSS 转化成 CommonJS 模块
  8. "css-loader",
  9. ],
  10. },

缩小webpack解析的范围

优化resolve配置

resolve用来配置 webpack 如何解析模块,可通过优化 resolve 配置来覆盖默认配置项,减少解析范围:

  • alias
  • extensions
    • 高频文件后缀名放前面
    • 手动配置后,默认配置会被覆盖,可以用 … 扩展运算符代表默认配置
  • modules
    • 告诉 webpack 优先 src 目录下查找需要解析的文件,会大大节省查找时间
  • symlinks
    • 如果项目不使用 symlinks(例如 npm link 或者 yarn link),可以设置 resolve.symlinks: false,减少解析工作量。(npm包开发调试用)

webpack.common.js 配置方式如下:

  1. module.exports = {
  2. resolve: {
  3. alias: {
  4. '@': paths.appSrc, // @ 代表 src 路径
  5. },
  6. extensions: ['.tsx', '.ts', '.js', '...'],
  7. modules: ['node_modules', paths.appSrc],
  8. symlinks: false,
  9. }
  10. }

使用externals配置

externals 配置选项提供了「从输出的 bundle 中排除依赖」的方法,我们可以用这样的方法来剥离不需要改动的一些依赖,大大节省打包构建的时间。

例如,从 CDN 引入 jQuery,而不是把它打包:

  1. <script
  2. src="https://code.jquery.com/jquery-3.1.0.js"
  3. integrity="sha256-slogkvB1K3VOkzAI8QITxV3VzpOnkeNVsKvtkYLMjfk="
  4. crossorigin="anonymous"
  5. ></script>

webpack.common.js 配置方式如下:

  1. module.exports = {
  2. externals: {
  3. jquery: 'jQuery',
  4. },
  5. }

使用jQuery

  1. import $ from 'jquery';
  2. $('.element').animate(/* ... */);

多线程编译构建

通过 thread-loader 将耗时的 loader 放在一个独立的 worker 池中运行,加快 loader 构建速度。

安装:

  1. yarn add -D thread-loader

使用时,需将此 loader 放置在其他 loader 之前,应该仅在非常耗时loader 前引入 thread-loader,在项目中sass-loader转换时间较长,webpack.common.js 配置方式如下:

  1. module.exports = {
  2. rules: [
  3. {
  4. test: /\.module\.(scss|sass)$/,
  5. include: paths.appSrc,
  6. use: [
  7. 'style-loader',
  8. {
  9. loader: 'css-loader',
  10. options: {
  11. modules: true,
  12. importLoaders: 2,
  13. },
  14. },
  15. {
  16. loader: 'postcss-loader',
  17. options: {
  18. postcssOptions: {
  19. plugins: [
  20. [
  21. 'postcss-preset-env',
  22. ],
  23. ],
  24. },
  25. },
  26. },
  27. {
  28. loader: 'thread-loader',
  29. options: {
  30. workerParallelJobs: 2
  31. }
  32. },
  33. 'sass-loader',
  34. ].filter(Boolean),
  35. },
  36. ]
  37. }

打包结果优化(体积)

  • CssMinimizerWebpackPlugin/ optimize-css-assets-webpack-plugin
  • TerserWebpackPlugin(webpack5自带)
  • SplitChunksPlugin(webpack5自带)
  • MiniCssExtractPlugin

安装:

  1. yarn add -D css-minimizer-webpack-plugin
  2. yarn add -D mini-css-extract-plugin

代码压缩

CSS压缩

CssMinimizerWebpackPlugin 将在 Webpack 构建期间搜索 CSS 文件,优化、压缩 CSS。

webpack.prod.js 配置方式如下:

  1. const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
  2. module.exports = {
  3. optimization: {
  4. minimizer: [
  5. new CssMinimizerPlugin({
  6. parallel: 4,
  7. }),
  8. ],
  9. }
  10. }

JS压缩

webpack5 自带最新的 terser-webpack-plugin,无需手动安装。
terser-webpack-plugin 默认开启了 parallel: true 配置,并发运行的默认数量: os.cpus().length - 1 ,这里配置的 parallel数量为 4,使用多进程并发运行压缩以提高构建速度

在生成环境下打包默认会开启 js 压缩,但是当我们手动配置 optimization 选项之后,就不再默认对 js 进行压缩,需要我们手动去配置。

webpack.prod.js 配置方式如下:

  1. const TerserPlugin = require('terser-webpack-plugin');
  2. module.exports = {
  3. optimization: {
  4. minimize: true, // 开启最小化
  5. minimizer: [
  6. new TerserPlugin({
  7. parallel: 4,
  8. terserOptions: {
  9. parse: {
  10. ecma: 8,
  11. },
  12. compress: {
  13. ecma: 5,
  14. warnings: false,
  15. comparisons: false,
  16. inline: 2,
  17. },
  18. mangle: {
  19. safari10: true,
  20. },
  21. output: {
  22. ecma: 5,
  23. comments: false,
  24. ascii_only: true,
  25. },
  26. },
  27. }),
  28. ]
  29. }
  30. }

代码分离

代码分离能够把代码分离到不同的 bundle 中,然后可以按需加载并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,可以缩短页面加载时间。

抽离重复代码

SplitChunksPlugin 插件开箱即用,可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。

webpack 将根据以下条件自动拆分 chunks:

  • 新的 chunk 可以被共享,或者模块来自于 node_modules 文件夹;
  • 新的 chunk 体积大于 20kb(在进行 min+gz 之前的体积);
  • 当按需加载 chunks 时,并行请求的最大数量小于或等于 30;
  • 当加载初始化页面时,并发请求的最大数量小于或等于 30;

通过 splitChunksreact 等公共库抽离出来,不重复引入占用体积。

webpack.prod.js 配置方式如下:

  1. module.exports = {
  2. optimization: {
  3. minimize: true, // 开启最小化
  4. minimizer: [
  5. new TerserPlugin({
  6. parallel: 4,
  7. terserOptions: {
  8. parse: {
  9. ecma: 8,
  10. },
  11. compress: {
  12. ecma: 5,
  13. warnings: false,
  14. comparisons: false,
  15. inline: 2,
  16. },
  17. mangle: {
  18. safari10: true,
  19. },
  20. output: {
  21. ecma: 5,
  22. comments: false,
  23. ascii_only: true,
  24. },
  25. },
  26. }),
  27. ],
  28. splitChunks: {
  29. // include all types of chunks
  30. chunks: 'all',
  31. // 重复打包问题
  32. cacheGroups:{
  33. vendors:{ // node_modules里的代码
  34. test: /[\\/]node_modules[\\/]/,
  35. chunks: "all",
  36. // name: 'vendors', 一定不要定义固定的name
  37. priority: 10, // 优先级
  38. enforce: true
  39. }
  40. }
  41. },
  42. }
  43. }

CSS分离

MiniCssExtractPlugin 插件将 CSS 提取到单独的文件中,为每个包含 CSS 的 JS 文件创建一个 CSS 文件,并且支持 CSS 和 SourceMaps 的按需加载

webpack.common.js 配置方式如下:

  1. const MiniCssExtractPlugin = require("mini-css-extract-plugin");
  2. module.exports = {
  3. plugins: [new MiniCssExtractPlugin()],
  4. module: {
  5. rules: [
  6. {
  7. test: /\.module\.(scss|sass)$/,
  8. include: paths.appSrc,
  9. use: [
  10. 'style-loader',
  11. isEnvProduction && MiniCssExtractPlugin.loader, // 仅生产环境
  12. {
  13. loader: 'css-loader',
  14. options: {
  15. modules: true,
  16. importLoaders: 2,
  17. },
  18. },
  19. {
  20. loader: 'postcss-loader',
  21. options: {
  22. postcssOptions: {
  23. plugins: [
  24. [
  25. 'postcss-preset-env',
  26. ],
  27. ],
  28. },
  29. },
  30. },
  31. {
  32. loader: 'thread-loader',
  33. options: {
  34. workerParallelJobs: 2
  35. }
  36. },
  37. 'sass-loader',
  38. ].filter(Boolean),
  39. },
  40. ]
  41. },
  42. };

Tree Shaking

Tree-shaking 作用是剔除没有使用的代码,以降低包的体积

JS

JS Tree Shaking 将 JavaScript 上下文中的未引用代码(Dead Code)移除。目前在设置mode为production的时候已经自动开启了tree-shaking。但是要想使其生效,生成的代码必须ES模块不能使用其它类型的模块如CommonJS之类。

Dead Code 一般具有以下几个特征:

  • 代码不会被执行,不可到达;
  • 代码执行的结果不会被用到;
  • 代码只会影响死变量(只写不读)。

CSS

CSS 代码也需要摇树,打包时把没有用的 CSS 代码摇走,可以大幅减少打包后的 CSS 文件大小。

使用purgecss-webpack-plugin 对 CSS Tree Shaking。

安装:

  1. yarn add purgecss-webpack-plugin -D

因为打包时 CSS 默认放在 JS 文件内,因此要结合 webpack 分离 CSS 文件插件 mini-css-extract-plugin 一起使用,先将 CSS 文件分离,再进行 CSS Tree Shaking

webpack.prod.js 配置方式如下:

  1. const glob = require('glob')
  2. const MiniCssExtractPlugin = require('mini-css-extract-plugin')
  3. const PurgeCSSPlugin = require('purgecss-webpack-plugin')
  4. const paths = require('paths')
  5. module.exports = {
  6. plugins: [
  7. // 打包体积分析
  8. new BundleAnalyzerPlugin(),
  9. // 提取 CSS
  10. new MiniCssExtractPlugin({
  11. filename: "[name].css",
  12. }),
  13. // CSS Tree Shaking
  14. new PurgeCSSPlugin({
  15. paths: glob.sync(`${paths.appSrc}/**/*`, { nodir: true }),
  16. }),
  17. ]
  18. }