Vite源码学习(六)——DEV流程探究起步

141 阅读8分钟

前言

VITE的构建流程非常简单,因为就是封装了Rollup,加上自己内置一些插件,完成了构建的能力。

在本系列的前面的文章,我们是花了相当多的篇幅来阐述VITE的一些核心内置插件,对于一些DEV阶段的插件我们暂时还不阐述,在我们大致完成DEV流程的探究之后,最后再来研究只应用于DEV流程的内置插件。

VITE的DEV流程相对复杂,我们会花几篇文章的篇幅来向大家讲解,好了,废话就不多说了,我们还是像学习构建流程的时候的方法一样,从CLI的入口开始分析。

CLI入口

在cli.ts文件中,导入了Server的文件,VITE的DEV流程相关的内容基本上都放在Server目录下的。 image.png 然后调用了createServer这个方法,调用这个方法之前,我们事先已经拿到了配置文件的内容了。 image.png 这个createServer方法就是VITE提供的创建的devServerJS API

然后调用server实例的listen方法,如果你是自己使用VITE的JS API编写程序,你也一样需要这个操作。 image.png 最后,调用printUrls,VITE就会在命令行输出我们启动服务的信息,我们自己使用JS API要想实现这样的效果,这个步骤依然少不了。 image.png 如果还想像VITE一样,给CLI提供交互,我们还需要调用bindCLIShortcus这个方法,效果如下: image.png

创建DevServer

我们接下来需要关注VITE是如何创建DevServer的。

从上小节中我们学习到的CLI中的操作,我们将目光聚焦到内部的_createServer上。 image.png 这个方法中首先调用的resolveConfig跟之前我们在build阶段看到的那个方法是一样的,所以我们就不再关注了。

接下来的解析publicDir下的文件内容以及读取https的配置的部分我们都可以不需要关注。

我们直接来到使用connect初始化httpServer的这个位置: image.png VITE不是使用的Express来初始化的HttpServer,而是使用的一个叫做connect的包来处理配置,然后再把这些配置挂到node:http创建的Server上去,这其实是connect的用法,我第一次看的时候,整个人还有点儿懵,因为connect其实就是一个中间件,跟传统的我们理解的Express直接初始化一个Server有一些区别。

关于这个包的使用及知识点,本文不会做任何讲解,有兴趣的同学可以查看github image.png 接下来,是创建WebSocket的Server,我们在DEV的前半阶段我们对WebSocket不做重点关注,后面我们会单独开连续的文章来阐述WebSocket相关的处理逻辑。 image.png 紧接着,又使用chokidar处理了文件监听,监听的范围就是配置文件指定的root,一系列的env文件,还有vite的配置文件,还有publicDir下面的文件。 image.png 然后是初始化Env上下文,Env上下文其实很重要,在之前的插件中,VITE其实广泛的应用了这个上下文中的数据,只不过我避重就轻,没有引起大家都关注。 image.png Env初始化之后,最重要的内容就要开始来了哦,大家请注意看。 image.png 先初始化了ModuleGraph,这个类在Rollup中我们其实已经见过了,它是用来记录资源的引用关系的。我们的资源相互引用其实就像是数据结构中讲过的图。

这个位置的实例目前看起来也是为了做兼容处理,后面我们在讲VITE核心类的时候会讲真正有用的ModuleGraph

再初始化了插件的容器,即createPluginContainer,这是VITE实现DEV阶段最核心的一个部分之一,不过这个挂载在ViteDevServer实例上的PluginContainer是为了兼容(我猜测可能跟VITE的API变更或者代码重构有一定的关系),真正干活儿的其实是EnviormentPluginContainer,这个类已经迁移到了DevEnvironment上面去管理了,PluginContainer其实就是一个桥接的作用,负责调度DevEnvironment上的EnviormentPluginContainer,后面我们会花专门的篇幅来讲这个内容。

再然后,定义了一个Server对象,这个Server对象就是我们在configureServer生命周期中所拿到的那个Server。

image.png 只不过上面被Proxy包裹了一下而已。 image.png image.png 这个Server对象上的内容,我们暂时也不关注,我们在下一小节中专门开一小节来阐述这个ViteDevServer

