webpack5 在 2020-10-10 正式发布。
webpack5 发布以及更新日志:webpack.docschina.org/blog/2020-1…
1. webpack5 新特性
1.1 最小 Node.js 版本:10.13.0
最低支持的 Node.js 版本从 6 增加到 10.13.0
。
1.2 功能清除
- 所有在 webpack4 里面被废弃的能力都被清除,因此需要确保 webpack4 没有打印警告。
require.includes
语法已经被废弃(可以通过Rule.parser.requireInclude
将行为改为允许、废弃或禁用)
require.includes
的作用:是 webpack 自身实现的一个语法,作用是实现一个模块预加载。
- 不再为 Node.js 模块引入 polyfill
之前的版本是:如果某个模块依赖 Node.js 里面的核心模块,那么这个模块被引入的时候会把 Node.js 整个 polyfill 顺带引入。
webpack4:里面利用 node-libs-browser 这个实现 polyfill 的。
1.3 长期缓存:确定的模块 ld、chunk 和导出名称
在生产模式下,默认的 chunklds: "deterministic", modulelds:“deterministic"
。设置成 deterministic
时默认最小3位数字会被使用。
例子:
webpack4:1,2,3...
递增的方式
webpack5:数字是确定的,比如多个 import 模块时,不会因为一个变化了而产生变化。
1.4 持久化缓存
在 webpack4 里面,可以使用 cache-loader 将编译结果写入硬盘缓存,还可以使用 babel-loader,设置 option.cacheDirectory 将 babel-loader 编译的结果写进磁盘。
webpack5缓存策略
- 默认开启缓存,缓存默认是在内存里。可以对 cache 进行设置。
- 缓存淘汰策略:文件缓存存储在 node_ modules/.cache/webpack,最大 500 MB, 缓存时常两个星期,旧的缓存先淘汰
1.5 构建优化
Tree Shaking 优化 — 嵌套的 Tree shaking
例子:module.js 里使用了 inner.js,然后 user.js 里使用了 module.js 文件
Tree Shaking 优化 — 内部模块 Tree shaking
例子:usingSomething 里使用了 something,test 里使用了 usingSomething,但是 test 未被使用。
webpack4 会打包进去
webpack5 里可以分析模块之间的关系(内部通过一个 innerGraph 算法
去实现),上面不会打包
1.6 代码生成:支持生成 ES6 代码
webpack4 之前只生成 ES5 的代码。
webpack5 则现在既可以生成 ES5 又可以生成 ES6/ES2015
代码。
两种设置方式:5 =< ecmaVersion <= 11
或 2009 =< ecmaVersion <= 2020
比如:写 2015 也是可以的
1.7 开创性的特性:模块联邦
发明者:Zack Jackson
模块联邦可以实现微前端。
基本解释:使一个 JavaScript
应用在运行过程中可以动态加载另一个应用的代码, 并支持共享依赖(CDN
)。不再需要本地安装 Npm
包。
Remote:被依赖方,服务提供方,被 Host 消费的 Webpack 构建 Host:依赖方,消费其他 Remote 的 Webpack 构建
一个应用可以是 Host,也可以是 Remote,也可以同时是 Host 和 Remote。
ModuleFederationPlugin 介绍
Webpack 内部通过 ModuleFederationPlugin 插件将多个应用结合起来。
- name:必须,唯一 ID, 作为输出的模块名,使用的时通过 {expose} 的方式使用
- library:必须,其中这里的 name 为作为 umd 的 name;
- remotes:可选,表示作为 Host 时,去消费哪些 Remote;
- shared:可选,优先用 Host 的依赖,如果 Host 没有,再用自己的;
- main.js:应用主文件
- remoteEntry.js:作为 remote 时被引的文件
webpack5 相对于 webpack4 ,构建速度更快,构建体积也更小,但是在开发阶段并不能明显感受到。
模块联邦使用案例
应用一 (host):要去消费 app2,端口号 3002
应用二 (remote):提供了 Button 组件
浏览器的效果
app1 应用使用了 app2 的 Button 组件端口为 3001。
app2 应用端口为 3002
app1 怎么找到 app2 的?
通过 remoteEntry.js
找到生产者的模块。
app2 提供了 button
app1 使用 button
这样就达到了组件的共享。
打包构建之后,可以看到 remoteEntry.js 里面有 moduleMap,并且提供了get 方法。
2. bundle 和 bundless 的差异
vite 发布 2.0 版本
vitejs.cn/
Vite 是一种新型前端构建工具,能够显著提升前端开发体验。它主要由两部分组成:
- 一个开发服务器,它基于 原生 ES 模块 提供了 丰富的内建功能,如速度快到惊人的 模块热更新(HMR)。
- 一套构建指令,它使用 Rollup 打包你的代码,并且它是预配置的,可输出用于生产环境的高度优化过的静态资源。
2.1 更早实践 bundless 的工具:Snowpack
Snowpack 是一个闪电般快速的前端构建工具,专为现代网络而设计。 它是开发工作流程中更重、更复杂的打包工具(如 webpack 或 Parcel)的替代品。 Snowpack 利用 JavaScript 的本机模块系统(称为 ESM)来避免不必要的工作并保持快速,无论您的项目有多大。
2.2 浏览器对ESM的支持情况
2.3 HTTP/1.1 的请求数限制
打包会合并文件,减少请求数
各浏览器在HTTP/1.1 协议下的最大请求数限制:chrome为6个,IE11为11个,Firefoc为9个 Safari 13为6个。 但是在 http2中就已经没有请求数限制了。
2.4 HTTP/2 越来越多的应用在主流网站
现在,国内很多应用已经开始使用 HTTP/2 了,比如 抖音、掘金等等。
2.5 资源加载差异
- bundle 浏览器最开始会加载一个 bundle.js 文件
- bundless:浏览器请求多个文件
bundle: 浏览器最开始会加载一个构建好的 bundle.js 文件,如下图:
bundless: 直接加载原文件,不是加载构建好的,如下图:
2.6 打包速度的对比
- bundless 的冷启动时间大大缩短
- bundless 的 HMR 速度不受整个项目体积影响
- bundless 的单文件粒度的缓存更优
2.7 开发体验的对比
以 webpack 里面的 sourcemap 类型举例
bundle 依赖 sourcemap,并且类型很多,bundless 直接打 debugger 就行,调试起来相对简单。
3. Vite 的构建原理
3.1 Vite 构建速度快的原因
预构建使用 ESBuild (冷启动快的原因) : github.com/evanw/esbui…
因为 esbuild 里面使用的是 Golang 语言去进行打包,它是静态语言。
如图,esbuild 打包用时 0.33s。
3.2 Vite 打包流程
创建构建服务
静态文件托管服务
重写模块路径
-
对于
bare import
,把模块名替换为这个模块的 entrypath
,并在path
的开头补上一个/@modules
的标识符。如:import React from "/@modules/@pika/react/source.development.js"
-
相对路径转绝对路径,方便浏览器请求。
-
补齐文件扩展名和经常被省略的
index.xxx
,如:import a from '/src/a/index.js'
-
给非 js 类型( js 类型:如
js(x)/ts(x)/vue
)的文件地址加上一个叫 "import
" 的query
参数。 -
给
hmr
相关的请求地址添加时间戳,避免缓存。如:import '/src/App.jsx?t=1599124870589'
静态资源打包策略
浏览器不支持 JS 中直接写 import CSS
、图片、JSON
等语法。.
回顾一下webpack loader 的处理策略:
- CSS:转换成 js 的模块,执行模块会在 DOM 中创建
<style>
标签并且插入 CSS 内容 - 图片:转换成图片路径
- JSON:转化成 js 模块,
default export = json
vue 脚本打包策略:
CSS 资源打包策略:
模板打包策略:
总结
到底选择哪一种构建工具,需要看场景,
- 如果是很复杂的场景,选择 webpack 作为构建工具,因为 webpack 的生态是比较强大的;
- 对于不复杂的场景,而且 vite 的生态也能满足的场景,可以去使用 vite。
- 也可以尝试结合两种构建工具的优势进行开发。