lucifer

从零实现 vite(先导篇)

  |  

来实现一个 vite ?

基本知识

假设我们有如下两个 JS 文件。

即两个 esm 的模块, 并且 main.mjs 依赖 utils.mjs。

如上代码可以被支持 ESM 的浏览器所识别,但并不意味着其可以直接被运行。 比如我的代码依赖了 npm 包和一些相对路径,这些浏览器是无法识别的。

而 vite 则解决了这个问题。由于 vite 本质还是依赖了浏览器的特性,因此可以直接利用浏览器的诸如缓存的特点来提高性能。

除此之外, 每次修改文件,比如修改上面的 main.mjs 或者 utils.mjs 中的任意一个文件并不会导致“打包”全部文件。这是因为 vite 根本没有打包过程, 而是直接将修改过的文件热更新到浏览器的内存中。

比如,我修改了 main.js,那么就直接发送一个 http 去请求最新的 main.mjs 文件,而 utils.mjs 则可以继续使用浏览器缓存中的内容即可。

我画了一个简单的原理图给大家参考一下。

模块之间的关系如上图所示。并且这个时序指的是更新一个文件之后的更新流程

我将其分成了若干模块,它们分别是:

  • 浏览器。用于处理 ESM
  • 文件系统。用于存储源代码文件。
  • vite-server。 响应浏览器,并返回内容。这些内容主要是最新的文件系统中的文件,除此外还有注入到 client 中的代码等。
  • hrm-sever。用于根据模块的依赖关系确定应该更新的模块,并触发相应的回调函数。
  • watcher。 监听文件系统的变更,当文件内容发生变化的时候,通知 hmr-server。之后 hmr-server 再去通过 websocket 通知浏览器获取最新的模块(按需请求)。

如何确定需要更新的模块

我们可以根据 esm 的 import 关系生成一个依赖图。并将图中的所有点都放入一个哈希表中,key 可以是文件的请求路径,value 可以是模块本身,这样就可以根据请求路径在 $O(1)$ 的时间获取到指定的节点。之后我们可以遍历依赖图,并依次发起浏览器的 http 请求获取最新内容,并触发回调函数。

如下图红色的模块被更新,我们通过 $O(1)$ 时间获取到它,然后依次遍历虚线的两个模块,发起请求获取其最新模块内容,最后触发注册到这三个模块上的回调函数即可。

回调函数通过 module.hot.accept 注册,具体参考 hmr 相关文档。

一个更复杂的例子:

之后我会根据这个原理图带大家一步步实现一个 mono-vite(等西法有时间的)。


 评论


博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

本站使用 Material X 作为主题 。
载入天数...载入时分秒...