用于在开发过程中,实时预览修改后的页面,无需重新加载整个页面。
其主要通过一下几种方式来加快开发速度:
保留在完全重新加载页面时丢失的应用程序状态。
只更新变更内容,以节省宝贵的开发时间。
调整样式更加快速 - 几乎相当于在浏览器调试器中更改样式。
核心概念:
entry: 入口文件
module:模块,webpack里一切皆模块,一个模块对应一个文件
chunk:代码块,对应多个module。
loader:模块转换器,用于模块内容的转换。
plugin:插件,在构建流程中监听特定的事件来做一些处理。
流程:
其中:
在热更新开启后,当webpack打包时,会向client端注入一段HMR runtime代码,同时server端会启动了一个HMR服务器,然后通过websocket和注入的runtime进行通信。
在webpack检测到文件修改后,会重新构建,并通过ws向client端发送更新消息,浏览器通过jsonp拉取更新过的模块,回调触发模块热更新逻辑。
主要更新流程如下图:
compile: 是webpack进行编译过程。
hmr-server: 建立连接并完成模块热更新的推送。
hmr-runtime: 运行时注入到bundle.js中的代码。
其中:
webpack-hot-middleware:实现热更新必须使用webpack-hot-middleware插件,该插件通过webpack的HMR API,浏览器和服务器之间建立连接并接收更新。它只专注于webpack和浏览器之间的通信机制。
accept函数:接受指定依赖项的代码更新,依赖的模块更新后回调函数被调用,如下表示接受特定moduleID的更新。
语法:
module.hot.accept(
dependencies, //表示依赖项,可以是一个字符串或字符串数组
callback //回调函数,用于在模块更新后触发的函数
);
具体例子:
if(module.hot){
module.hot.accept("./print.js",function(){
//使用更新过的print模块来执行某些操作·····
});
}
当accept的参数为errHandler时,表示此模块的代码在不通知父级扽情况下更新。当模块不导出任何内容时会应使用此选项。
4.check函数:检查所有当前加载的模块是否有更新,如果找到则应用更新如果未找到更新,则调用回调null
例如:
module.hot.check(autoApply).then(outdatedModules => {
//超时的模块
}).catch(error => {
//捕获错误
});
如果autoApply为true,将使用所有已处理的模块调用回调。
如果autoApply未设置,则将调用所有将被处置的模块。
autoApply 参数可以是布尔值,也可以是 options,当被调用时可以传递给 apply 方法。
5.apply函数:只要 module.hot.status() === ‘ready’就会继续更新过程,其中options 的ignoreUnaccepted为true,表示某些模块未被接受(并且会冒泡到入口点),但更新过程仍会继续。
例如:
module.hot.apply(options).then(outdatedModules => {
//超时的模块
}).catch(error => {
//捕获错误
});
6.status函数:表示当前hmr的状态:module.hot.status(); // 返回以下字符串之一
(1)通过插件实现:
下载安装插件:
npm install –save-dev webpack-hot-middleware
因为它是webpack内置的HMR插件所以不用引用,直接在配置文件的plugins中创建插件对象即可:
new webpack.HotModuleReplacementPlugin() //热替换模块插件
创建一个print.js作为子,将index.js作为父,具体代码如下:
//print.js
export default function printMe() {
//console.log('I get called from print.js!');
console.log('hello vue!');
}
//index.js
import _ from 'lodash'
import printMe from './print.js'
import Vue from 'vue'
Vue.config.productionTip = false
function component() {
var element = document.createElement('div');
var btn = document.createElement('button');
element.innerHTML ='Hello webpack!';
btn.innerHTML = 'Click me!';
btn.onclick = printMe;
element.appendChild(btn);
return element;
}
let element = component();
//当 print.js 改变导致页面重新渲染时,重新获取渲染的元素
document.body.appendChild(element);
if (module.hot) {
module.hot.accept('./print.js', function() {//表示接受更新
console.log('Accepting the updated printMe module!');
document.body.removeChild(element);
element = component(); // 重新渲染component到点击事件上
document.body.appendChild(element);
})
}
export default {
name:'component'
};
这里在index.js中创建一个点击按钮,当点击按钮的时候控制台会输出print.js中的内容。
首先,当print.js内容未改变时,运行项目,其显示效果如下:
当改变其内容,但是不运行项目时,浏览器控制台显示如下:
过一会后:
通过插件可以实现浏览器的无刷新更新。
(2)通过node.js的API来实现:
原理:
在使用webpack-dev-server和node.js API时,将dev服务器作为第二参数进行传递而不是webpack的配置对象。
首先:创建一个server.js,index.js与print.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);
//webpack-dev-server中具自带方法,他能添加 HMR的入口点
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, options),
//将dev服务器选项放在webpack配置对象,作为第二个参数传递
server.listen(3000, 'localhost', () => {
console.log('dev server listening on port 3000');
//监听3000端口
});
其次创建一个style.css,并在index.js中引用:
import './style.css'
并下载相应加载css的loader:
npm install –save-dev npm install –save-dev style-loader css-loader 加载css文件的loader
npm install –save postcss-loader PostCSS用于处理CSS的webpack的加载器
在moudle中规定好其匹配规则:
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
},
{
test:/\.vue$/, //匹配所有的.vue文件
loader:'vue-loader',//依赖的loader包名
options: {
loaders:{
css: ExtractTextPlugin.extract({
use: 'css-loader',
fallback: 'vue-style-loader'
})
//用来提取vue中的css样式
}
}
}
style.css如下:
body{
background: red;
/* background: blue;*/
}
效果同上,浏览器无需刷新而模块进行更新。
参考:
webpack热更新流程
热模块更换
webpack官方文档