Webpack 4.0 学习(转)


原文地址:https://segmentfault.com/a/1190000013608316

官方已经发布了Webpack 4.0。有哪些新特性?有哪些改进?学着使用这个新版本,来构建更快的应用吧。
2018年2月25日星期天,Webpack 4.0向公众发布。代号为Legato,是Javascript模块打包器的一个主要版本。继3.0版本之后,4.0花了将近八个月的时间来发布。在这篇文章中,覆盖了Webpack 4.0的主要新特性以及改进和弃用。
Webpack是一个实用的JavaScript程序打包工具。它能把每个模块打包在至少一个文件中(通常只有一个文件),并且提供给浏览器运行。无论如何,Webpack不仅仅是一个打包的工具。在各种loaders和plugins的帮助下,他能改变、缩小和优化文件,然后在浏览器上运行。它需要不同的资源,比如JavaScript、CSS、字体、图片和HTML,然后将这些资源放进配置中,以便浏览器使用。Webpack的高明之处,在于它的整体。
好了,接下来简单的介绍下webpack。
What’s new in Webpack 4.0?
1.不再支持Node.js 4
不再支持Nodes.js 4。因为源码已经更新到一个更高的ECMAScript版本。

Webpack的创始人之一,Tobias,建议用户使用Node >= 8.94,以便使用最优的性能,是因为源代码使用了JavaScript新特性。
2.告别CommonChunkPlugin,迎接SplitChunksPlugin
再见了,CommonChunkPlugin。臭名昭著(infamous)的CommonChunkPlugin被移除了,替代的是两个新API:optimize.splitChunks和optimization.runtimeChunk。接下来我会解释一下。
Webpack 4对块图进行了巨大改进,并使用了新的块拆分技术。在新的改进过程中,诞生了新的插件---SplitChunksPlugin。这个插件能够自动识别哪些模块需要拆分为启发式的(heuristics),哪些需要拆分为块。另外,它还有一些极好的特性,比如在异步chunks的高效,以及把vender拆分成不同的vender块。
默认情况下,Webpack 4会默认为你创建共享块。它通过optimize.splitChunks进行配置。optimization.runtimeChunk: true选项只在运行时在每个入口添加额外的chunks。
3.支持WebAssembly
WebAssembly(wasm) 是一个新的移动的、高效加载的可供web使用的工具。它确实很快,开发者社区也很快拥抱了这个技术。因此,Webpack 4 已经提供了wasm的支持。在Webpack 4,你可以引入和导出任何一个Webassembly的模块。同时,你也可以写一个loaders来直接引入C++、C和Rust。
Note: WebAssembly 模块只能在异步chunks中使用。
4.支持多种模块类型
Webpack 4.0现在支持以下五种模块类型:
?javascript/auto: Webpack 3.0在多种模块系统中支持了这种模块类型,比如CommonJS, AMD, ESM.
?javascript/esm: 只支持ECMAScript模块
?javascript/dynamic: 只支持CommonJS 和AMD.
?json: 支持JSON数据,可以通过require和import使用
?webassembly/experimental: 只支持WebAssembly,目前只是试验阶段
5.用mode点燃火焰
在此刻,对于这个新特性,我比任何都激动。webpack团队引入了一个叫做mode的配置属性来实现零配置(#oCJS)的模块打包。mode可以设置为如下值:development和production。开箱即用,默认为production。
production选项提供了一系列默认配置,它可以:
?更小的输出尺寸
?运行时的快速加载代码
?省略只在开发时的代码
?不暴露源代码或者路径
?快捷使用输出assets
development选项提供了一下默认配置,它可以:
?浏览器内调试更好的工具
?在一个快速的开发周期,更快的增量编译
?更好的运行时错误提示
6.零配置模块打包 #OCJS
到目前为止,在webpack.config.js中需要定义一个entry point来为app打包。然而,在Webpack 4.0,不需要定义这个erntry point,默认为./src/index.js
而且,也不要定义输出文件,缺省为./dist/main.js
这种特性的重要意义在于处理小型项目的时候,不需要配置文件,只需要webpack。
Webpack 4不需要配置文件和入口文件。
Webpack 4 不需要任何配置文件,只要src/index.js文件。
7.更快的构建时间
Webpack的构建时间,和之前的主要版本相比,速度提升了98%。如果不信,可以自己尝试一下。你可以这样用:
npm i webpack --save-dev
或者使用yarn:
yarn add webpack --dev
在package.json中,你可以看到devDependencies这一块:
"webpack": "^4.0.0"
Webpack 3: 2405ms


webpack 4 :1388ms

Webpack 4.x 是3.x版本的2倍多。
8.全新的插件系统
Webpack 4配备了全新整改的插件系统。这是一个全新的API,这些插件和hooks的API有以下的配置:
?hooks对象将所有钩子作为可扩展类的属性
?多种Hook类现在会根据hook类型存在:sync, async, normal, bailing, waterfall, looping.
?在添加插件的时候,你需要提供一个名字
?在添加插件的时候,你可以选择插件的类型(sync/callback/promise)
?this.hooks = { myHook: new SyncHook(…) } 是注册钩子的新方法。创建一个新的Hook对象,作为hooks对象的属性
9.到webpack-cli的改变
webpack的cli(command line interface)已经移动到webpack-cli了,如果要使用CLI,你需要安装webpack-cli。
废弃和其他的更新
?这些插件已经废弃了,NoEmitOnErrorsPlugin, ModuleConcatenationPlugin, NamedModulesPlugin。现在你可以分别使用optimization.noEmitOnErrors,optimization.concatenateModules和optimization.namedModules了。
?import()一直返回一个命名空间对象
?Wepack默认删除不存在的分支
?现在使用System.import()会弹出警告的提示
?webpackInclud和webpackExclude在import()魔法注释的支持下,提供了支持,当使用动态表达式的时候,他们允许过滤文件。
通往Webpack 5
Wepack 5正在进行几项规划。其中一些计划包括:
?稳定的WebAssembly支持
?创建一个自定义的模块类型,并且使Webpack提供支持的能力
?移除ExtractTextWebpackPlugin插件,并支持CSS模块类型
?支持HTML模块类型
?持久化缓存
结论:
现在你已经了解到Weboack的新功能了。Webpack拥有很多的新特性和改进,Wepack 的团队对Webpack 4.0的迁移和合并有详细指导,在接下来的几周和一月内将会推出。对Webpack很陌生?我写了一篇模块打包器的简单介绍。Sean Larkin在Webpack Academy也有一些非常棒的Webpack课程。

精读《webpack4.0 升级指南》
转自https://juejin.im/post/5aafc6846fb9a028d936f97c
本周精读的是 webpack4.0 一些变化,以及 typescript 该怎么做才能最大化利用 webpack4.0 的所有特性。
1 引言
前段时间尝试了 parcel 作为构建工具,就像农村人享受了都市的生活,就再也回不去了一样,发现无配置真是前端构建工具的大趋势,用起来非常方便快捷,再也不想碰 webpack 的配置了。
可是实践一段实践后,发现 parcel 还是不够成熟,主要体现在暂时不支持一些 rollup 优秀特性:Tree shaking、Scope Hoist,大型项目打包速度反而比 webpack3.0 慢。由于笔者完全零配置,当发现构建速度急速下降时,自然把矛头指向了 parcel :p.
就在前几周,webpack4.0 发布了,也拥抱了零配置,我想,是时候再回到 webpack 了。可是,文档好少,怎么迁移呢?
就在这几天,webpack 文档发布了 4.0 版本,虽然遗留了大量旧文档,不过也足够参考了。
2 精读
笔者尝试了?webpack node api,尝试了很久,发现被坑了。文档里只字未提?mode?模式,4.0 环境下?compiler?总是提示没有?mode?的 warning。
读了一些文档,发现 webpack4.0 大力度宣传的是 cli 方式启动,里面提到了最重要的?webpack --mode?模式,可见 webpack4.0 更推崇的是让开发者使用高度封装的 cli,而不是使用 node 方式开发(那 node 文档也应该更新呀)。笔者又看了一圈,发现?webpack-dev-server?的 webpack 版本升到了 4.0,ts-loader?也升级到了 4.0,可能生态已经全部准备好了。
使用 webpack cli、webpack-dev-server cli
安装?webpack^4.1.1?webpack-cli^2.0.10?webpack-dev-server^3.1.0,以及创建一个公共配置文件?webpack.config.ts:
export default {
entry,

output,

module: {
rules
},

resolve,

resolveLoader,

devServer: {
https: true,
open: true,
overlay: {
warnings: true,
errors: true
},
port
}
}
记得用?tsc?转换为?webpack.config.js?作为 cli 入口。
开发模式下使用?webpack-dev-server:
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
使用 typescript + webpack
简单来说,只需要?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 语法还存在报错,而且也不会做兼容处理。
使用 typescript + babel + webpcak
注意处理顺序,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,上述的小优化也是内置的功能之一。
webpack4 带来的是适配成本的大幅优化
社区似乎有部分声音在抱怨,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。
3 总结
只要合理的使用 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。
随着新的一波零配置浪潮,真的不应该在编译配置上花那么多时间了。
4 番外 - prefetch
读者自习阅读就会发现,这不是一篇单纯 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?规则将会无效。

猜你喜欢

转载自www.cnblogs.com/shichangchun/p/11029163.html