上一篇文章介绍了如何打包成 HTML 文件,接着我们将介绍如何搭建开发环境之热更新。
使用 source-map
如果按照默认的 production 模式打包代码是,可能会很难追踪错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js
, b.js
和 c.js
)打包到一个 bundle(bundle.js)
中,而其中一个源文件包含一个错误,那么堆栈跟踪就会直接指向到 bundle.js
。你可能需要准确地知道错误来自于哪个源文件,所以这种提示这通常不会提供太多帮助。
为了更容易地追踪 error 和 warning,JavaScript 提供了 source maps 功能,可以将编译后的代码映射回原始源代码。如果一个错误来自于 b.js
,source map 就会明确的告诉你。
source map 有许多可用选项,请务必仔细阅读它们,以便可以根据需要进行配置。
对于本指南,我们将使用 eval-source-map
选项,这有助于解释说明示例意图(此配置仅用于示例,不要用于生产环境):
选择一个工具
在每次编译代码时,手动运行 yarn run build
会显得特别麻烦。
webpack 提供了几种可选方式,帮助我们在代码发生变化后自动编译代码:
- webpack watch mode(webpack 观察模式)
- webpack-dev-server
- webpack-dev-middleware
webpack 可以在 watch mode
(观察模式)下使用。在这种模式下,webpack 将监视您的文件,并在更改时重新编译。
webpack-dev-server
提供了一个易于部署的开发服务器,具有快速的实时重载(live reloading)功能。
如果你已经有一个开发服务器并且需要完全的灵活性,可以使用 webpack-dev-middleware
作为中间件。
webapck-dev-server
和 webpack-dev-middleware
使用内存编译,这意味着 bundle
不会被保存在硬盘上。这使得编译十分迅速,并导致你的文件系统更少麻烦。
在大多数情况下你会想要使用 webpack-dev-server
,因为这是最简单的开始的方式,并且提供了很多开箱即用的功能。本项目中也将会使用到它。
简单配置 webpack-dev-server
# 安装
$ yarn add webpack-dev-server@3.9.0 --dev
// 修改 webpack.config.js 配置
devServer: {
contentBase: './dist',
open: true
}
// 在 package.json 添加 npm script
"scripts": {
"dev": "webpack-dev-server --config webpack.config.js --colors"
}
以上配置告知 webpack-dev-server
,将 dist
目录下的文件 serve
到 localhost:8080
下。(译注:serve
,将资源作为 server
的可访问文件)。
现在,在命令行中运行 yarn run dev
,我们会看到浏览器自动加载页面。如果你更改任何源文件并保存它们,web server 将在编译代码后自动重新加载。
但是同时可以观察到一个细节,每次更改文件页面会重新加载,但是这应该不是我们想要的,我们想要的是模块热替换(hot module replacement
)。
HMR 模块热替换配置
*首先它不适用于生产环境,仅应用于开发环境。
模块热替换功能会在应用程序运行过程中,替换、添加或删除模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
// webpack.config.js
const config = {
// ... 其他已有配置不变
devServer: {
contentBase: './dist',
hot: true,
open: true
},
plugins: [
// 在 webpack 4 中其实已被弃用,取代它的是 optimization.namedModules 后续的文章会讲解到
// new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}
我们在入口文件 index.js
引入 main.js
。
// main.js
console.log('This is main.js')
我们来修改 index.js
文件,以便当 main.js
内部发生变更时可以告诉 webpack 接受更新的模块。
// index.js
import './main.js'
console.log('Hello Webpack!')
if(module.hot) {
module.hot.accept('./main.js', () => {
console.log('Accept update!')
})
}
注意,若修改了 webpack 配置文件,我们需要重新执行
yarn run dev
使其生效。
当我们修改 main.js
按下保存之后,会看到浏览器控制台会输出以下信息:
[WDS] App updated. Recompiling...
[WDS] App hot update...
[HMR] Checking for updates on the server...
This is main.js.
We modified the main.js file.
Accept update!
[HMR] Updated modules:
[HMR] - ./src/main.js
[HMR] App is up to date.
HMR 加载样式
借助于 style-loader
,使用模块热替换来加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept
,在 CSS 依赖模块更新之后,会将其 patch(修补) 到 <style>
标签中。
在此之前,我们说过 webpack 只能读取 JavaScript 和 JSON 文件,其他类型的文件需要借助对应的 loader 来处理。
首先使用以下命令安装两个 loader 。
$ yarn add --dev css-loader@3.2.0 style-loader@1.0.0
更新 webpack 配置文件
// webpack.config.js
const config = {
module: {
rules: [
{
test: /\.css/,
use: ['style-loader', 'css-loader']
}
]
}
}
module.exports = config
我们在 src
目录下新增 style.css
文件,并在 index.js
中引入。
/* style.css */
body {
font-size: 30px;
}
// index.js
import './main.js'
import './style.css'
console.log('Hello Webpack!')
if(module.hot) {
module.hot.accept('./main.js', () => {
console.log('Accept update!')
})
}
然后我们试着修改 style.css
的样式,就能看到页面字体大小随之更改,而无需完全刷新。
至此
我们最简单的 HMR 已经配置好了,接着我们将会介绍接入 React 。
最后附上
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const webpack = require('webpack')
const config = {
mode: 'development',
devtool: 'eval-source-map',
entry: {
index: path.resolve(__dirname, './src/index.js')
},
devServer: {
contentBase: './dist',
hot: true,
open: true
},
plugins: [
new HtmlWebpackPlugin({
title: '开发环境', // 模板要使用 <title><%= htmlWebpackPlugin.options.title %></title> 配置才生效
template: './src/index.html', // 模板路径
filename: 'index.html', // 输出 HTML 文件名称
inject: 'body', // 插入的 script 标签位于 body 底部,true 同理
hash: true, // 加上 hash 值
favicon: './src/favicon.ico'
}),
// 新版无需再指定删除目录,默认删除 output 目录
new CleanWebpackPlugin(),
// 热更新
new webpack.NamedModulesPlugin(),
new webpack.HotModuleReplacementPlugin()
],
module: {
rules: [
{
test: /\.css/,
use: ['style-loader', 'css-loader']
}
]
}
}
module.exports = config
// index.js
import './main.js'
import './style.css'
console.log('Hello Webpack!')
if(module.hot) {
module.hot.accept('./main.js', () => {
console.log('Accept update!')
})
}
// 当前项目目录
webpack4_demo
| - /assets
| - /config
| - /dist
| - some outputh file
| - /src
| - favicon.ico
| - index.html
| - index.js
| - main.js
| - styles.css
| - .gitignore
| - package.json
| - README.md
| - webpack.config.js
| - yarn.lock
网友评论