5

从零实现 vite(先导篇)

 3 years ago
source link: https://lucifer.ren/blog/2021/07/03/mono-vite/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
从零实现 vite(先导篇)

来实现一个 vite ?

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

008i3skNly1gs3u43i4j0j31750u0771.jpg

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

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

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

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

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

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

008i3skNly1gs3ueacuygj31of0u0afq.jpg

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

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

  • 浏览器。用于处理 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 相关文档。

008i3skNly1gs3uouzbrcj311w0o2gns.jpg

一个更复杂的例子:

008i3skNly1gs3v01nwd4j30zw0rm40c.jpg

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK