开发环境调优
在之前的章节中我们介绍了 Webpack 的基础配置,一些 Loader 和 Plugin 的使用方法,到此我们应该对 Webpack 有了一定的认识,Webpack 作为打包工具的重要使命之一就是提升效率,下面我们介绍一些对日常开发有一定帮助的 Webpack 插件、配置。
source map
Webpack 打包编译后的代码基本不具备可读性,工程发布或启动后此时若代码跑出一个错误,想要回溯它的调用栈是非常困难的。而有了 source map 在加上浏览器的调试工具,要做到追踪错误和警告就容易的多了。source map 指的是将编译、打包、压缩等操作后的代码映射回原文件的过程。开发环境通过 source map 我们可以直接看到源代码调试,生产环境通过 source map 我们可以通过工具回溯到报错的代码位置。为我们进一步分析错误提供了便利。
在开发环境即 mode 选项设置为 development 模式下,Webpack 将自动生成映射文件,source map 除了将 Javascript 映射回原文件外,还同样适用于样式文件。
source map 配置
Javascript 的 source map 通过配置项 devtool 来启用,只要在 webpack.config.js 中添加 devtool 即可。
index.js
const divElement = document.createElement("div");
divElement.className = "demo";
divElement.innerHTML = 'mini-css-extract-plugin';
document.body.appendChild(divElement);
webpack.config.js
const path = require('path');
module.exports = {
// ...
devtool: 'source-map',
}
对于 CSS、SCSS、Less 来说,需要在 loader 中添加 source map 配置。
webpack.config.js
const path = require('path');
module.exports = {
// ...
module: {
rules: [
{
test: /\.(css)$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
sourceMap: true
}
}
]
}
]
},
}
打包后,在 dist 文件夹下除了 index.js 外还生成了一个 index.js.map 文件。在生成 mapping 文件的同时,还为 index.js 添加了一个引用注释,以便开发工具知道在哪里可以找到它。
dist/index.js
(function() {
// 内容
})();
//# sourceMappingURL=index.js.map
当我们打开浏览器的开发者工具时,map 文件会同时被加载,这时浏览器会使用它来对打包后的 bundle 进行解析。分析出源码的内容。当我们打断点进行调试时,可以直接调试源码。
source map 分类
Webpack 支持的 source map 大概分为两种,inline(内连) 和 separate(独立)两种方式。inline 类型即生成的映射关系内容保存在 bundle 中不生成单独的文件,separate 类型即可生成单独的 map 文件可以独立使用。source map 支持类型很多,下面只介绍其中几种,详细信息可官网查看,source map官网
inline source map 类型
Webpack 提供了多种内联映射文件类型。通常 eval 是起点,因为它是速度和质量之间的良好折衷,同时在 Chrome 和 Firefox 浏览器中可以可靠地工作。下面我们介绍两个内连类型的例子。
注意:为了查看效果我们去掉 webpack.config.js 中的 mode 配置。
devtool: "eval"
eval生成代码,其中每个模块都包装在一个eval函数中。并且都包含 //# sourceURL,此选项会非常快的构建。
(()=>{var __webpack_modules__={
138:()=>{
eval('const divElement = document.createElement("div");\ndivElement.className = "demo";\ndivElement.innerHTML = \'mini-css-extract-plugin\';\ndocument.body.appendChild(divElement);\n\n//# sourceURL=webpack://6.1/./src/index.js?')
}
},__webpack_exports__={};__webpack_modules__[138]()})();
devtool: "eval-source-map"
每个模块使用 eval() 执行,并且 source map 转换为 DataUrl 后添加到 eval() 中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。
(() =>
{var __webpack_modules__={
138: ()=>{
eval('const divElement = document.createElement("div");\ndivElement.className = "demo";\ndivElement.innerHTML = \'mini-css-extract-plugin\';\ndocument.body.appendChild(divElement);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMTM4LmpzIiwibWFwcGluZ3MiOiJBQUFBO0FBQ0E7QUFDQTtBQUNBIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vNi4xLy4vc3JjL2luZGV4LmpzP2I2MzUiXSwic291cmNlc0NvbnRlbnQiOlsiY29uc3QgZGl2RWxlbWVudCA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJkaXZcIik7XG5kaXZFbGVtZW50LmNsYXNzTmFtZSA9IFwiZGVtb1wiO1xuZGl2RWxlbWVudC5pbm5lckhUTUwgPSAnbWluaS1jc3MtZXh0cmFjdC1wbHVnaW4nO1xuZG9jdW1lbnQuYm9keS5hcHBlbmRDaGlsZChkaXZFbGVtZW50KTsiXSwibmFtZXMiOltdLCJzb3VyY2VSb290IjoiIn0=\n//# sourceURL=webpack-internal:///138\n')
}
},__webpack_exports__={};__webpack_modules__[138]()})();
separate source map 类型
Webpack 提供了多种独立映射文件类型。我们常用的为 source-map 类型,source-map 类型可以生成单独的 map 文件,有了 source map 也就意味着任何人都可以通过浏览器调试工具看到工程源码,这对于安全性来说有了极大隐患。那么如何能保证线上问题可以追踪又能防止源码泄漏,Webpack 提供了 hidden-source-map 和 nosources-source-map 两种策略来提升 source map 的安全性。
devtool: "hidden-source-map"
hidden-source-map 与 source-map 作用相同都会生成一个 map 文件,唯一区别是不会为 bundle 添加引用注释。我们在浏览器中直接打开工程,是看不到原文件的,生成的 map 文件只是作为我们追踪错误信息的依据。
(()=>{const e=document.createElement("div");e.className="demo",e.innerHTML="mini-css-extract-plugin",document.body.appendChild(e)})();
警告:你不应将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。
devtool: "nosources-source-map"
nosources-source-map 创建的 source map 不包含 sourcesContent(源代码内容)。虽然在 bundle 中增加了 //# sourceMappingURL,但是当我们在浏览器中打开源码文件时是看不到源码内容的。
index.js
(()=>{const e=document.createElement("div");e.className="demo",e.innerHTML="mini-css-extract-plugin",document.body.appendChild(e)})();
//# sourceMappingURL=index.js.map
这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。
插件介绍
Webpack 拥有非常强大的生态系统,社区中相关的工具也是数不胜数,这里我们介绍两个项目中常用的插件,可以节省开发效率和减少我们操作步骤。
html-webpack-plugin
html-webpack-plugin 可以自动创建 html 文件,也支持使用 html 文件模版。html-webpack-plugin 会自动将所有必要的 css、javascript、manifest 和 favicon 文件注入到生成的 html 文件中。之前在 5.2 章节做过 html-webpack-plugin 的介绍,所以这里不在详细介绍。
安装 html-webpack-plugin
npm install html-webpack-plugin --save-dev
webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
...
plugins: [
...
new HtmlWebpackPlugin()
]
};
clean-webpack-plugin
clean-webpack-plugin 插件用于在每次构建工程时清除上次构建生成的文件,有了这个插件我们再也不用手动清除构建目录了。
安装 clean-webpack-plugin
npm install clean-webpack-plugin --save-dev
webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
...
plugins: [
...
new CleanWebpackPlugin()
]
};
使用 webpack-dev-server
在之前的案例中,我们一直都是更改代码后执行打包命令,然后手动去打包文件中打开 html 文件查看效果,这种方式在实际的项目发开中会增加很多额外的重复工作,并且打包后的内容需要发布到服务上才能被其他伙伴访问。为了提升开发效率,在构建代码并部署到生产环境之前,我们需要一个本地环境,用于运行我们开发的代码。这个环境相当于提供了一个简单的服务器,用于访问 webpack 构建好的静态文件,我们日常开发时可以使用它来调试前端代码。webpack-dev-server 可以很好的帮我们解决这个需求,webpack-dev-server 是 webpack 官方提供的一个工具,可以基于当前的 webpack 构建配置快速启动一个静态服务。当 mode 为 development 时,会具备 hot reload 的功能,即当源码文件变化时,会即时更新当前页面,以便我们实时看到效果。webpack-dev-server 仅应用于开发环境。下面简单介绍下 webpack-dev-server 的使用,详细配置可查看官网。
安装
npm install webpack-dev-server -D
package.json 中增加 scripts
"scripts": {
...
"serve": "webpack serve"
},
配置成功后,命令行执行 npm run serve,项目启动成功并在控制台输出了网址,打开此网址即可运行我们现有代码。当我们修改 index.js 内容时页面会自动刷新。
模块热替换
当项目功能体量很大页面元素较多时,使用 webpack-dev-server 实现页面整体刷新会影响开发体验,这时我们会考虑使用模块热替换。
模块热替换(Hot Module Replacement 或 HMR)是 webpack 提供的最有用的功能之一。HMR 功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
使用 HMR
HMR 是 Webpack 内置的插件,我们可以通过 webpack-dev-server 配置来开启 HMR。
webpack.config.js
module.exports = {
...
devServer: {
hot: true,
}
}
上面的配置产生的结果是 Webpack 为每个模块绑定一个 module.hot 对象,这个对象可以调用 HMR 的 API。通过这些 API 我们可以对特定模块开启或关闭热替换。
index.js
const add = (a, b) => {
return a + b;
};
const addRes = add(3, 5);
const divElement = document.createElement('div');
divElement.innerHTML = addRes;
document.body.appendChild(divElement);
if (module.hot) {
module.hot.accept()
}
启动项目后可以在页面上看到结果 8,当修改 add 函数中的参数时,页面上 8 并没有清空,HMR 会使应用在当前浏览器环境下又执行来一遍 index.js (包括其依赖的模块)内容,但是页面本身并没有刷新。
调用 HMR API 可以如上面例子中手动调用,我们还可以借助现成的工具去调用,如 react-hot-loader、vue-loader 等。喜欢的小伙伴可以自行研究。
总结
本章我们介绍了 Webpack 开发环境下的常用的配置和插件以及如何使用 HMR。篇幅有限只写了些项目中用到的,Webpack 周边实用插件很多,感兴趣的小伙伴儿可以选择一些去使用一下,这对于了解 Webpack 也会有很大帮助。
阅读量:1719
点赞量:0
收藏量:0