webpack -- 热更新原理




开发环境页面热更新早已是主流,常见的需求如赛事网页推送比赛结果、网页实时展示投票或点赞数据、在线评论或弹幕、在线聊天室等,都需要借助热更新功能,才能达到实时的端对端的极致体验。

webpack-hot-middleware

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

EventSource 不是一个新鲜的技术,它早就随着H5规范提出了,正式一点应该叫Server-sent events,即SSE。鉴于传统的通过ajax轮训获取服务器信息的技术方案已经过时,我们迫切需要一个高效的节省资源的方式去获取服务器信息,一旦服务器资源有更新,能够及时地通知到客户端,从而实时地反馈到用户界面上。EventSource就是这样的技术,它本质上还是HTTP,通过response流实时推送服务器信息到客户端。
客户端新建一个EventSource对象非常简单。

    const es = new EventSource('/message');// /message是服务端支持EventSource的接口
  • 1

服务端实现/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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

我们注意到,为了避免缓存,Cache-Control 特别设置成了 no-cache,为了能够发送多个response, Connection被设置成了keep-alive.。发送数据时,请务必保证服务器推送的数据以 data:开始,以\n\n结束,否则推送将会失败(原因就不说了,这是约定的)。
以上,服务器每隔1s主动向客户端发送当前时间戳,为了接受这个信息,客户端需要监听服务器。如下:

    es.onmessage = function(e){
      console.log(e.data); // 打印服务器推送的信息
    }
  • 1
  • 2
  • 3

这里写图片描述

es可以监听任何指定类型的事件。

    es.addEventListener("####", function(e) {// 事件类型可以随你定义
      console.log('####:', e.data);
    },false)
  • 1
  • 2
  • 3

服务器发送不同类型的事件时,需要指定event字段。

    res.write('event: ####\n');
    res.write('data: 这是一个自定义的####类型事件\n');
    res.write('data: 多个data字段将被解析成一个字段\n\n');
  • 1
  • 2
  • 3

使用EventSource技术实时更新网页信息十分高效。实际使用中,我们几乎不用担心兼容性问题,主流浏览器都了支持EventSource,当然,除了掉队的IE系。对于不支持的浏览器,其PolyFill方案请参考HTML5 Cross Browser Polyfills

CORS

关于CORS配置可以参考我另一篇博文 前端跨域详解

nginx配置

既然说到了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;
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

WebSocket

WebSocket是基于TCP的全双工通讯的协议,它与EventSource有着本质上的不同.(前者基于TCP,后者依然基于HTTP)。关于WebSocket可以参考我另一篇博文WebSocket




阅读更多
文章标签: webpack 热更新
个人分类: webpack
所属专栏: vue及其生态圈学习
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