在前端开发中,我们做的最频繁的是刷新浏览器,清楚缓存。如果以webpack打包方式开发,修改代码的时候我们还需要重新构建,再重新刷新浏览器,效率肯定很低。
采用热更新技术可以提高效率,通俗就是说,我一修改js文件且保存,webpack自动构建,浏览器自动刷新。
感觉很黑科技,其实就是用webpack把js打包到内存里面,为了达到此效果,我们需要配置一个本地服务器,主页面请求此服务器的js,css等资源,前台和后台通过websocket协议传输, 所以就能实时更新。服务器模块可以选择express也可以使用webpack推荐的webpack-dev-server,这里我选择的是webpack-dev-server
通过webpack-dev-server配置热更新,有两种方式,一种是用nodejs方式启动,另外一种是通过webpack-dev-server命令,第二种需要全局安装webpack-dev-server,这里我选择nodejs配置,不需要全局安装,本地安装即可
第一篇文章已经在项目目录下载好了基本依赖包。我们就从第一篇文章介绍的项目入手,使其支持热更新。
注意,我的webpack版本是3.10.0,webpack-dev-server就必须选用2.x.x。这是个坑,之前我下载的webpack-dev-server是3.X.X,导致一直报错comp.hooks.compile.tap(‘webpack-dev-server’, invalidPlugin);。最后看到某篇帖子,这位仁兄说他也是在google里面才找到答案,如果webpack是4.x.x,那么webpack-dev-server就是3.x.x,如果webpack是3.x.x,webpack就选2.x.x。
1、首先我们在根目录下创建一个webpack.config.dev.js文件,代码如下
var webpack = require('webpack')
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
entry: {
bundle:['webpack-dev-server/client?http://127.0.0.1:9090','webpack/hot/dev-server','./index.js'] (1)
},
output: {
path: __dirname + '/build/',
filename:'bundle.js',
publicPath: './'
},
module:{
loaders:[
{
test: /\.js$/,
loader:'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("css-loader","style-loader")
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192&name=distImg/[name].[ext]'
}
]
},
plugins:[
new ExtractTextPlugin('prefixer_main.css', {
disable: false,
allowChunks: true
}),
new webpack.HotModuleReplacementPlugin() (2)
]
}
(1):入口文件写成数组形式,一旦webpack开始工作,所有的模块都会被加载进来,最后一个会被输出(看文档自己翻译,不是很懂,这里数组的前2个就得这么写,具体原因我也不是很清楚,’webpack-dev-server/client?http://127.0.0.1:9090‘这个是用于配置webpack-dev-server的inline模式(nodejs方式启动webpack-dev-server)。
(2): 插件项,打包构建过程中的动作,生成内存中的热替换块(看文档自己翻译,我想就是打包后放到内存中的动作吧)
2、我们在根目录下创建一个server.js文件
var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config.dev') (1)
new WebpackDevServer(webpack(config),{
publicPath:'http://127.0.0.1:9090/storage', (2)
hot: true, (3)
inline : true, (4)
historyApiFallback: true, (5)
}).listen(9090,'127.0.0.1',function(err, result){ (6)
if(err){
console.log(err)
}else{
console.log('Listening at 127.0.0.1:9090');
}
})
(1):将webpack.config.dev的配置引入。
(2):在index.html里面的script src=”http://127.0.0.1:9090/storage/bundle.js”的依据就是它(这里我也是只知其然,不知所以然,通过测试试出来的)。
(3):开启热更新
(4):webpack-dev-server的inline模式,基本常用这个模式,它还有一个iframe模式, iframe mode实际上是在网页中嵌入了一个iframe,然后将我们自己的应用注入到这个iframe 当中去, 当每次文件被修改后,这个iframe会进行了reload,当我们用iframe模式的时候,需要浏览器中输入http://127.0.0.1:9090/webpack-dev-server/index.html(inline mode下直接输入http://127.0.0.1:9090)同时,webpack.config.dev中入口文件项entry中配置的’webpack-dev-server/client?http://127.0.0.1:9090‘就不需要了。两者在实际效果没区别,一般我们都用的是inline模式。
(5):在开发单页应用时非常有用,它依赖于HTML5 history API,如果设置为true,不存在的页面会被替换成index.html,比如你访问http://127.0.0.1:9090/q2e/dfef 和 http://127.0.0.1:9090一样会返回http://127.0.0.1:9090/index.html资源
(6):通过webpack(config)将配置配到WebpackDevServer上,然后监听127.0.0.1的9090端口。这样webpack-dev-server配置热更新完毕。
3、新建一个index.html文件。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>INDEXtitle>
<link rel="stylesheet" href="http://127.0.0.1:9090/storage/prefixer_main.css" type="text/css" />
head>
<body>
<div id='main'>div>
<script src="http://127.0.0.1:9090/storage/bundle.js">script>
body>
html>
4、打开cmd定位到根目录运行 node server.js
,然后访问127.0.0.1::9090,最后更改index.js,此时不用手动构建和刷新浏览器,页面自动刷新。
此时,项目目录并未生成build目录。我们用dom探测器查看页面。
可以看到,index.html的结构和main.html(第一篇文章创建)引用资源的形式很相似。因此我猜想,webpack-dev-server将资源打包到内存,server.js的publicPath指定访问内存资源的地址(可能在我们看不见的地方生成了bundle.js,prefixer_main.css,distImg/test.png吧)。
5、修改index.js或者main.css,页面自动刷新。
为什么要建立index.html,之前的main.html(第一篇文章创建)不行吗。其实是可以的,index.html是默认项而已,下面是如何指定为main.html
修改webpack.config.dev.js
var webpack = require('webpack')
var ExtractTextPlugin = require('extract-text-webpack-plugin')
var HtmlWebpackPlugin = require('html-webpack-plugin'); (1)
module.exports = {
entry: {
bundle:['webpack-dev-server/client?http://127.0.0.1:9090','webpack/hot/dev-server','./index.js']
},
output: {
path: __dirname + '/build/',
filename:'bundle.js',
publicPath: './'
},
module:{
loaders:[
{
test: /\.js$/,
loader:'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
loader: ExtractTextPlugin.extract("css-loader","style-loader")
},
{
test: /\.(png|jpg)$/,
loader: 'url-loader?limit=8192&name=distImg/[name].[ext]'
}
]
},
plugins:[
new HtmlWebpackPlugin({
template: './main.html'
}), (2)
new ExtractTextPlugin('prefixer_main.css', {
disable: false,
allowChunks: true
}),
new webpack.HotModuleReplacementPlugin()
]
}
(1):定义HtmlWebpackPlugin ; (2):在plugins添加HtmlWebpackPlugin,指定main.html
修改main.html
<html lang="en">
<head>
<meta charset="UTF-8">
<title>MAINtitle>
<link rel="stylesheet" href="http://127.0.0.1:9090/storage/prefixer_main.css" type="text/css" />
head>
<body>
<h3>I AM THE MAIN.HTMLh3>
<div id='main'>div>
<script src="http://127.0.0.1:9090/storage/bundle.js">script>
body>
html>
在项目目录输入 node server.js
。访问http://127.0.0.1:9090/main.html(不能直接用http://127.0.0.1:9090)。在这里,如果页面不存在就不会重定向到index.html里面了。为了方便还是用默认的index.html吧。
下一篇会介绍分模块打包,使部署更合理。
git地址:https://github.com/kiramario/hello-world.git