Webpack 记录 - webpack 5 变更记录

1,788 阅读4分钟

webpack.docschina.org/blog/2020-1…

引言

2020年10月,webpack 发布了 webpack 5.0.0。作为前端工程化的核心能力,webpack团队考虑到开发者的学习成本,并没有做成重大的 API 升级(ღ( ´・ᴗ・)ღ), 对比了一下 webpack4,对一些核心的升级点做下记录并分享 `


Node.js Polyfills 不再引入

webpack 5 开始,更加专注于前端工程构建,不再自动填充 Node Api 垫片,提高 web 构建效率

webpack 4

import { crypto } from 'crypto';

console.log('node 模块测试');

这些 polyfill 比较庞大,浪费性能,webpack 本身也给出了警告,我们需要通过其他途径来优化(polyfill.io)

webpack 5

import { crypto } from 'crypto';

console.log('node webpack5 模块测试');

webpack 5 不再引入并编译失败,希望你为这些 node api, 配置 polyfill

如果你的项目中使用到了 Node 模块,我们可以通过如下配置来开启:

// webpack.config.js
module.exports = {
  mode: 'production',
  ...
  resolve: {
    fallback: {
      // 以 crypto 举例, 如果使用到某些 api,可通过如下配置来兼容
      "crypto": require.resolve("crypto-browserify"),
      // 完全没用到,不引入垫片,可通过如下配置来关闭
      "crypto": false
    }
  }
}

支持资源模块编译 webpack5 开始,对各种资源文件进行编译能力内置,jpg/gif/txt/.. 等资源文件不再需要 通过file-loader, url-loader 等资源处理 Loader 进行拓展编译了。

现在 webpack 天然支持如下模块webpack 天生支持如下模块类型`

  • ECMAScript 模块
  • CommonJS 模块
  • AMD 模块
  • Assets
  • WebAssembly 模块

此前,我们通过 url-loader 来对部分小文件进行优化( < 1024 * 5 转换 base64)

现在对于公共资源类的规则配置,可通过下面几种方式来完成:

// webpack.config.js

// 方式一
module.exports = {
  mode: 'production',
  ...
  // 写法一
  module: {
    generator: {
      'asset': {},
      'asset/inline': {},
      'asset/resource': {},
      'asset/source': {},
    }
  }
  // 写法二
  module: {
    parser: {
      'asset': {},
      'asset/inline': {},
      'asset/resource': {},
      'asset/source': {},
    }
  }
  // 写法三
  module: {
  	rules: [
         {
             test: /\.jpg$/,
             type: 'asset/resource',
             parser: {},
         },
         {
             test: /\.png$/,
             type: 'asset/inline',
             parser: {},
         },
         {
             test: /\.txt$/,
             type: 'asset/source',
             parser: {},
         }
      ]
  }
}

webpack 5 内部各模块对应方式如下 asset/inline => url-loader, asset/source => raw-loader, asset/resource => file-loader。

更详情的规则可以点击这里

持久化编译缓存机制更新

webpack 5 中,新更新了缓存策略,通过缓存机制的使用,可以大幅提高我们开发、编译的构建速度。

webpack 4

webpack < 5 中,我们通常会使用 babel 插件各类配置或者 manifest.json 文件索引来优化构建速度

webpack 5

添加测试代码:


// webpack.config.js
module.exports = {
  mode: "production",
  ...
  module: {
    rules: [
      {
         test: /\.(js|jsx)$/,
         use: [
            {
              loader: "babel-loader",
              options: {
                  presets: [
     			   "@babel/preset-react"
                  ]
              }
             }
          ]
      }
    ]
  },
  plugins: [
     new HtmlWebpackPlugin({
        template: './public/index.html'
     }),
  ],
  ...
}


// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app.jsx';

ReactDOM.render(<App />, document.getElementById('root'));


//app.jsx
import React from 'react';
import { fnA1 } from './moduleA';

class App extends React.Component {

  componentDidMount() {
    fnA1();
  }
  
