注意attention: 转载学习来源webpack 插件拾趣 (1) —— webpack-dev-server
webpack-dev-middleware
假设我们在服务端使用 express 开发一个站点,同时也想利用 webpack 对静态资源进行打包编译,那么在开发环节,每次修改完文件后,都得先执行一遍 webpack 的编译命令,等待新的文件打包到本地,再做进一步调试。虽然咱们可以利用 webpack 的 watch mode 来监听变更、自动打包,但等待 webpack 重新执行的过程往往很耗时。
而 webpack-dev-middleware 的出现很好地解决了上述问题 —— 作为一个 webpack 中间件,它会开启 watch mode 监听文件变更,并自动地在内存中快速地重新打包、提供新的 bundle。
说白了就是 —— 自动编译(watch mode)+速度快(全部走内存)!
// app.js
const path = require('path');
const express = require("express");
const ejs = require('ejs');
const app = express();
const webpack = require('webpack');
const webpackMiddleware = require("webpack-dev-middleware");
let webpackConf = require('./webpack.config.js');
app.engine('html', ejs.renderFile);
app.set('views', path.join(__dirname, 'src/html'));
app.set("view engine", "html");
var compiler = webpack(webpackConf);
app.use(webpackMiddleware(compiler, { //使用 webpack-dev-middleware
publicPath: webpackConf.output.publicPath //保持和 webpack.config.js 里的 publicPath 一致
}));
app.get("/", function(req, res) {
res.render("index");
});
app.listen(3333);
// webpack.config.js
module.exports = {
entry: './app.js',
output: {
publicPath: "/assets/",
filename: 'bundle.js',
//path: '/' //只使用 dev-middleware 可以忽略本属性
publicPath —— 它用于决定 webpack 打包编译后的文件,要存放在内存中的哪一个虚拟路径,并提供一个 SERVER,将路径和文件映射起来(即使它们都是虚拟的,但依旧可请求的到)。
当前的例子,是将内存路径配置为 /assets/,这意味着打包后的 bundle.js 会存放在虚拟内存路径 SERVERROOT/assets/ 下(这里的“SERVERROOT”实际上即 html 文件的访问路径),也意味着我们可以直接在 src/html/index.html 中通过 src=’assets/bundle.js’ 的形式引用和访问内存中的 bundle 文件:
<body>
<div></div>
<script src="assets/bundle.js"></script>
</body>
同时,只要我们修改了页面的脚本模块(比如 src/js/index.js),webpack-dev-middleware 便会自行重新打包到内存,替换掉旧的 bundle,我们只需要刷新页面即可看到刚才的变更。
源码研究
注意attention: 转载学习来源webpack-dev-middleware@1.12.2 源码解读
webpack-dev-middleware这个中间件内部其实主就是做了两件事,第一就是在中间件函数初始化时,修改webpack的文件操作对象,让webpack编译后的文件输出到内存里,以监听模式启动webpack。第二就是当有http get请求过来时,中间件函数内部读取webpack输出到内存里的文件,然后输出到response上,这时候浏览器拿到的就是webpack编译后的资源文件了。
HMR
HMR 即模块热替换(hot module replacement)的简称,它可以在应用运行的时候,不需要刷新页面,就可以直接替换、增删模块。
webpack 可以通过配置 webpack.HotModuleReplacementPlugin 插件来开启全局的 HMR 能力,开启后 bundle 文件会变大一些,因为它加入了一个小型的 HMR 运行时(runtime),当你的应用在运行的时候,webpack 监听到文件变更并重新打包模块时,HMR 会判断这些模块是否接受 update,若允许,则发信号通知应用进行热替换。
webpack-hot-middleware
// app.js
app.engine('html', ejs.renderFile);
app.set('views', path.join(__dirname, 'src/html'));
app.set("view engine", "html");
var compiler = webpack(webpackConf);
app.use(webpackMiddleware(compiler, {
publicPath: webpackConf.output.publicPath
}));
//添加的代码段,引入和使用 webpack-hot-middleware
app.use(require("webpack-hot-middleware")(compiler, {
path: '/__webpack_hmr'
}));
app.get("/", function(req, res) {
res.render("index");
});
app.listen(3333);
这里的 options 是 webpack-hot-middleware 的配置项,详细见官方文档,这里咱们只填一个必要的 path —— 它表示 webpack-hot-middleware 会在哪个路径生成热更新的事件流服务,且访问的页面会自动与这个路径通过 EventSource 进行通讯,来拉取更新的数据重新粉饰自己。
这里要了解下,实际上 webpack-hot-middleware 最大的能力,是让 SERVER 能够和 HMR 运行时进行通讯,从而对模块进行热更新。
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: ['webpack-hot-middleware/client', './app.js'], //修改点1
output: {
publicPath: "/assets/",
filename: 'bundle.js'
},
plugins: [ //修改点2
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin() //出错时只打印错误,但不重新加载页面
]
};
HTML 5 服务器发送事件
<script>
if(typeof(EventSource)!=="undefined"){
var source = new EventSource("/example/html5/demo_sse.php");
source.onmessage = function(event){
document.getElementById("result").innerHTML+=event.data + "<br />";
};
}
else{
document.getElementById("result").innerHTML="抱歉,您的浏览器不支持 server-sent 事件 ...";
}
</script>
为了让上面的例子可以运行,您还需要能够发送数据更新的服务器(比如 PHP 和 ASP)。
服务器端事件流的语法是非常简单的。把 “Content-Type” 报头设置为 “text/event-stream”。现在,您可以开始发送事件流了。
<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
$time = date('r');
echo "data: The server time is: {$time}\n\n";
flush();
?>
源码研究
注意attention: 转载学习来源webpack-hot-middleware解读
webpack-dev-server
顾名思义,webpack-dev-server 相对前两个工具多了个“server”,实际上它的确也是在 webpack-dev-middleware 的基础上多套了一层壳来提供 CLI 及 server 能力
// webpack.config.js
const path = require('path');
const webpack = require('webpack');
module.exports = {
entry: './app.js',
output: {
publicPath: "/assets/",
filename: 'bundle.js'
},
devServer: {
contentBase: path.join(__dirname, "src/html"),
port: 3333,
hot: true // 让 dev-server 开启 HMR
},
plugins: [
new webpack.HotModuleReplacementPlugin() //让 webpack 启动全局 HMR
]
};