从0开始写一个简单的vite hmr 插件( 二 )

2.5 如何让typescript支持导入这个模块?回到之前,我们不是说vite插件中transform能够丰富资源的导入,
但是这不代表typescript就认可,不认可依然不能提供完备的补全和检查,
所以为了让typescript彻底服气,就需要在vite-env.d.ts中写一段模块解析的配置
// vite-env.d.tsdeclare module '*.todo' {export const data: string;export function parser(content: string);}这里定义了一个模块,并导出了两个成员
一个叫data, 是string类型的资源
一个叫parser,是个解析函数(稍后会介绍)
这样写了之后,typescript就会默认我们能够导入.todo 后缀的文件,并且这里面有两个成员,一个是data,一个是parser 。
3. todo插件编写O 吃饭X 喝水O 跑步五公里这样一种文本,O表示未完成 , X表示完成,后面表示当前todo的信息
3.1 todo插件在plugins中创建一个todoParser.ts
export default function todoParser(): Plugin {let todoFileRegex = /\.(todo)$/;// 解析.todo 的正则return {name: "todo-parser",transformIndexHtml(html) {return html.replace(/<title>(.*?)<\/title>/, '<title>TODO Parser</title>');},async transform(src, id) {// module injectconsole.log(id);// 看看当前文件是否通过了正则,如果通过了,就执行if (todoFileRegex.test(id)) {return {// 这里的parser是解析器,稍后会说code: `export let data = "https://www.huyubaike.com/biancheng/${parser(src)}"export ${parser}`};}}}}相信阅读了前面有关vite插件的介绍应该不难理解
3.2 parser为了能够解析.todo文件,并且输出我们希望的内容,
还需要提供解析一个解析器来解析 。
// todoParser.tsfunction parser(src: string) {// 解析const lines = src.split('\n');let todoList = "";let finishRegex = /^X/;let readyRegex = /^O/;let content = /\s(.*)$/let randomId: string;for (let line of lines) {randomId = Math.random().toString(32).slice(2);let html: string;if (finishRegex.test(line)) {console.log(line);html = `<li><input type='checkbox' checked id='${randomId}'/><label for='${randomId}'>${line.trim().match(content)![1]}</li>`console.log("通过",html);} else if (readyRegex.test(line)) {html = `<li><input type='checkbox' id='${randomId}'/><label for='${randomId}'>${line.trim().match(content)![1]}</li>`console.log("拒绝",html);}todoList += html!;}return todoList;}我们这里通过正则获取了每一行数据中表示状态的 OX, 以及其内容,并且封装为一组checkbox
这些文本信息可以直接插入html以显示其内容
3.3 插件的装载import { defineConfig } from "vite";import todoParser from './plugins/todoParser';export default defineConfig({plugins: [todoParser()],assetsInclude: ["src/**/*.todo"]})回到vite.config.ts中,在plugins数组内部直接执行todoParser(),实现插件的装载
在main.ts 中接收这个导入的资源 , 并且赋值到document中
import { data } from './assets/journey.todo'import './style.css'; // 样式,这里消除了li的一般样式 list-style: noneconsole.log(data);document.body.innerHTML = data;

从0开始写一个简单的vite hmr 插件

文章插图
  • 上面的预览图可以发现我们实现了功能,但是每次一写完 , 整个页面就会全部刷新,这可不太好,所以还需要HMR
4. HMR 实现
从0开始写一个简单的vite hmr 插件

文章插图
注意,vite中server和client是可以相互通信!这里只需要server向client发送消息
4.1 server 发送vite服务器实例的获取有很多种方法:
  1. 直接通过vite钩子 configureServer(server) {}获取
    一般用来给vite服务器添加中间件
  2. 通过处理更新的钩子获取 handleHotUpdate({file, server, modules}){}
这里我们要实现的是热更新,所以采用handleHotUpdate就可以了,在模块更新的时候,就会触发这个函数,通过server向client发送更新的消息,以及更新的数据 , 然后让浏览器在未刷新的情况下直接更新
async handleHotUpdate({ server, file, modules }) {let fileData = https://www.huyubaike.com/biancheng/await fs.readFile(modules[0].id as string);server.ws.send({type:'custom',event: 'special-update', // 事件名data: {msg: "Update from server",updateVal: fileData.toString()}})console.log(`${file} should be updated`);return [];}通过node的fs模块读取到了文本的数据
随后通过server.ws.send()向client发送的数据,其中更新之后的数据存放在data.updateVal中
4.2 client 获取在vite中,模块热更新以事件的形式抛出,具体来说是

推荐阅读