Vite源码学习(二)——Vite内置插件(一)

332 阅读7分钟

前言

在上一篇文章中,我们初步学习了Vite的构建流程,当时是跳过了Vite的内置插件,只选取了一个插件进行粗略的阐述,从这篇文章开始,我们要对Vite中重要的内置插件代码进行精读。

关于内置插件篇,估计可能得需要花几篇文章来向大家阐述,大家先有一个心理预期。

从这篇文章开始,我将会大量的用到之前在Rollup阐述生命周期的相关知识点,如果你对生命周期还比较陌生的话,请查阅我之前的文章。

内置插件的加载

在这个小节中,我们需要搞清楚Vite构建流程中和DEV流程中加载插件的区别。然后,我们接下来的篇幅就主要去探索构建流程中的插件,对于DEV流程中加载的插件,后续在聊DEV逻辑的时候继续阐述。

  • 构建过程中加载的插件 image.png
  • DEV过程中加载的插件 image.png 我们对这个两个数组取一下交集,来列举一下哪些插件是Vite在构建和DEV过程中都加载的插件。
[
	"vite:watch-package-data",
	"vite:pre-alias",
	"alias",
	"vite:modulepreload-polyfill",
	"vite:resolve",
	"vite:html-inline-proxy",
	"vite:css",
	"vite:esbuild",
	"vite:json",
	"vite:wasm-helper",
	"vite:worker",
	"vite:asset",
	"vite:wasm-fallback",
	"vite:define",
	"vite:css-post",
	"vite:worker-import-meta-url",
	"vite:asset-import-meta-url",
	"vite:dynamic-import-vars",
	"vite:import-glob"
]

看了一下,大概有19个插件是两个流程都需要的。接下来,我们便开始聊这些插件中常用的插件。

还是由简入难的方式向大家阐述。

vite:json

这个插件相对比较简单,本着老太太吃柿子,捡软的捏的原则,我们就从它开始。

image.png 这个插件处理的是transform生命周期,对于Vite内部的逻辑或者一些特殊的逻辑不作处理 image.png 然后调用Rollup的插件,把JSON的内容转成一个ESM。 image.png image.png 看起来非常简单,我把Vite额外的处理忽略了,大家知道这个插件就是做一个JSON的解析就足够了,剩下的那些额外的边界逻辑处理,如果你是一个框架的设计者的话,自行查看学习即可,我们以掌握Vite的原理的角度出发,就省略这些细节了。

vite:css

从这个插件开始,就要上难度了哦,大家要跟上。

这个文件有3000多行代码,我们逐步拆开来看,先看关于vite:css插件的逻辑。 image.png 这个插件,主代码有300多行,但是调用外部逻辑还不止这么多,我们的静下心慢慢看。

buildStart生命周期中,主要是初始化缓存的逻辑+CSS预处理器的逻辑。 image.png 接下来,看一下Vite是如何处理CSS的预处理器的。 image.png Vite一共支持了scsssassstylstylusless这几种预处理器。

我在项目中主要用scss,所以本文的关注点主要就聚焦在scss上了(后文中的所有预处理器均只讨论scss),其它的处理逻辑类似,不赘述,有兴趣的读者可以自行查阅Vite的源码。

image.png 暂时先不关注process方法处理的逻辑,它一会儿是在外部被调用的,调用到的时候我们再回过头来看。

我们又需要把目光切回插件本身,在buildEnd生命周期中,释放资源,没有什么好说的。 image.png

接下来,比较重要的load生命周期开始要开始做事儿了。 image.png 这个isCSSRequest支持了各种常见的css预处理器,不是css资源不会处理到。 image.png 对于css资源且查询字符串中带?url,在load生命周期中需要完成注入逻辑,我猜测这样的话,后续将不会对带有.css?url这样的资源进行转换了,拭目以待。 image.png image.png 的确如此。

好吧,那接下来咱们就接着看在transform生命周期里面完成了什么工作。

urlReplacer看起来没有做什么我们需要特别关注的逻辑,那就不看了,主要看一下compileCSS这个方法里面完成的逻辑就好。

image.png

之前我们初始化的preprocessorWorkerController此刻应该是要开始干活儿了。

Vite默认用的是postcss,但是也支持lightningcss,所在这儿判断了一下用户选择的css后置处理器。 image.png 我们就假设使用的是postcss,就不看lightningcss的处理逻辑了。

然后Vite尝试加载postcss的配置: image.png 如果没有什么额外的工作要做的话(资源是css不需要预处理),就直接返回css的内容就完事儿了。

加载CSS预处理器

接下来,处理的是css的各种预处理器的逻辑。 image.png 这儿await等待的内容,就是之前我们看到的那个定义预处理器的process方法。 image.png image.png 好了,到这个位置,我们就可以把目光切回之前只讨论了一点儿的创建scssProcesser的逻辑了。 image.png 先看一下worker的创建逻辑,即if分支没有命中的时候,进行的逻辑: image.png 因为Vite的scss编译配置可以配置多种api,所以这儿就有多种选择,我们随便找一个来聊就好,就以modern-compiler进行阐述吧。 image.png 暂时,这个位置只是获取了worker的实例,将来我们再看worker内部的runstop是如何调用的。

好了,拿到worker之后,就要开始准备干活儿了,准备干活儿之前,需要把待加工的原材料准备一下(注入依赖和additionalData): image.png 这就是为什么我们在使用scss时,可以注入一下全局变量的原因(MagicString是一个比较方便的对字符串增删改查的库,大家不清楚的话请查看github)。 image.png 之前我们只看了创建worker,没有看worker的run方法的实现。 image.png 这个run方法现在我们可以关注了,它其实就是调用scss包暴露的编译方法完成scss源码的编译。 image.png 所以,现在回过头来,大家明白了编译结果是怎么得来的了吧! image.png image.png 经过了SCSS的预处理之后,接下来就是postcss的处理了。

加载postcss处理

如果有inline的导入,postcss需要加载postcss-import这个插件。

image.png image.png 如果是css-module语法的话,引入postcss-module插件: image.png 最后,走基本的postcss的处理: image.png 至此,CSS处理逻辑就已经完成了: image.png

小结

vite:css是一个Vite内置的处理css相关的插件,这个插件仅处理和css相关的内容(包含各种css预处理器的格式的问题)如果我们导入的是带有.css?url,Vite认为你是在处理资源,不会为你做转化。

当一个css资源命中对应的处理器时,插件会尝试去加载对应的css预处理器,如果你没有安装的话,Vite会打印出错误信息,提醒您安装。

目前Vite支持scsssassstylstylusless这几种格式的的预处理器。

当经过css预处理器处理之后,css会经过postcss处理,如果你是有内联的css,Vite会去加载postcss的postcss-import插件,如果你当前的css命中了css-module的规则,Vite还会尝试加载postcss-module插件做css-module的转换。

最后,调用用户传进来的postcss的配置(比如px2remautoprefixer),将之前得到的css内容再次转换得到最终的内容,完成构建。

可以看到,Vite的处理CSS流程是相当复杂的,但是对于用户来说的话,因为它集成了各种css的预处理器,只需要配一两行代码就可以完成编译工作,降低了使用难度。

所以说,一个好的软件产品,一定是把恶心留给自己,把方便留给用户,为Vite的开发团队点赞!!!

总结

考虑到处理css插件的内容太多,本文暂时就只阐述JSON+CSS这两个插件,我们在后续的文章中继续阐述Vite中的核心插件,未完待续......