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()方法(如下所示),让模块变得可更新。