前端工程化之Webpack---进阶篇

282 阅读4分钟

小知识,大挑战!本文正在参与「程序员必备小知识」创作活动

本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。

引言

本文是weback学习的进阶篇,主要讲解webpack优化方面常用的Tree-Shaking和Code Splitting,关于基础篇请看这里:前端工程化之Webpack---基础篇

Tree-Shaking

Tree-Shaking的本意是摇掉没有用到的代码,即Dead Code Elimination(DCE),它最开始是有rollup提出的, 例如我们在math.js中导出两个方法addminus

// math.js
export function add(a, b) {
  return a + b
}

export function minus(a, b) {
  return a - b
}

然后在index.js中导入add方法并使用

// index.js
import { add } from './math'

add(1,2)
// webpack.config.js
const path = require('path')

const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')

module.exports = {
  mode: 'development',
  devtool: 'cheap-module-source-map',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      {
        test: /.(jpg|png|gif)$/,
        use: {
          loader: 'url-loader',
          options: {
            name: '[name]_[hash].[ext]',
            outputPath: 'images/'
          }
        }
      },
      {
        test: /.css$/,
        use: [
          'style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.js$/,
        use: [
          'loader1',
          'loader2',
          {
            loader: 'loader3',
            options: {
              name: 'alexis'
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    }),
    new CleanWebpackPlugin()
  ],
  resolveLoader: {
    modules: [
      'node_modules',
      path.resolve(__dirname, 'loaders')
    ]
  },
  optimization: {
    usedExports: true
  }
}

然后在package.json中加入以下字段

"sideEffects": false

在控制台运行npx webpack,可以看到使用tree-shaking前后生成的bundle.js

image.png

image.png

可以看到webpack在harmony export字段中提示我们用到了add,而没有用到(unused)minus模块,并且两段代码中都包含了addminus的相关代码,这是因为我们在开发环境下tree-shaking不会删除没有用到的代码,删除的话可能会影响scource-map的映射,如果我们将代码打包上线(production)的话,tree-shaking就会将dead code删除掉,并且如果我们将mode字段值改为prodction的话,webpack内置的优化会自动将tree-shaking相关配置设定好,我们甚至可以不用添加optimization

注意:Tree-Shaking只支持ES Module的引入方式,不支持CommonJS的引入方式,也就是说Tree-Shaking只能检测import导入的模块,对于require引入的模块则不能检测

Code Splitting

假如在开发中,业务代码中一小个片段修改了,比如只修改了一个变量名,而webpack需要打包生成整个文件,并且用户需要重新下载整个资源文件,造成资源/性能浪费,而我们经常会引入一些公共类库,这些库的修改频率是非常低的,因此我们有必要分离公共类库,让多页面利用缓存,从而减少下载的代码量。

Code Splitting本质上与Webpack没有任何关系,只不过webpack的一些插件可以很快帮我们实现code splitting

const path = require('path')

module.exports = {
  mode: 'development',
  devtool: 'eval—cheap-source-map',
  entry: {
    main: './src/index.js'
  },
  output: {
    filename: '[name].js',
    path: path.resolve(__dirname, 'dist')
  },
  module: {
    rules: [
      // ...
    ]
  },
  plugins: [
    // ...
  ],
  optimization: {
    usedExports: true,
    splitChunks: {
        chunks: "async",  // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
        minSize: 30000,  // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
        minChunks: 1,  // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
        maxAsyncRequests: 5,  // 表示按需加载模块时,同时请求的最大数目。默认为5,如果超过5个则不会做代码分割
        maxInitialRequests: 3,  // 表示加载入口文件时,同时请求的最大数目。默认为3。
        automaticNameDelimiter: '~',  // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
        name: true,  // 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。
        cacheGroups: {
            vendors: {
                test: /[\\/]node_modules[\\/]/,
                priority: -10,
                filename: 'vendor.js'
            },
        default: {
                priority: -20,
                reuseExistingChunk: true,    // 
                filename: 'common.js'
            }
        }
    }
  }
}

关于cacheGroups:cacheGroups下可以可以配置多个缓存组,每个组根据test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,如果两个组的条件都符合,最终会根据priority来决定打包到哪个组中,值越大优先级越高。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。 运行npx webpack可以看到dist目录下多生成了一个vendor.js文件

image.png

这是因为我们对optimization作了相应配置,webpack会自动将外部库文件和业务代码分离,自动为我们实现code splitting