[太闲了]自己实现一个网页热加载服务

611 阅读4分钟

寒假赋闲在家,没啥业余爱好,闲暇之余喜欢学学好看的css效果,然每次更改完样式都需要刷新浏览器,于是去寻热加载工具,未果,自己写了一个🚀

当然,您也可以用react提供的cra等类似工具实现此需求,但是我因为就想用简单一点的方式编辑css效果,所以不喜勿喷哈

下文的网页热加载均指此需求

注:如果您是想用于生产环境,那就不用往下看,这里纯粹图一乐哈

思路

网页热加载大概分为一下步骤

  1. 开启一个端口实现http服务

  2. server监听文件夹

  3. 文件夹有变动的时候通过SSE发送给client

  4. client刷新重新获取页面

所以我们要用到的工具有

  • Node包:httpfs
  • WebAPIs:localtion.reload()EventSource

开始

提供http服务

首先,我们开启一个端口来提供http服务

const http = require('http');

const server = http.createServer((req, res) => {
});

server.listen(8080, () => {
    console.log(`server running on http://localhost:8080/🚀...`)
});

node运行一下,这个时候服务已经开启啦http://localhost:8080/

接下来要提供静态资源的访问服务,这样我们才能看到我们的网页呀

这里我们使用fs.readFile读取文件并响应http请求

注意⚠⚠更换自己的静态资源文件夹路径

const fs = require('fs');
//别忘了改成自己监听的文件夹
fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
  if (err) {
    res.writeHead(404);
    res.end("File Not Found");
    return;
  }
  res.writeHead(200);
  res.end(data);
});

加到我们的http服务中就是这样

这里我们默认访问根目录返回index.html,用一个函数封装一下给其他静态资源复用

const http = require("http");
const fs = require("fs");
const getStaticResource = (url, res) => {
  fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
    if (err) {
      res.writeHead(404);
      res.end("File Not Found");
      return;
    }
    res.writeHead(200);
    res.end(data);
  });
};
const server = http.createServer((req, res) => {
  const { headers, url, method } = req;
  if (url.toLowerCase() === "/" && method.toLowerCase() === "get") {
    getStaticResource("/index.html", res);
    return;
  }
  if (method.toLowerCase() === "get") getStaticResource(url, res);
});

server.listen(8080, () => {
  console.log(`server running on http://localhost:8080/🚀...`);
});

这样的话,我们在相应的文件夹下建立index.html就能在http://localhost:8080/

看到我们的网页啦

使用SSE和文件监控实现热加载

接下来就是实现热加载啦

首先我们的网页中一定要默认链接一个JS文件,里面放入我们SSE需要的浏览器端API

//sse.js
const events = new EventSource("/events"); //监听我们http服务中的/events路由
events.onmessage = (event) => {
  location.reload();
};

设定好监听的路由,只需要设置收到信息的时候刷新页面就好啦

不要忘记链接到HTML中

<script src="sse.js"></script>

接下来实现我们server端的服务

首先是文件监听APIfs.watch,我们要配合这个来给浏览器端发送消息

老样子,写一个函数包装一下,并且应该是立即执行的(因为我们想打开端口就用,如果有业务逻辑则不要学我)

(function watchFile() {
  fs.watch("./public", { encoding: "buffer" }, () => {
    client.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Connection": "keep-alive",
    "Cache-Control": "no-cache"
  });//headers标准
    client.end("data: reflush\n\n");//别忘了data:和\n\n
  });
})();

你可能发现,我这里多了一个client变量,不过别急我待会解释,要注意SSE的通信标准(代码中注释)

由于SSE的本质是不断向server端发送GET请求保持连接一直未中断

而我们只需要文件更改的时候发送信息给浏览器端,所以我这样安排了响应

if (url.toLowerCase() === "/events" && method.toLowerCase() === "get") {
    client = res;
    return;
  }

每次响应我们只需要保存“指针”,以便于文件更改的时候能找到

其实这里全局变量有些不优雅,但是别计较太多

到这里我们的功能已经实现完全啦

全代码

const http = require("http");
const fs = require("fs");
let client;

const server = http.createServer((req, res) => {
  const { headers, url, method } = req;
  if (url.toLowerCase() === "/" && method.toLowerCase() === "get") {
    getStaticResource("/index.html", res);
    return;
  }
  if (url.toLowerCase() === "/events" && method.toLowerCase() === "get") {
    client = res;
    return;
  }
  if (method.toLowerCase() === "get") getStaticResource(url, res);
});

server.listen(8080, () => {
  console.log(`server running on http://localhost:8080/🚀...`);
});

(function watchFile() {
  fs.watch("./public", { encoding: "buffer" }, () => {
    client.writeHead(200, {
    "Content-Type": "text/event-stream",
    "Connection": "keep-alive",
    "Cache-Control": "no-cache"
  });//headers标准
    client.end("data: reflush\n\n");//别忘了data和/n/n
  });
})();

const getStaticResource = (url, res) => {
  fs.readFile("/Users/shwei/code/livereload/public" + url, (err, data) => {
    if (err) {
      res.writeHead(404);
      res.end("File Not Found");
      return;
    }
    res.writeHead(200);
    res.end(data);
  });
};

打开你的代码编辑器和http://localhost:8080/

尽情享受吧!