本周精读的是 webpack4.0 一些变化,以及 typescript 该怎么作才能最大化利用 webpack4.0 的全部特性。html
前段时间尝试了 parcel 做为构建工具,就像农村人享受了都市的生活,就再也回不去了同样,发现无配置真是前端构建工具的大趋势,用起来很是方便快捷,不再想碰 webpack 的配置了。前端
但是实践一段实践后,发现 parcel 仍是不够成熟,主要体如今暂时不支持一些 rollup 优秀特性:Tree shaking、Scope Hoist,大型项目打包速度反而比 webpack3.0 慢。因为笔者彻底零配置,当发现构建速度急速降低时,天然把矛头指向了 parcel :p.node
就在前几周,webpack4.0 发布了,也拥抱了零配置,我想,是时候再回到 webpack 了。但是,文档好少,怎么迁移呢?react
就在这几天,webpack 文档发布了 4.0 版本,虽然遗留了大量旧文档,不过也足够参考了。webpack
笔者尝试了 webpack node api,尝试了好久,发现被坑了。文档里只字未提 mode
模式,4.0 环境下 compiler
老是提示没有 mode
的 warning。git
读了一些文档,发现 webpack4.0 大力度宣传的是 cli 方式启动,里面提到了最重要的 webpack --mode
模式,可见 webpack4.0 更推崇的是让开发者使用高度封装的 cli,而不是使用 node 方式开发(那 node 文档也应该更新呀)。笔者又看了一圈,发现 webpack-dev-server
的 webpack 版本升到了 4.0,ts-loader
也升级到了 4.0,可能生态已经所有准备好了。es6
安装 webpack^4.1.1
webpack-cli^2.0.10
webpack-dev-server^3.1.0
,以及建立一个公共配置文件 webpack.config.ts
:github
export default {
entry,
output,
module: {
rules
},
resolve,
resolveLoader,
devServer: {
https: true,
open: true,
overlay: {
warnings: true,
errors: true
},
port
}
}
复制代码
记得用 tsc
转换为 webpack.config.js
做为 cli 入口。web
开发模式下使用 webpack-dev-server
:typescript
webpack-dev-server --mode development --progress --hot --hotOnly --config ./webpack.config.js
复制代码
生产环境 build 使用 webpack
:
webpack --mode production --progress --config ./webpack.config.js
复制代码
开发/生产模式,都以 webpack.config.ts
做为配置,其中 devServer
项仅在开发模式下,对 webpack-dev-server
生效。
一旦开启了 --mode production
,会自动开启代码压缩、scope hoist 等插件,以及自动传递环境变量给 lib 包,因此已经不须要 plugins
这个配置项了。同理,开启了 --mode development
会自动开启 sourceMap 等开发插件,咱们只要关心更简单的配置,这就是 4.0 零配置的重要改变。
mode=production
,mode=development
具体内置了哪些配置,能够参考这篇文章:webpack 4 终于知道「约定优于配置」了。偏偏有意思的是,webpack4 这么作,就是不想咱们浪费时间了解这些机制,社区应该会慢慢习惯零配置的开发方式。
固然,虽说零配置,但配置文件基本三板斧仍是很是有必要配置:entry
output
module
。
咱们可能还要给配置文件传一些参数,好比定制多种开发模式的入口,经过 --env
传递:
webpack-dev-server --mode development --env.entry ./src/main.tsx
复制代码
webpack.config.ts
接收:
const entry = yargs.argv.env.entry
复制代码
简单来讲,只须要 ts-loader
就够了。在 webpack.config.ts
中增长新的 rules
:
{
module: {
rules: [{
test: /\.(tsx|ts)?$/,
use: ["ts-loader"]
}]
}
}
复制代码
注意 tsconfig.json
中模块解析策略使用: "module": "esnext"
。
缘由是 webpack 须要 es6 import 语句,才能进行 tree shaking 或者动态 import 优化,咱们再也不让 ts-loader
包办模块设置,换句话说,咱们采用白名单方式看待 typescript
以及 babel
,只让他作咱们须要的工做,剩下的丢给 webpack 处理,能够得到最大程度性能优化。
若是仅使用 webpack + typescript,建议将 ts 编译输出模式调整为 es3
,由于 webpack 自带的压缩工具对 es6 语法还存在报错,并且也不会作兼容处理。
注意处理顺序,ts -> babel -> webpack。
由于多出了 babel,咱们将 ts 编译兼容模式关闭:"target": "esnext"
,模块也不要解析:"module": "esnext"
,ts-loader
仅仅将 typescript 代码转换成 js,其余一切优化都不要作,将 esnext 原生代码直接传给 babel 处理。
babel 这一层的职责是对代码进行兼容处理,不要压缩,也不要把 import 转成 require。笔者发现 babel 直接解析 import 代码会没法处理,所以须要 stage-2
preset:
{
presets: [
["env", {
modules: false,
}],
["stage-2"]
],
plugins: [
["transform-runtime"]
],
comments: true
}
复制代码
从上面配置能够看到,babel 这层对 esnext 的代码进行了浏览器兼容处理(env 插件),直接透传 import
(stage-2 插件让 babel 识别 esModule),以及支持 async await(transform-runtime) 插件。
原本想用 env 替代 transform-runtime 的功能,笔者暂时没有查询到可行方式,欢迎读者补充。
另外要容许 babel 保留注释(comments: true
),由于 webpack import 支持自定义 chunkName 是经过注释的方式:
import(/* webpackChunkName: "src" */ "./src")
复制代码
配合 react-loadable
使用更佳:
Loadable({
loader: () => import(/* webpackChunkName: "src" */ "./src"),
loading: (): any => null
})
复制代码
由于 react-loadable
让页面按 chunk 方式打包,而 webpack 又会自动 picke shared chunks,配合给每一个 page chunks 经过 webpackChunkName
定义名称,webpack 能够给每一个共享 chunks 更加可读的名字,好比:vendor~src,about,login
,你就知道这个是 src
about
login
三个页面间公共模块。
可能已经有人看出瑕疵了,给每一个文件增长 webpackChunkName
注释既麻烦又不优雅,并且只要有一个开发者没有加这个注释,上面说的可读 chunks 可能就缺乏了某个模块名。
这就要笔者以前一篇精读来看了:精读《Rekit Studio》,项目能够经过约定的方式定义页面,入口文件经过 cli 自动生成,不就既减小业务代量,又统一加上了 webpackChunkName
嘛?
这里小小安利下集成了这个思路的项目脚手架 pri,使用了 ts + babel + webpack4.0,上述的小优化也是内置的功能之一。
社区彷佛有部分声音在抱怨,webpack 又发新版本,咱们又要适配一轮。其实 webpack 这么作偏偏没有带来适配成本,出问题的在于咱们对 webpack 的使用方式与理念。
若是咱们开始就将 webpack 看成一体化打包方案,开发调试使用 webpack-dev-server cli
,开发环境编译使用 webpack cli
,那么 webpack4 其实只是补充了开发环境这个最重要的配置变量而已。类比 parcel
的两个命令:
parcel index.html
parcel build index.html
复制代码
对应:
webpack-dev-server --mode development
webpack --mode production
复制代码
因此 webpack4 几乎是有史以来最方便使用与迁移的版本,前提是使用思惟得正确,舍得将编译环节全权交给两个官方的 Cli。
只要合理的使用 typescript、babel,让各自只发挥最小功能,将原生的模块化代码抛给 webpack,再配合 --mode production
配置,webpack 会自动开启一切可能的插件优化你的项目,而咱们再不须要阅读形形色色的 webpack 插件了,更使人激动的是,随着 webpack 版本升级,优化会不断升级,而咱们只要留着 --mode
参数,不须要改一行配置。
总结起来,就是不用关心优化相关的配置,咱们只须要配置业务相关的 entry
output
module
,这就是 webpack4.0.
我之前为了实现第一次编译完后当即打开浏览器的功能,写了一共 200 行的 customCompiler
以及 format-webpack-message
,并且利用 koa 开了一个 server,利用 await 和 flags 等待第一次编译完的时机,并利用 opn
库打开网页。
其实用 cli 只须要 webpack-dev-server --open
。
随着新的一波零配置浪潮,真的不该该在编译配置上花那么多时间了。
读者自习阅读就会发现,这不是一篇单纯 webpack4 升级指南,仔细阅读能够发现文中蕴藏的一些工程优化思路。文章末尾再给一波福利,分析一下 prefetch 优化是什么,以及怎么作。
现代浏览器支持了如下两种语法:
<link rel="preload" />
<link rel="prefetch" />
复制代码
兼容性本身查 Caniuse,笔者重点在功能上。preload
收集当前用到的资源,prefetch
收集将来用到的资源。
页面本质上也是将来一种资源,若是认为用户会点击另外一个页面(若是对产品没自信,或者 pv 太低能够忽略这个功能),就能够用 prefetch
让浏览器在空闲时间下载下一个页面的 chunk 文件。
前端包体积优化效率通常和用户体验是违背的,既然下一个页面在另外一个 chunk 中,用户点击后必然会产生 loading。但是若是结合了 prefetch
,鱼和熊掌就兼得了(正经常使用户不可能页面还没加载完就马上点按钮跳页,因此惟一的缺点几乎不会对正经常使用户产生影响)。
api 有了,那么最大的问题就是,当前页面怎么知道要加载哪些 chunks?通常两种作法:
全量模式 使用好比 preload-webpack-plugin 插件,将全部生成的 chunk 都做为 prefetch
资源,在全部页面中。几乎全部规模的项目都不会产生过多的 chunks,因此这个方案理论上不够优雅,但能解决实际问题。
按需模式,是理论和实践双重优雅的方案,是否要这么作取决于您是否有代码洁癖。方法是提供一个定制的 Link
标签,根据 URL 地址按需生成 prefetch
标签。这种方案最大缺陷是,若是用户不按照约定使用内置的 Link
,prefetch
规则将会无效。
若是你想参与讨论,请点击这里,每周都有新的主题,每周五发布。