  render() {
    return <div>hello react</div>;
  }
}

export default App;

webpack --config webpack.config.js

两次构建的输出如下:

第一次

第二次

两次构建时间接近 3000ms,接下来开启 webpack5 长缓存机制

// webpack.config.js
module.exports = {
  mode: "production",
  ...
  cache: {
  	type: "fileSystem"
  }
  ...
}

第二次构建,可以看到速度提高了6倍。

可以在 node_modules/.cache/webpack 中找到缓存文件, 可通过 cacheDirectory 配置来修改缓存路径。

实际使用中,webpack 中提供fileSystem | memory 两种方式, memory 会将构建产物缓存至系统内存中提高构建速度,这种模式只能在 development 模式中使用。

默认情况下,webpack 5 在生产模式的构建中,会使用 文件 + 内容 进行 hash 值计算,在开发模式则使用时间戳进行缓存机制处理,配合长期缓存算法机制,webpack5 还更新了moduleIds 、chunkIds 的生成机制【看下面】。

Optimization 之 moduleIds 、chunkIds的变更

【Tip】 module 和 chunk 可以简单理解为一个是编译前的文件,一个是编译后的产物,一个文件对应一个 module, 一个代码块对应一个 chunk, 不同 chunk 间相互引用。通常,运行在浏览器上的脚本又称之为 bundle。

webpack 5 之前,异步模块以数字的形式递增的方式输出,这样缓存机制在某些情况下就会失效

webpack4

webpack4 增加代码

// demo/index.js 

import { comm1, comm2 } from './d';

(async () => {
  await import('./a');
  await import('./b');
  await import('./c');
})()

console.log(comm1, comm2);

// webpack.config.js
module.exports = {
  mode: "production",
  ...
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 500,
      minChunks: 1,
      cacheGroups: {
        defaultVendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10,
          reuseExistingChunk: true,
        },
        default: {
          minChunks: 2,
          priority: -20,
          reuseExistingChunk: true,
        },
      },
    }
  },
}

编译输出如下:

webpack5

同样的文件,在 webpack 5 中,会输出为短数字 id。

webpack 5,会在 production 模式下,默认开启 chunkIds: "deterministic"模式,将文件内容进行内容级别的缓存。

更强大的 tree-shaking

webpack 还有一个重要的优化算法就是 tree-shaking, webpack 5 相比 webpack4,对 tree-shaking 算法进行了更进一步的增强:支持嵌套的模块的 tree-shaking 及 内部模块 tree-shaking

webpack 4

增加代码

// a.js
function a () {
  console.log('a')
}

function b () {
  console.log('b')
}

export default {
  a, b
}

// index.js
import a from './a'

console.log(a.a());
console.log('hello world');

webpack 4 构建输出:

webpack 4 查找 a 模块中存在 a.a() 的引入,于是将 a 模块进行引入。

webpack 5

同样的引入方式及使用,webpack 5 则更加智能,会对多层依赖模块进行优化:

可以发现,b 被正确的剔除了。

模块联邦

除了上述几个比较关键的配置或优化变更外,webpack 对于远程异步加载 bundle 新增了模块联邦的加载机制,它允许多个 webpack 构建产物一起工作。允许从指定的远程构建中导入,并以最小的限制来使用远程的 js bundle。

这种加载机制,非常适用于微前端的加载方式,因为微前端本身的机制也是跨域加载一段 js 脚本来插入第三方模块视图。

后续会更新联邦模块的测试 demo。

总结


总体来看,webpack 5 为了降低开发者的学习成本,尽可能小的优化了核心算法及模块优化机制,让 webpack 功能更加强大,确不增加我们的学习成本,这使得我们老项目迁移升级 webpack5 变的更加简单。关键的更新点可以总结如下:

  • 持久性缓存来提高构建性能。
  • 更加强大的 Tree Shaking 算法
  • 编译能力的增加,更加关注于 web 构建的能力
  • 更好的内容级 hash 算法改进长期缓存
  • 面向现代化复杂业务的解决方案