Webpack 开发环境
使用 source map 找到错误源代码
使用 devtool
选项,来如何生成 source map。另外使用 SourceMapDevToolPlugin
进行更细粒度的配置,用 source-map-loader
来处理已有的 source map。
注意: 可以直接使用 SourceMapDevToolPlugin
/EvalSourceMapDevToolPlugin
来替代使用 devtool
选项, 但切勿同时使用 devtool
选项, devtool
选项在内部添加过这些插件,所以会最终将应用两次插件。
devtool 的选项: 关于下面的选项 webpack 仓库中包含一个 显示所有 devtool
变体效果的示例 。
devtool | 构建速度 | 重新构建速度 | 生产环境 | 品质(quality) |
---|---|---|---|---|
(none) | +++ | +++ | yes | 打包后的代码 |
eval | +++ | +++ | no | 生成后的代码 |
cheap-eval-source-map | + | ++ | no | 转换过的代码(仅限行) |
cheap-module-eval-source-map | o | ++ | no | 原始源代码(仅限行) |
eval-source-map | -- | + | no | 原始源代码 |
cheap-source-map | + | o | yes | 转换过的代码(仅限行) |
cheap-module-source-map | o | - | yes | 原始源代码(仅限行) |
inline-cheap-source-map | + | o | no | 转换过的代码(仅限行) |
inline-cheap-module-source-map | o | - | no | 原始源代码(仅限行) |
source-map | -- | -- | yes | 原始源代码 |
inline-source-map | -- | -- | no | 原始源代码 |
hidden-source-map | -- | -- | yes | 原始源代码 |
nosources-source-map | -- | -- | yes | 无源代码内容 |
- 打包后的代码
将所有生成的代码视为一大块代码,看不到相互分离的模块
- 生成后的代码
每个模块相互分离,并用模块名称进行注释。
可以看到 webpack 生成的代码。示例:会看到类似
var module__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(42); module__WEBPACK_IMPORTED_MODULE_1__.a();
,而不是import {test} from "module"; test();
。 - 转换过的代码
每个模块相互分离,并用模块名称进行注释。
可以看到 webpack 转换前、loader 转译后的代码。示例:你会看到类似
import {test} from "module"; var A = function(_test) { ... }(test);
,而不是import {test} from "module"; class A extends test {}
。 - 原始源代码
每个模块相互分离,并用模块名称进行注释。
会看到转译之前的代码,正如编写它时。取决于 loader 支持。
- 无源代码内容
source map 中不包含源代码内容。
浏览器通常会尝试从 web 服务器或文件系统加载源代码。所以必须确保正确设置
output.devtoolModuleFilenameTemplate
,以匹配源代码的 url。 - (仅限行)
source map 被简化为每行一个映射。这通常意味着每个语句只有一个映射(假设你使用这种方式)。这会妨碍你在语句级别上调试执行,也会妨碍你在每行的一些列上设置断点。与压缩后的代码组合后,映射关系是不可能实现的,因为压缩工具通常只会输出一行。
适合开发环境的 devtool 选项
- eval
每个模块都使用
eval()
执行,并且都有//@ sourceURL
。此选项会非常快地构建。缺点是,由于会映射到转换后的代码,而不是映射到原始代码(没有从 loader 中获取 source map),所以不能正确的显示行数。
- eval-source-map
每个模块使用
eval()
执行,并且 source map 转换为 DataUrl 后添加到eval()
中。初始化 source map 时比较慢,但是会在重新构建时提供比较快的速度,并且生成实际的文件。行数能够正确映射,因为会映射到原始代码中。它会生成用于开发环境的最佳品质的 source map。 - cheap-eval-source-map
类似
eval-source-map
,每个模块使用eval()
执行。这是 "cheap(低开销)" 的 source map,因为它没有生成列映射(column mapping),只是映射行数。它会忽略源自 loader 的 source map,并且仅显示转译后的代码,就像eval
devtool。 - cheap-module-eval-source-map
类似
cheap-eval-source-map
,并且,在这种情况下,源自 loader 的 source map 会得到更好的处理结果。然而,loader source map 会被简化为每行一个映射(mapping)。
适合生产环境的 devtool 选项
- none
省略
devtool
选项。不生成 source map - source-map
整个 source map 作为一个单独的文件生成。它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。
注意应该将服务器配置为,不允许普通用户访问 source map 文件!
- hidden-source-map
与
source-map
相同,但不会为 bundle 添加引用注释。如果只想 source map 映射那些源自错误报告的错误堆栈跟踪信息,但不想为浏览器开发工具暴露 source map,这个选项会很有用。
注意不应将 source map 文件部署到 web 服务器。而是只将其用于错误报告工具。
- nosources-source-map
创建的 source map 不包含
sourcesContent(源代码内容)
。它可以用来映射客户端上的堆栈跟踪,而无须暴露所有的源代码。可以将 source map 文件部署到 web 服务器。这仍然会暴露反编译后的文件名和结构,但它不会暴露原始代码。
注意: 在使用 terser-webpack-plugin
时,你必须提供 sourceMap:true
选项来启用 source map 支持。
特定场景
以下选项对于开发环境和生产环境并不理想。他们是一些特定场景下需要的,例如,针对一些第三方工具。
- inline-source-map
source map 转换为 DataUrl 后添加到 bundle 中
- cheap-source-map
没有列映射(column mapping) 的 source map,忽略 loader source map。
- inline-cheap-source-map
类似
cheap-source-map
,但是 source map 转换为 DataUrl 后添加到 bundle 中。 - cheap-module-source-map
没有列映射(column mapping) 的 source map,将 loader source map 简化为每行一个映射(mapping)。
- inline-cheap-module-source-map
类似
cheap-module-source-map
,但是 source map 转换为 DataUrl 添加到 bundle 中。
解决手动输入 npm run xxx
的麻烦
webpack 提供几种可选方式,帮助在代码发生变化后自动编译代码:
webpack watch mode
webpack 观察模式
# package.json
"scripts": {
"watch":"webpack --watch"
},
在命令行中运行 npm run watch
,每次修改会自动编译代码。
唯一的缺点是,为了看到修改后的实际效果,需要刷新浏览器。
webpack-dev-server
配置文档: https://v4.webpack.docschina.org/configuration/dev-server
webpack-dev-server
为你提供了一个简单的 web server,并且具有 live reloading(实时重新加载) 功能。
npm install --save-dev webpack-dev-server
# webpack.config.js
module.exports = {
devServer:{
contentBase: './dist', // 告知 webpack-dev-server,将 dist 目录下的文件 serve 到 localhost:8080 下
}
}
webpack-dev-server 在编译之后不会写入到任何输出文件。而是将 bundle 文件保留在内存中,然后将它们 serve 到 server 中,就好像它们是挂载在 server 根路径上的真实文件一样。如果希望页面在其他不同路径中找到 bundle 文件,则可以通过 dev server 配置中的 publicPath
选项进行修改。
# package.json
"scripts": {
"start": "webpack-dev-server --open"
},
运行 npm start
,浏览器自动加载页面,更改任何源文件并保存它们,web server 将在编译代码后自动重新加载。
启用热模块更新 HMR
更新 webpack-dev-server 配置,然后使用 webpack 内置的 HMR 插件。如果你在技术选型中使用了 webpack-dev-middleware
而没有使用 webpack-dev-server
,请使用 webpack-hot-middleware
package,以在你的自定义 server 或应用程序上启用 HMR。
# webpack.config.js
const webpack = require('webpack');
module.exports={
devServer: {
contentBase: './dist',
hot: true
},
plugins:[
new webpack.HotModuleReplacementPlugin()
]
}
# package.json
{
"scripts": {
"start": "webpack-dev-server --hotOnly"
},
}
修改 index.js
文件,以便在 print.js
内部发生变更时,告诉 webpack 接受 updated module
if (module.hot) {
// 在 print.js 内部发生变更时,告诉 webpack 接受 updated module
module.hot.accept('./print.js',function () {
console.log('Accepting the updated printMe module!');
printMe();
})
}
通过 Node.js API
在 Node.js API 中使用 webpack dev server 时,不要将 dev server 选项放在 webpack 配置对象(webpack config object) 中。而是,在创建时,将其作为第二个参数传递。例如:
new WebpackDevServer(compiler, options)
想要启用 HMR,还需要修改 webpack 配置对象,使其包含 HMR 入口起点。webpack-dev-server 的 addDevServerEntrypoints
的方法可以通过使用这个方法来实现。
# dev-server.js
const webpackDevServer = require('webpack-dev-server');
const webpack = require('webpack');
const config = require('./webpack.config.js');
const options = {
contentBase: './dist',
hot: true,
host: 'localhost'
};
webpackDevServer.addDevServerEntrypoints(config, options);
const compiler = webpack(config);
const server = new webpackDevServer(compiler, options);
server.listen(5000, 'localhost', () => {
console.log('dev server listening on port 5000');
});
借助于 style-loader
,使用模块热替换来加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept
,在 CSS 依赖模块更新之后,会将其 patch(修补) 到 <style>
标签中
# webpack.config.js
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
},
社区还提供许多其他 loader 和示例,可以使 HMR 与各种框架和库平滑地进行交互……
- React Hot Loader :实时调整 react 组件。
- Vue Loader :此 loader 支持 vue 组件的 HMR,提供开箱即用体验。
- Elm Hot Loader :支持 Elm 编程语言的 HMR。
- Angular HMR :没有必要使用 loader!直接修改 NgModule 主文件就够了,它可以完全控制 HMR API。
webpack-dev-middleware
webpack-dev-middleware
是一个封装器(wrapper),它可以把 webpack 处理过的文件发送到一个 server。 webpack-dev-server
在内部使用了它,然而它也可以作为一个单独的 package 来使用,以便根据需求进行更多自定义设置。
下面是一个 webpack-dev-middleware 配合 express server 的示例:
安装: npm install --save-dev express webpack-dev-middleware
# webpack.config.js
module.exports = {
mode: 'development',
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/', // 在 server 脚本使用 publicPath,以确保文件资源能够正确地 serve 在 http://localhost:3000 下
}
};
# server.js
const express = require('express');
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const app = express();
const config = require('./webpack.config.js');
const compiler = webpack(config);
// 告诉 express 使用 webpack-dev-middleware,
// 以及将 webpack.config.js 配置文件作为基础配置
app.use(webpackDevMiddleware(compiler, {
publicPath: config.output.publicPath
}));
// 将文件 serve 到 port 3000。
app.listen(3000, function () {
console.log('Example app listening on port 3000!\n');
});
# package.json
{
"scripts": {
"server": "node server.js",
},
}
注意:某些编辑器具有 safe write(安全写入)
功能,会影响重新编译:
- Sublime Text 3:在用户首选项(user preferences) 中添加
atomic_save: 'false'
。 - JetBrains IDEs (e.g. WebStorm):在
Preferences > Appearance & Behavior > System Settings
中取消选中 "Use safe write"。 - Vim:在设置(settings) 中增加
:set backupcopy=yes
。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
上一篇: Webpack 模式 mode
下一篇: 彻底找到 Tomcat 启动速度慢的元凶
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论