开发环境页面热更新早已是主流,常见的需求如赛事网页推送比赛结果、网页实时展示投票或点赞数据、在线评论或弹幕、在线聊天室等,都需要借助热更新功能,才能达到实时的端对端的极致体验。
webpack-hot-middleware
中间件是webpack的一个plugin,通常结合webpack-dev-middleware一起使用。借助它可以实现浏览器的无刷新更新(热更新),即webpack里的HMR(Hot Module Replacement)。如何配置请参考 webpack-hot-middleware,如何理解其相关插件请参考 手把手深入理解 webpack dev middleware 原理與相關 plugins。
webpack加入webpack-hot-middleware后,内存中的页面将包含HMR相关js,加载页面后,Network栏可以看到如下请求:
__webpack_hmr是一个type为EventSource的请求, 从Time栏可以看出:默认情况下,服务器每十秒推送一条信息到浏览器。
如果此时关闭开发服务器,浏览器由于重连机制,将持续抛出类似GET http://www.test.com/__webpack_hmr 502 (Bad Gateway)
这样的错误。重新启动开发服务器后,重连将会成功,此时便会刷新页面。以上这些便是我们使用时感受到的最初的印象。当然,停留在使用层面不是我们的目标,接下来我们将跳出该中间件,讲解其所使用到的EventSource技术。
EventSource 不是一个新鲜的技术,它早就随着H5规范提出了,正式一点应该叫Server-sent events,即SSE。鉴于传统的通过ajax轮训获取服务器信息的技术方案已经过时,我们迫切需要一个高效的节省资源的方式去获取服务器信息,一旦服务器资源有更新,能够及时地通知到客户端,从而实时地反馈到用户界面上。EventSource就是这样的技术,它本质上还是HTTP,通过response流实时推送服务器信息到客户端。
客户端新建一个EventSource对象非常简单。
const es = new EventSource('/message');// /message是服务端支持EventSource的接口
服务端实现/message接口,需要返回类型为 text/event-stream的响应头。
var http = require('http');
http.createServer(function(req,res){
if(req.url === '/message'){
res.writeHead(200,{
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
setInterval(function(){
res.write('data: ' + +new Date() + '\n\n');
}, 1000);
}
}).listen(8888);
我们注意到,为了避免缓存,Cache-Control 特别设置成了 no-cache,为了能够发送多个response, Connection被设置成了keep-alive.。发送数据时,请务必保证服务器推送的数据以 data:开始,以\n\n结束,否则推送将会失败(原因就不说了,这是约定的)。
以上,服务器每隔1s主动向客户端发送当前时间戳,为了接受这个信息,客户端需要监听服务器。如下:
es.onmessage = function(e){
console.log(e.data); // 打印服务器推送的信息
}
es可以监听任何指定类型的事件。
es.addEventListener("####", function(e) {// 事件类型可以随你定义
console.log('####:', e.data);
},false)
服务器发送不同类型的事件时,需要指定event字段。
res.write('event: ####\n');
res.write('data: 这是一个自定义的####类型事件\n');
res.write('data: 多个data字段将被解析成一个字段\n\n');
使用EventSource技术实时更新网页信息十分高效。实际使用中,我们几乎不用担心兼容性问题,主流浏览器都了支持EventSource,当然,除了掉队的IE系。对于不支持的浏览器,其PolyFill方案请参考HTML5 Cross Browser Polyfills
关于CORS配置可以参考我另一篇博文 前端跨域详解
既然说到了EventSource,便有必要谈谈遇到的坑,接下来,就说说我遇到的webpack热更新延迟问题。
如我们所知,webpack借助webpack-hot-middleware插件,实现了网页热更新机制,正常情况下,浏览器打开 http://localhost:8080 这样的网页即可开始调试。然而实际开发中,由于远程服务器需要种cookie登录态到特定的域名上等原因,因此本地往往会用nginx做一层反向代理。即把 http://www.test.com 的请求转发到 http://localhost:8080 上转发过后,发现热更新便延迟了。
原因是nginx默认开启的buffer机制缓存了服务器推送的片段信息,缓存达到一定的量才会返回响应内容。只要关闭proxy_buffering即可。配置如下所示:
server {
listen 80;
server_name www.test.company.com;
location / {
proxy_pass http://localhost:8080;
proxy_buffering off;
}
}
WebSocket是基于TCP的全双工通讯的协议,它与EventSource有着本质上的不同.(前者基于TCP,后者依然基于HTTP)。关于WebSocket可以参考我另一篇博文WebSocket