前言
在上一篇文章中,我们初步学习了Vite的构建流程,当时是跳过了Vite的内置插件,只选取了一个插件进行粗略的阐述,从这篇文章开始,我们要对Vite中重要的内置插件代码进行精读。
关于内置插件篇,估计可能得需要花几篇文章来向大家阐述,大家先有一个心理预期。
从这篇文章开始,我将会大量的用到之前在Rollup阐述生命周期的相关知识点,如果你对生命周期还比较陌生的话,请查阅我之前的文章。
内置插件的加载
在这个小节中,我们需要搞清楚Vite构建流程中和DEV流程中加载插件的区别。然后,我们接下来的篇幅就主要去探索构建流程中的插件,对于DEV流程中加载的插件,后续在聊DEV逻辑的时候继续阐述。
- 构建过程中加载的插件
- DEV过程中加载的插件
我们对这个两个数组取一下交集,来列举一下哪些插件是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
这个插件相对比较简单,本着老太太吃柿子,捡软的捏的原则,我们就从它开始。
这个插件处理的是
transform生命周期,对于Vite内部的逻辑或者一些特殊的逻辑不作处理
然后调用Rollup的插件,把JSON的内容转成一个ESM。
看起来非常简单,我把Vite额外的处理忽略了,大家知道这个插件就是做一个JSON的解析就足够了,剩下的那些额外的边界逻辑处理,如果你是一个框架的设计者的话,自行查看学习即可,我们以掌握Vite的原理的角度出发,就省略这些细节了。
vite:css
从这个插件开始,就要上难度了哦,大家要跟上。
这个文件有3000多行代码,我们逐步拆开来看,先看关于vite:css插件的逻辑。
这个插件,主代码有300多行,但是调用外部逻辑还不止这么多,我们的静下心慢慢看。
在buildStart生命周期中,主要是初始化缓存的逻辑+CSS预处理器的逻辑。
接下来,看一下Vite是如何处理CSS的预处理器的。
Vite一共支持了
scss,sass,styl,stylus和less这几种预处理器。
我在项目中主要用scss,所以本文的关注点主要就聚焦在scss上了(后文中的所有预处理器均只讨论scss),其它的处理逻辑类似,不赘述,有兴趣的读者可以自行查阅Vite的源码。
暂时先不关注
process方法处理的逻辑,它一会儿是在外部被调用的,调用到的时候我们再回过头来看。
我们又需要把目光切回插件本身,在buildEnd生命周期中,释放资源,没有什么好说的。
接下来,比较重要的load生命周期开始要开始做事儿了。
这个
isCSSRequest支持了各种常见的css预处理器,不是css资源不会处理到。
对于
css资源且查询字符串中带?url,在load生命周期中需要完成注入逻辑,我猜测这样的话,后续将不会对带有.css?url这样的资源进行转换了,拭目以待。
的确如此。
好吧,那接下来咱们就接着看在transform生命周期里面完成了什么工作。
urlReplacer看起来没有做什么我们需要特别关注的逻辑,那就不看了,主要看一下compileCSS这个方法里面完成的逻辑就好。
之前我们初始化的preprocessorWorkerController此刻应该是要开始干活儿了。
Vite默认用的是postcss,但是也支持lightningcss,所在这儿判断了一下用户选择的css后置处理器。
我们就假设使用的是
postcss,就不看lightningcss的处理逻辑了。
然后Vite尝试加载postcss的配置:
如果没有什么额外的工作要做的话(资源是css不需要预处理),就直接返回css的内容就完事儿了。
加载CSS预处理器
接下来,处理的是css的各种预处理器的逻辑。
这儿
await等待的内容,就是之前我们看到的那个定义预处理器的process方法。
好了,到这个位置,我们就可以把目光切回之前只讨论了一点儿的创建
scssProcesser的逻辑了。
先看一下worker的创建逻辑,即
if分支没有命中的时候,进行的逻辑:
因为Vite的scss编译配置可以配置多种
api,所以这儿就有多种选择,我们随便找一个来聊就好,就以modern-compiler进行阐述吧。
暂时,这个位置只是获取了worker的实例,将来我们再看worker内部的
run和stop是如何调用的。
好了,拿到worker之后,就要开始准备干活儿了,准备干活儿之前,需要把待加工的原材料准备一下(注入依赖和additionalData):
这就是为什么我们在使用scss时,可以注入一下全局变量的原因(
MagicString是一个比较方便的对字符串增删改查的库,大家不清楚的话请查看github)。
之前我们只看了创建worker,没有看worker的
run方法的实现。
这个run方法现在我们可以关注了,它其实就是调用
scss包暴露的编译方法完成scss源码的编译。
所以,现在回过头来,大家明白了编译结果是怎么得来的了吧!
经过了SCSS的预处理之后,接下来就是postcss的处理了。
加载postcss处理
如果有inline的导入,postcss需要加载postcss-import这个插件。
如果是css-module语法的话,引入
postcss-module插件:
最后,走基本的postcss的处理:
至此,CSS处理逻辑就已经完成了:
小结
vite:css是一个Vite内置的处理css相关的插件,这个插件仅处理和css相关的内容(包含各种css预处理器的格式的问题)如果我们导入的是带有.css?url,Vite认为你是在处理资源,不会为你做转化。
当一个css资源命中对应的处理器时,插件会尝试去加载对应的css预处理器,如果你没有安装的话,Vite会打印出错误信息,提醒您安装。
目前Vite支持scss,sass,styl,stylus,less这几种格式的的预处理器。
当经过css预处理器处理之后,css会经过postcss处理,如果你是有内联的css,Vite会去加载postcss的postcss-import插件,如果你当前的css命中了css-module的规则,Vite还会尝试加载postcss-module插件做css-module的转换。
最后,调用用户传进来的postcss的配置(比如px2rem,autoprefixer),将之前得到的css内容再次转换得到最终的内容,完成构建。
可以看到,Vite的处理CSS流程是相当复杂的,但是对于用户来说的话,因为它集成了各种css的预处理器,只需要配一两行代码就可以完成编译工作,降低了使用难度。
所以说,一个好的软件产品,一定是把恶心留给自己,把方便留给用户,为Vite的开发团队点赞!!!
总结
考虑到处理css插件的内容太多,本文暂时就只阐述JSON+CSS这两个插件,我们在后续的文章中继续阐述Vite中的核心插件,未完待续......