目录

Webpack5的新特性以及项目实战配置

webpack5也已经发布了(2021.08)接进一年了,之前刚出来的时候还有些坑也不敢急着实际应用在项目中,现在官方的bug也修的差不多了,提出了很多新特性,我也实际在项目中运用了下,所以现在做个总结

1.用持久性缓存来提高构建性能

在webpack4中,我们可以通过cache-loader等手段来将编译的结果写入到磁盘中,现在可以直接通过设置将编译结果缓存到文件系统中

type:string: 'memory' | 'filesystem' ,将 cache 类型设置为内存或者文件系统

buildDependencies:object,针对构建的额外代码依赖的数组对象。webpack 将使用这些项和所有依赖项的哈希值来使文件系统缓存失效

1
2
3
4
5
6
cache: {
  type: 'filesystem', // 将缓存类型设置为文件系统,默认为memory
  buildDependencies: {
    config: [__filename], // 这样设置后webpack能获取最新的配置以及依赖项来重新缓存
  },
},

2.对资源实现了内部支持而不需要额外的loader

asset/resource 发送一个单独的文件并导出 URL。之前通过使用 file-loader 实现。

asset/inline 导出一个资源的 data URI。之前通过使用 url-loader 实现。

asset/source 导出资源的源代码。之前通过使用 raw-loader 实现。

asset 在导出一个 data URI 和发送一个单独的文件之间自动选择。之前通过使用 url-loader,并且配置资源体积限制实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
module.export = {
  module: {
    rules: [{
      test: /\.png$/,
      type: "asset/resource", //对应file-loader
    },
    {
      test: /\.svg$/,
      type: "asset/inline", //对应url-loader 大小<limt 转化为base64
    },
    {
      test: /\.txt$/,
      type: "asset/source", //对应raw-loader
    },
    {
      test: /\.gif$/,
      type: "asset", //自动选择
      parser: {
        dataUrlCondition: {
          maxSize: 4 * 1024,
        },
      },
    }],
  },
}

3.用更好的 Tree Shaking 和代码生成来改善包大小

现在的Tree Shaking 能够完全剔除未使用的代码,同样使用ES6模块,并开起production mode;并且现在能处理对 Commonjs 的 tree shaking,允许消除未使用的CommonJs导出并跟踪require()调用中引用的导出名称,在打包出的文件中可以看到最终的代码。

4.moduleIds & chunkIds & content hash 的优化

moduleIds & chunkIds的优化

在webpack5以前,没有从entry打包的chunk文件,都会以1,2,3…的文件命名方式输出,文件名称后的hash值是用chunkhash生成的。这样会造成当删除或者暂时不用1.js这个文件后,那么重新打包时2.js->1.js,3.js->2.js,这样会造成原来线上的2.js和3.js缓存失效。

webpack5为了确保moduleId,chunkId 的确定性, 增加了如下配置:

optimization.moduleIds

选荐值 描述
natural 按使用顺序的数字 id。
named 对调试更友好的可读的 id。
deterministic 被哈希转化成的小位数值模块名。
size 专注于让初始下载包大小更小的数字 id。

deterministic 选项有益于长期缓存,但对比于 hashed 来说,它会导致更小的文件 bundles。数字值的长度会被选作用于填满最多 80%的 id 空间。 当 optimization.moduleIds 被设置成 deterministic,默认最小 3 位数字会被使用。deterministic配置在生产模式下是默认开启)

1
2
optimization.moduleIds = 'deterministic'
optimization.chunkIds = 'deterministic'

当然在开发模式下,可以设置成natural以保持以前的老模式

content hash的优化

之前使用content hash的时候,当改动文件内容的时候content hash会重新生成;而现在Webpack 5 将使用真正的文件内容哈希值,也就是说当进行了修改注释或者修改变量名等代码逻辑是没有影响的操作时,文件内容的变更不会导致 contenthash 变化。

5.支持生成ES6/ES2015的代码

webpack 4 默认只能输出 ES5 代码,现在支持指定环境,然后生成该环境的一个代码

target

string [string] false

告知 webpack 为目标(target)指定一个环境。默认值为 "browserslist",如果没有找到 browserslist 的配置,则默认为 "web"

然后我们设置

target: ['web', 'es5']

webpack 将生成 web 平台的运行时代码,并且只使用 ES5 相关的特性

6.移除了 Node.js Polyfills