紧接着,就是给之前的那个connect对象的实例挂载一堆中间件。 image.png 最后是覆写了node:http的listen方法,然后对外返回ViteDevServer的实例。 image.png 在这之前,我们最后再看一眼initServer方法里面完成了什么逻辑? image.png 这个Environmentlisten方法,也是在完成初始化的操作,这一小节我们也不关注,最晚会在下一小节阐述它。

到这个位置,DevServer就已经创建完成了。

ViteDevServer

现在,我们着重来关注ViteDevServerimage.png 这个server实例上,关联了几乎所有重要的东西,如图。

接着,我们再看一下server中定义了哪些方法? image.png 这个warmupRequest方法非常重要,所有客户端的请求都将经过它,进行分发,然后转换成对应的构建资源返回给客户端。

接着,这个transformIndexHtml是专门用来处理dev阶段的的html内容的,我们在之前讲的vite:build-html只会在构建阶段处理html。 image.png 然后是在listen方法中,找到一个可用的端口,创建HttpServer image.png close方法里面定义了一些资源清理的方法: image.png 最后几个方法,定义了前面我们聊到的那个能够使得VITE能够在CLI有交互的方法,输出当前devServer监听的端口信息等操作。 image.png

从请求发出到得到资源经历了怎么样的过程?

在本小节中,我将会大致向大家讲清楚这个过程,但是这些过程中所牵涉到的细节,我们将会在后文进行详细的分析。

本文的目标就是让大家能够知道从一个请求发起到得到资源经历的过程,从而使得大家可以觉得有阶段性的收获而有一些慰藉。

已知在上一节中,transformIndexHtml就是获取DEV流程中index.html内容的方法,我们来看一下这个方法的处理流程。

请求地址如我们预期: image.png 我们打印看一下得到的html字符串:

image.png image.png applyHtmlTransforms这个方法大家是不是觉得很熟悉,之前我们在讲vite:build-html这个内置插件的时候,我们就已经看到过了,它其实就是把实现了transformIndexHtml生命周期的插件拿出来进行统一的执行。

所以,本身的重点就不是这个方法了,我们得看一下transformHooks里面添加了什么内容: image.png

哦,定义了一堆的插件,并且还把用户自定义的hook也一起添加进去了。

我们就看2个插件就好,看一下htmlEnvHookdevHtmlHook

htmlEnvHook完成了环境变量的替换: image.png devHtmlHook完成了html中资源的引入的转换(跟我们在vite:build-html中聊到的transform的生命周期中的逻辑基本一致): image.png image.png 经历了以上的流程,在DEV流程中,我们发出了一个请求html,就可以得到响应了,然后这个html又会依赖其它资源,接下来,我们就看看别的后续的资源是如何请求得到的。

已知在上一节中,transformMiddleware就是资源分发的入口,我们来看一下这个方法的调用堆栈: image.png image.png 从以上的处理流程,我们已经明白了网络请求的来源,接下来我们需要看一下网络请求是怎么被映射到资源,VITE是如何调用这些插件集合进行处理的。 image.png image.pngdoTransform里面,首先调用resolveId确定当前的资源如何解析,此刻,VITE的插件集合就开始起作用了: image.png 然后在这个loadAndTransform这个方法里面,处理从磁盘加载资源(load生命周期)和对从磁盘加载的资源进行转换(transform生命周期)。 image.png image.png image.png 最后,返回处理的结果: image.png 至此,我们就简单的明白了从一个请求发出,到VITE处理并返回的全部流程。

上述流程中,我们还忽略了很多关键的知识点,比如PluginContainer,比如ModuleGraph,这些都是我们在后文中需要着重关注的。

总结

考虑到篇幅的关系,本文阐述的内容就暂时囊括这么多。

本文从CLI入口文件开始,向大家阐述了VITE DevServer的创建过程,并且向大家讲述了如何从网络请求到资源解析的过程,这个过程中我们只向大家展示了主体流程,使得大家对VITE有一个感性的认识,没有针对具体的细节进行讨论,从下一篇文章开始,我们就会开始对VITE的几个核心类进行详细的分析。

如果你仔细阅读完成了本文的话,你应该是可以根据本文的知识点回答关于VITE的常规面试题了,在DEV流程中,VITE扮演的角色就是一个boundless的资源服务器。