前端利器躬行记(4)——webpack进阶
webpack是一个非常强大的工具,除了前文所介绍的基础概念之外,还有各种进阶应用,例如Source Map、模块热替换、集成等,本文会对这些内容做依次讲解。
一、 runtime和manifest
在webpack打包生成的bundle文件中,除了业务代码和引用的第三方库之外,还会包含管理模块交互的runtime和
图2 index.js文件中的打印和错误信息
devtool字段可选的关键字多达13个,要想了解其中的细节,可参考官方文档。
2)文件格式
Source Map保存的信息会随着模块的变多而变大,为了避免bundle文件的内容过于臃肿,有必要将其独立出来。在devtool可选的关键字中,有一个source-map,可生成一个专门记录Source Map信息的文件,其后缀为“.map”。以下面的配置为例,会生成一个index.bundle.js.map文件。
module.exports = { entry: { index: "./index.js" }, output: { filename: "[name].bundle.js" }, devtool: "source-map" };
通过source-map生成的bundle文件只会附加一条指向Source Map文件的语句,如下所示。
//# sourceMappingURL=index.bundle.js.map
index.bundle.js.map文件保存的是一个JSON格式的对象,其结构如下所示。
{ "version": 3, "sources": ["webpack:///webpack/bootstrap", "webpack:///./index.js"], "names": ["console", "log", "a"], "mappings": "yCAAiC,eAAe;AAChD......", "file": "index.bundle.js", "sourcesContent": [" \t// The module cache\n ....."], "sourceRoot": "" }
每个属性的说明如下所列:
(1)version:Source Map的版本,目前为3。
(2)sources:一个由源文件组成的数组。
(3)names:一个由源文件中的变量名和属性名组成的数组,需要经过编译、压缩等处理后才能得到。
(4)mappings:一段经过了Base64和VLQ编码的字符串,记录了位置的映射关系。
(5)file:bundle文件的名称。
(6)sourcesContent:一个由源文件内容组成的数组,其元素顺序和sources中的相同。
(7)sourceRoot:源文件所在的目录,如果与bundle文件处在同一目录,那么该项为空。
三、webpack-dev-server
webpack-dev-server搭建了一个基于Node.js的本地服务器,能够实时编译,并且在浏览器和服务器之间建立了一个websocket长连接,从而就能自动加载页面了,其安装命令如下所示。
npm install --save-dev webpack-dev-server
修改webpack.config.js文件,增加devServer字段(如下配置所示),其contentBase属性用于定义服务器可访问的目录。
module.exports = { entry: { index: "./index.js" }, output: { filename: "[name].bundle.js" }, devServer: { contentBase: "./dist" } };
下面的命令会开启本地服务器,并打开默认的浏览器。在地址栏输入http://localhost:8080/后,就能访问到dist目录下的文件了。
npx webpack-dev-server --open
假设在配置文件的同级目录中有个index.js文件,一旦修改它的代码,在终端就会显示一长串的编译和加载的提示,如下所示(只给出了部分内容)。
i wdm: Compiling... i wdm: Hash: 37e4296852feeec48286 Version: webpack 4.34.0 Time: 149ms Built at: 2019-06-21 11:10:47 AM Asset Size Chunks Chunk Names index.bundle.js 335 KiB 0 [emitted] index Entrypoint index = index.bundle.js [0] multi ../node_modules/webpack-dev-server/client?http://localhost ./index.js 40 bytes {0} [built] [1] ../node_modules/webpack-dev-server/client?http://localhost 4.29 KiB {0} [built] ...... [30] ../node_modules/webpack/hot sync nonrecursive ^\.\/log$ 170 bytes {0} [built][32] ./index.js 29 bytes {0} [built] + 18 hidden modules i wdm: Compiled successfully.
如果不想实时加载页面,那么可以将inline属性设为false,如下所示,devServer字段还有许多其它的属性,例如https、compress、hot等,可参考官方文档。
module.exports = { devServer: { inline: false } };
注意,由于经过webpack-dev-server实时编译的文件都保存在了内存中,因此输出目录内的bundle文件的内容并不会实时更新。这么做既能快速的读取代码,也能降低写入文件的开销。
四、模块热替换
模块热替换(Hot Module Replacement,HMR)能在程序运行时替换、新增或删除模块,而无需加载整个页面(即不刷新窗口),其效果类似于在Chrome浏览器的调试器中直接更改样式。
如果要开启模块热替换,那么首先要在webpack.config.js中为webpack-dev-server配置hot字段为true(如下所示),启用模块的热替换特性。
module.exports = { devServer: { hot: true } };
然后在配置文件中,添加HotModuleReplacementPlugin插件,如下所示。
const webpack = require('webpack'); module.exports = { plugins: [ new webpack.HotModuleReplacementPlugin() ] };
如果要更新的是JavaScript模块,那么还需要在模块中添加module.hot.accept()方法(如下所示),让模块变得可更新。