简单实现 Vite 热更新

热更新(HRM Hot Module Reload)

热更新(hmr)全称 Hot Module Reload,常常在构建工具里面出现。

在我们开发时候修改代码后页面会立即自动更新。

这是怎么做到的呢?

现在我们通过一行一行代码来动手操作,构建一个最简单的热更新。

问题是我们怎么做到文件变动到页面自动响应更新呢?

首先我们把整个过程分成3步:

  1. 监听文件变动
  2. 读取文件内容
  3. 通知浏览器更新页面

写出伪代码:

// server

watch(file); // 1
fileContent = readFile(file) // 2
send(fileContent); // 3

实现

第一步:监听文件变动

nodejs 里面有个fs.watch api。它可以监听文件变动。

(fs.FSWatcher) fs.watch(filename[, options][, listener])

node原生的 watch api,存在些许兼容性问题。 这个时候最好去找些库来实现.
使用 chokidar 来实现文件的监听

const chokidar = require('chokidar');
// One-liner for current directory
chokidar.watch('demo/chokidar').on('all', (event, path) => {
    console.log(path);
});

请添加图片描述
成功监听到了文件的变动,但是发现启动服务起后,就直接打印路径。 现在我们只想要文件变更时候触发事件。把代码改成这样 all 替换成 change 去掉 event 参数.

const chokidar = require('chokidar');
// One-liner for current directory
chokidar.watch('demo/chokidar').on('change', (path) => {
    console.log(path);
});

重启服务器后
请添加图片描述
现在监听文件变动没问题了。

第二步:读取文件内容

这个比较简单,因为是在服务端直接读取文件,我们就直接用 nodejs 中的 fs.readFileSync 来实现

const chokidar = require('chokidar');
const fs = require('fs');
const path = require('path');

chokidar.watch('demo/ws-chokidar').on('change', (relativePath) => {
    const filePath = path.resolve(__dirname, '../../', relativePath);
    const data = fs.readFileSync(filePath, 'utf-8');
    console.log(data, relativePath);

    ws.send(data);
});

由于监听到的 path 参数是相对路径,而readFileSync需要读取的绝对文件路径,使用 path 进行库进行拼接。现在看看效果请添加图片描述
当我们随意修改 change.html 内容时,控制台输出了我们的文件内容和路径。

第三步: 通知浏览器更新页面

我们知道,通过 AJAXhttp 协议只能客户端发请求到服务器,接受响应。
那怎么让服务器主动通知客户端更新了哪些文件了?
答案就是 WebSocket。 虽然 WebSocket 在浏览器和 node 都有api 支持,但是解密 socket 的数据还是挺麻烦。 这里我们需要用到 WebSocket 的库。

一种是 socket.io,一种是 ws。这里只用 ws 作为例子。

我们先写一个前端页面。简单构造一个 socket 发送和监听器。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>socket</title>
  </head>
  <body>
    <button class="button">触发</button>
    <h2>修改整个文件会被监听并实时改变:</h2>
    <div class="response-data"></div>

    <script>
      var oButtons = document.getElementsByClassName("button");
      var oResponseDatas = document.getElementsByClassName("response-data");
      var oResponseData = oResponseDatas.length && oResponseDatas[0];

      if (oButtons.length) {
        oButtons[0].addEventListener("click", onClick, false);
      }

      function onClick() {
        var socket = new WebSocket("ws://localhost:3000");
        socket.open = function () {
          socket.send("12dd");
        };
        socket.onmessage = function (event) {
          // console.log(event.data);
          oResponseData.innerHTML = event.data;
        };

        socket.onclose = function () {
          console.log("closed");
        };
      }
    </script>
  </body>
</html>

每次接收到服务端数据,更新 data 后面的内容。

然后我们使用ws给服务器端构建发送和接收器:

const chokidar = require("chokidar");
const WebSocket = require("ws");
const fs = require("fs");
const path = require("path");

const wss = new WebSocket.Server({ port: 3000 });

wss.on("connection", function connection(ws) {
  ws.on("message", function incoming(message) {
    console.log("received: %s", message);
  });

  chokidar.watch("./ws-chokidar").on("change", (relativePath) => {//监听文件变化
    const filePath = path.resolve(__dirname, "", relativePath);
    const data = fs.readFileSync(filePath, "utf-8");
    // console.log(data, relativePath);
    ws.send(data);//把文件内容返回到浏览器
  });
});

总结:

vite对webpack没有任何依赖, hmr, socket是它自己用ws实现的。

全部代码

你可能感兴趣的:(vite,vite模块热更新简单原理,模块热更新原理,模块热更新简单实现)