在早期,webpack 的目的是为了让大多数的 Node.js 模块运行在浏览器中,但如今模块的格局已经发生了变化,现在许多模块主要是为前端而编写。webpack <= 4 的版本中提供了许多 Node.js 核心模块的 polyfills,一旦某个模块引用了任何一个核心模块(如 cypto 模块),webpack 就会自动引用这些 polyfills。

尽管这会使得使用为 Node.js 编写模块变得容易,但它在构建时给 bundle 附加了庞大的 polyfills。在大部分情况下,这些 polyfills 并非必须。

从 webpack 5 开始不再自动填充这些 polyfills,而会专注于前端模块兼容。我们的目标是提高 web 平台的兼容性。

迁移

  • 尽量使用前端兼容的模块。
  • 可以手动为 Node.js 核心模块添加 polyfill。错误提示会告诉你如何实现。
  • Package 作者:在 package.json 中添加 browser 字段,使 package 与前端兼容。为浏览器提供其他的实现/dependencies。

7.Module Federation

多个独立的构建可以组成一个应用程序,这些独立的构建之间不应该存在依赖关系,因此可以单独开发和部>署它们。

这通常被称作微前端,但并不仅限于此。

简单来说就是允许一个应用中动态地去加载和引入另一个应用的代码,就好像本地起一个应用作CDN库,其它地方去引用,具体可以看官方配置,个人感觉并不是特别的实用

8.项目实战

首先定义一个webpack.config.base.js,即开发环境以及生产环境都需要的配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
const path = require('path')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const MiniCssExtractPlugin = require("mini-css-extract-plugin")

module.exports = {
  entry: './src/index.js', 
  output: {
    filename: '[name].[chunkhash].bundle.js',
    path: path.resolve(__dirname, 'dist') 
  },
  module: {
    rules: [{
        test: /\.m?js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader', 
        }
      },
      {
        test: /\.s[ac]ss$/, 
        use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
      },
      {
        test: /\.(png|svg|jpg|gif|webp|jfif)$/,
        use: [{
          loader: 'url-loader',
          options: {
            limit: 1024 * 100,
            name: 'assets/[name]_[hash:10].[ext]'
          }
        }]
      }
    ]
  },
  plugins: [
    new CleanWebpackPlugin({ // 每次打包前删除dist文件夹中的文件
      cleanOnceBeforeBuildPatterns: ['**/*', '!favicon.ico', '!lib/**'], // dist文件夹下的favicon.ico文件和lib文件夹下的东西都忽略不进行删除
    }),
    new HtmlWebpackPlugin({
      template: 'index.html', 
      favicon: 'favicon.ico', 
      inject: 'head' 
    }),
    new MiniCssExtractPlugin({
      filename: '[name]-[contenthash].css'
    }),
  ],
};

然后配置webpack.config.dev.js,开发环境主要配置代理服务器,代码热更新,以及使用最新的缓存特性

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
const { merge } = require('webpack-merge')
const path = require('path')
const baseConfig = require('./webpack.base.js')
const ReactRefreshPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
const webpack = require('webpack')

module.exports = merge(baseConfig, {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  devServer: { 
    contentBase: path.resolve(__dirname, 'dist'),
    port: '8090',
    proxy: { 
      '/api': {
        target: 'http://127.0.0.1:3001',
        pathRewrite: {
          '^/api': '
        }
      },
    },
    hot: true,
    open: true,
  },
  // 热更新
  plugins: [
    new ReactRefreshPlugin({
      overlay: false,
    }),
    new webpack.HotModuleReplacementPlugin()
  ],
  // 缓存
  cache: {
    type: 'filesystem',
    buildDependencies: {
      config: [__filename],
    },
  },
})

最后是生产环境的配置,生产环境的配置主要是对代码进行一个压缩

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const { merge } = require('webpack-merge') 
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin")
const TerserJSPlugin = require("terser-webpack-plugin")
const baseConfig = require('./webpack.base.js')

module.exports = merge(baseConfig, {
  mode: "production",
  optimization: {
    minimizer: [new OptimizeCSSAssetsPlugin({})],
    minimizer: [new TerserJSPlugin({}), new OptimizeCSSAssetsPlugin({})] 
  }
})

以上就是一个webpack5的基本配置,可以看到除了其内部更新的特性意外,其它的还是可以用原来的loader去配置,主要用到的还是它的缓存的一个功能,当然其它的你也可以按照官方文档进行一个配置

9.参考链接

webpack官方网站

webpack新特性