likes
comments
collection

浅谈前端热更新(上)

作者站长头像
站长
· 阅读数 11

浅谈前端热更新(上)

前言

在日常的前端开发中,相信很多小伙伴们应该对前端热更新又熟悉又陌生。一开始听说前端还有热更新,我表示疑惑 前端还有热更新? 一般来说热更新是手游这种比较庞大的APP等等需要热更新,可以通过加载补丁包去更新 也叫做热替换等等。那么在前端开发中热更新指的又是什么呢?

热更新(Hot Module Replacement)原理

在开发过程中 当我们文件修改了 webpack会对代码进行一个新的编译,并把托管在服务器上的项目进行一个刷新或者是局部模块重载,从而达到了自动刷新的效果,提高了开发者的效率。而在服务端和浏览器之间,什么时候需要刷新?需要全局liveReload 还是hmr 局部去动态刷新? 首先我们先捋清楚服务器和浏览器之间的关系和交互。

浅谈前端热更新(上)

通过这个图我们来简单分析下两者的交互流程
  • 通过webpack启一个默认为8080的服务,并且启一个WebSocket服务 端口为3000 然后通过客户端去打开这个8080的地址 那这个服务器上是托管了我们的项目。
  • 那客户端也就是我们托管在服务器上的项目需要去连接 刚刚启动的WebSocket 服务 这样两个就建立起了连接。
  • 两者之间建立起连接了,那么问题就解决了一半,如果文件修改了 那么这个更改会被webpackwatch 这个插件捕获到,继而发送给客户端,让客户端去进行下一步的刷新操作

简单手写热更新

  • 第一步我们首先要用webpack初始化一个项目, 因为框架一般已经帮你载入热更新啦。那么初始化项目要是有同学不懂的话可以去查阅webpack 文档哦 这里就不多做解说 温馨提示道路 webpack.docschina.org/concepts/

  • 简单初始化了项目 项目目录大概是这样 我们可以先把webpack.config.js 里面的devServer相关配置关闭

浅谈前端热更新(上)

devServer: {
    open: false,
    hot: false, // 关闭热更新
    liveReload: false, // 关闭自动刷新
    webSocketServer: false,
  },
  • 通过webpack 自己去启一个默认服务 和一个WebSocket服务 就我们上述说的8080 和 3000的端口
    // server 服务
    const Webpack = require('webpack');
    const WebpackDevServer = require('webpack-dev-server');
    const webpackConfig = require('../webpack.config.js');
    const devServerOptions = { ...webpackConfig.devServer };
    
    // webServer
    const webServer = new WebpackDevServer(devServerOptions, compiler);
    const runServer = async () => {
      console.log('Starting server...');
      await webServer.start(); // 需要手动start() 启动
    };

    runServer();
// websocket 服务
const connections = []; //存放链接对象
var ws = require("nodejs-websocket")
const server = ws.createServer(function (conn) {
    connections.push(conn);
    console.log("New connection")
    conn.on("text", function (str) {
        console.log("Received "+str)
        conn.sendText(str.toUpperCase()+"!!!")
    })
    conn.on("error", function (code) { // 链接错误 就关闭当前链接
      try {
        socket.close();
      } catch (error) {}
    });
    conn.on("close", function (code, reason) {
       const nowIndex = connections.findIndex(item => item === conn); // 链接断开了 把当前失效链接删除
       connections.splice(nowIndex, 1);
      console.log("Connection closed")
    })
}).listen(3000);

// webpack 监听
const watching = compiler.watch({
  aggregateTimeout: 300,
  poll: undefined
}, (err, stats) => { // [Stats Object](#stats-object)
  if(!err) {
    connections.forEach((item) => {
      item.sendText('[HMR] Module Update');
    })
  }
});
  • 通过webpack 成功启动了服务,那么下一步我们需要去连接这个websocket服务 那么我们可以在public下的index.html中去写JS层逻辑
const address = "ws://localhost:3000"; // websocket 服务 注意非http/ https 是ws协议
  var socket = new WebSocket(address);
  socket.onopen = function () {
    console.log("[HMR] Live reload enabled."); // 建立连接
  };

注意 在这里我们可以执行下通过node 去执行刚刚我们前面完成的两个步骤

浅谈前端热更新(上) 在这里我们可以看到8080这个端口了 然后打开这个页面可以看到控制台打印了 这说明两者之间已经建立起了连接

浅谈前端热更新(上)

  • webpack 的 watch 我们在每个连接更新都重新发送了text 那么在项目里我们收到了text 我们就可以去进行下一步的操作了
const address = "ws://localhost:3000"; // websocket 服务 注意非http/ https 是ws协议
  var socket = new WebSocket(address);
  socket.onopen = function () {
    console.log("[HMR] Live reload enabled."); // 建立连接
  };
  socket.onmessage = function (msg) {
    console.log("msg", msg); // 接收到msg
    window.location.reload(); // 全局刷新
  };

浅谈前端热更新(上) 这时候我们修改文件内容的话,返回控制台可以看到打印的msg 那么就可以实现刷新了

总结 通过建立连接以后我们大致可以知道了热更新的操作流程了 那么这个是属于热更新吗? 其实严格意义上来说刚刚上面那一步算是热刷新 在我们监听到文件修改了 直接去手动全局刷新,虽然说是提高了开发效率但是在性能和消耗上还不是最优。 是属于实现了liveReload 全局刷新,那么真正的hot 该如何实现 如何去局部刷新,局部载入新模块替换旧模块呢? 移步下一篇文章哦 通过解析webpack-dev-server源码去分析hmr 思想 (文章正在肝的过程中....)写的不足希望大佬补充

在服务器和客户端建立长链接的时候为什么选择使用websocket

两者之间保持长链接的方式其实不仅仅是只有websocket方式

轮询

轮询也算是服务器和客户端之间保持长连接的一种方式 但是!太消耗性能啦! >< 五星不推荐

EventSource

EventSource 是基于http协议 只是简单的单项通信 实现方式比 Websocket 更简单一点,并且可以自动重连接等等,而WebSocket要借助第三方库比如socket.io可以实现重连。 但是EventSource只能发送文本,而websocket支持发送二进制数据等等 单项通信适合实现单向推送播报等等 综合考虑 Websocket 更适合去建立两者之间的链接。

对这两者的区别和优缺点感兴趣的可以康康 www.jianshu.com/p/958eba34a… 比较详细哦 (>< 搬运)