webpack5发布简译

562 阅读7分钟

2020 年真是多事之秋,疫情啊、牛市啊就这样措不及防地来了。。。

前端里也是,被社区推动着学习,之前不小心在 twitter 上刷到 React 17 发布,看完后幸好没什么大改动(hooks 就已经喝够一壶了)。接着掘金里狂刷 Vue 3.0 发布,大航海时代神马的醉了,不过幸好主技术栈不是它 😉。然后当一名老 webpack 配置工程师去官网查文档的时候,纳尼,webpack 5 发布了!

简译于官方文档,加入一些自己的理解(&吐槽),不保证原汁原味,建议查阅官网。

webpack 5 发布(2020-10-10)

距离 2018 年 2 月的 webpack 4 发布已有两年,老实说挺不容易的,因为 webpack 主要都是作者在维护。很多人都不喜欢 breaking changes,特别是 webpack 这种只要能用就行了就不会花时间改它,但是没有 breaking changes 就意味着没法儿进行大更新和优化。

扯了一阵犊子,也确实该优化下了,这启动速度,还有君不见很多神奇的 bug。

这个主要的版本修改了一些默认的配置,以及向未来看齐。

不得不说,很多发布文章都一样,又臭又长,看完就跟没看一样,等着哪天用着不对劲了才发现这个东东改了。。

这次发布主要内容:

  • 打包优化,基于持久缓存 (Persistent Caching)。没用过 cache-loader 直接略过。
  • 长期缓存优化,基于更好的算法Long Term Cacheing,听名词可能不太懂,主要是指打包出来的文件名称是一致的,这样利于浏览器的缓存机制,比如一个单独提取出来的第三方库,每次打包出来名称都一样的话用户只需要第一次访问下载就行了。
  • 打包后的文件大小优化,基于更好的 Tree Shaking,代码生成的算法也优化了。
  • 提升 web 平台的兼容性
  • 清理内部结构。略过
  • 一些 breaking changes 和未来的新功能。

接下来喜闻乐见的这个版本主要改了啥。用到的要改的 😫

主要改动 Major Changes

移除的一些东西:

  • v4 所有的弃用项都被移除了。
  • 新的弃用项。包括 require.include,没用过略过。
  • Node.js Polyfills 被移除了。比如 crypto 模块等

长期缓存 Long Term Cacheing,新的默认算法:

  module.exports = {
      optimization: {
          // v4
-         chunkIds: "size",
-         moduleIds: "size",
-         mangleExports: "size",
          // v5
+         chunkIds: "deterministic",
+         moduleIds: "deterministic",
+         mangleExports: "deterministic",
      },
  };

v4 版本一般都是 contenthash + HashedModuleIdsPlugin 来做长期缓存。

真正的内容哈希。webpack 5 使用真正的文件内容生成哈希,以前只基于内部结构。因此当注释或变量名称改变时会影响长期缓存。压缩了的话没啥变化,甭管。

开发环境新的默认 Chunk ID 命名算法,主要跟动态引入的模块生成后的文件名相关,以前是生成的数字,现在基于其内容,import(/* webpackChunkName: "name" */ 'module') 这种。(生产环境建议继续这样写)

Module Federation

重点!这应该算的上 webpack 5 最大的特性了,让不同项目里的模块能一起工作,说个微前端就都懂了 😂

简单的例子:

// 项目 app_remote 的配置
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: 'app_remote',
            library: { type: 'var', name: 'app_remote' },
            filename: 'remoteEntry.js',
            exposes: {
                './Button': './src/Button.jsx',
            },
            shared: ['react', 'react-dom'],
        }),
    ],
};

使用时,先引入 remoteEntry.js,再定义插件,最后代码中使用:

<!-- index.html -->
<script src="http://localhost:8002/remoteEntry.js"></script>
// webpack.config.js
module.exports = {
    plugins: [
        new ModuleFederationPlugin({
            name: 'app',
            remotes: {
                app_remote: 'app_remote',
                // 或者
                app_remote: 'app_remote@http://localhost:8002/remoteEntry.js',
            },
            shared: ['react', 'react-dom'],
        }),
        new HtmlWebpackPlugin({ template: './index.html' }),
    ],
};
import React from 'react';

// 从 app_remote 项目中引入的模块
const Button = React.lazy(() => import('app_remote/Button'));

export default function Page () {
    return (
        <div>
            <h1>Page title</h1>
            <React.Suspense fallback="Loading...">
                <Button />
            </React.Suspense>
        </div>
    );
}

功能确实强大,不过作为使用者,感觉可能会有许多问题,比如:

  1. webpack 打包模式不同难以调试。app_remote 线上只是生产环境,而本地开发 app 一般都是 development mode,如果说 app_remote 也在本地克隆下来启动,但 remotes 一多总不可能逐个启动吧。
  2. 第三方库版本问题?抑或者 remotes 可用性?
  3. 组件使用者的体验。框架一般都有文档能查阅,这个上哪搞?给项目里的组件写文档能给烦死。
  4. 平时最多的是写业务,如果产品临时把这个项目的业务流程改了,涉及到共用的业务组件咋办。

唉~光是 react、vue 不同的特性就让微前端像是个梦了,何况跨平台乎。不过有这么多大佬推动着前端发展,还是被学习好了 😂

接着 webpack 的改动,一些 web 平台新特性:

  • JSON modules 对齐规范,没有 named exports 又是几个意思。。。貌似之前就能直接导入,内置了 json-loader
  • import.meta。模块的基本信息,目前只规范了个 url 模块绝对地址
  • 资源模块新写法,new URL('./assets/image.png', import.meta.url)。原生的写法
  • 原生 Worker 支持,包括 new Worker/SharedWorker/ navigator.serviceWorker.register。webpack 会单独打包这些文件:new Worker(new URL('./worker.js', import.meta.url))。也是原生写法,要浏览器支持
  • URI 协议支持,包括 data:file://http(s)://。比如:import x from 'data:text/javascript,export default 32'
  • Async modules 支持。不会,略过
  • 新的 external 类型,promise/import/scriptscript 能通过 <script> 标签加载,可以不用写到 html 里了。

Node.js 生态新特性。略过

一些开发体验的优化。target 可以指定多个且指定版本,比如:target: ['web', 'es2020'];Stats 输出信息优化;Progress 进度信息优化,插件还加了个百分比选项;自动定义多个 runtime 在同个页面下时,之前会有冲突;自动定义 output.publicPath 如果没设置的话;可生成 ts 类型文件。

一些打包优化:

  • tree-shaking。包括了嵌套模块,模块内部,以及 CommonJS 模块。
  • 模块副作用分析。以前需要在 package.json 中手动定义 sideEffects,现在基于源代码自动标记这些模块。
  • 优化每次打包
  • Module Concatenation
  • 代码生成优化
  • splitChunks.minSize 可指定不同类型

性能优化。文件系统的持久缓存、文件生成。这块不会翻,高级用户还是直接看文档好了 🙄

一直以来存在的问题。没碰到过,略过

实验性性功能。详情请看 experimentsTop-Level-Await 了解一下,基本上是未来的规范:

import React from 'react';
// Stage 3 proposal
// 顶层作用域直接使用 await
const x = await import('x');

最小的 Node.js 版本 6 -> 10.13.0

一些配置的改动。只列举写常见的,如果启动出问题的话直接去官网查好了:

  1. entry: {} 可以通过插件添加入口文件
  2. target 上文提过的支持数组和版本,默认 browerslist 如果有的话
  3. cache 新的 filesystem 类型
  4. output.filename 可以是个函数。顶一下,可以自定义生成的文件名称。之前在 webpack 4 文档里查到这个选项,开心地试了,but 并不能用,后来才在 issue 里找到这是 webpack 5 里的功能,吐槽下文档,ca
  5. optimization.moduleIds: 'deterministic' 添加的默认项
  6. module.rules loaders 被删除了,用 use
  7. optimization.splitChunks.cacheGroups.vendors 重命名成 defaultVendors
  8. etc...

loader 相关的改动。写过 loader 的可以关注下。

内部细节的改动。插件应用的顺序改了,依赖的 tapable 升级,分析模块依赖用的 ModuleGraph 和 ChunkGraph 改动了,最后顺带提下,文件监听系统重构改用 node 原生的 fs 了,不知道是好是坏啊。。。更多的看不动了 😱

做为一名老 webpack 配置工程师,基本上就到此为止了,看过源码的大佬们可以详查之。

次要改动 Minor Changes

可在乎~(太多了...很多都是和常用配置没啥关系的)

最后总结一下,看完过会儿就等于没看一样。

现在基本上都是用脚手架了,诸如 create-react-app 这种,有大佬在调 webpack 配置,省得翻文档了。2020 年了也衍生了几个基于 esm 的打包工具,比如 snowpackesbuild,据说打包速度贼快,阔以试试。

参考链接

原文链接(博客小站引流): www.ningtaostudy.cn/articles/bi…