Vite(rollup 插件机制)
比如路径别名(alias) 、全局变量注入和代码压缩等等。
Rollup 整体构建阶段
// Build 阶段
const bundle = await rollup.rollup(inputOptions);
// Output 阶段
await Promise.all(outputOptions.map(bundle.write));
// 构建结束
await bundle.close();write和generate方法唯一的区别在于前者打包完产物会写入磁盘,而后者不会)。 主要就是 build 和 output 阶段 对于一次完整的构建过程而言, Rollup 会先进入到 Build 阶段,解析各模块的内容及依赖关系,然后进入Output阶段,完成打包及输出的过程
拆解插件工作流
Build&Output 阶段
根据这两个构建阶段分为两类: Build Hook 与 Output Hook。
Build Hook即在Build阶段执行的钩子函数,在这个阶段主要进行模块代码的转换、AST 解析以及模块依赖的解析,那么这个阶段的 Hook 对于代码的操作粒度一般为模块级别,也就是单文件级别。Ouput Hook(官方称为Output Generation Hook),则主要进行代码的打包,对于代码而言,操作粒度一般为chunk级别(一个 chunk 通常指很多文件打包到一起的产物)。 根据不同的 Hook 执行方式也会有不同的分类,主要包括Async、Sync、Parallel、Squential、First这五种
1. Async & Sync
异步和同步执行
2. Parallel
并行
3. Sequential
串行
4. First
如果有多个插件实现了这个 Hook,那么 Hook 将依次运行,直到返回一个非 null 或非 undefined 的值为止。比较典型的 Hook 是 `resolveId`,一旦有插件的 resolveId 返回了一个路径,将停止执行后续插件的 resolveId 逻辑。
Build 阶段工作流
首先经历
options钩子进行配置的转换,得到处理后的配置对象。随之 Rollup 会调用
buildStart钩子,正式开始构建流程。Rollup 先进入到
resolveId钩子中解析文件路径。(从input配置指定的入口文件开始)。Rollup 通过调用
load钩子加载模块内容。紧接着 Rollup 执行所有的
transform钩子来对模块内容进行进行自定义的转换,比如 babel 转译。现在 Rollup 拿到最后的模块内容,进行 AST 分析,得到所有的 import 内容,调用 moduleParsed 钩子:
- 6.1 如果是普通的 import,则执行
resolveId钩子,继续回到步骤3。 - 6.2 如果是动态 import,则执行
resolveDynamicImport钩子解析路径,如果解析成功,则回到步骤4加载模块,否则回到步骤3通过resolveId解析路径。
- 6.1 如果是普通的 import,则执行
直到所有的 import 都解析完毕,Rollup 执行
buildEnd钩子,Build 阶段结束。
当然,在 Rollup 解析路径的时候,即执行resolveId或者resolveDynamicImport的时候,有些路径可能会被标记为external(翻译为排除),也就是说不参加 Rollup 打包过程,这个时候就不会进行load、transform等等后续的处理了。
在流程图最上面,不知道大家有没有注意到watchChange和closeWatcher这两个 Hook,这里其实是对应了 rollup 的watch模式。当你使用 rollup --watch 指令或者在配置文件配有watch: true的属性时,代表开启了 Rollup 的watch打包模式,这个时候 Rollup 内部会初始化一个 watcher 对象,当文件内容发生变化路径解析: resolveId时,watcher 对象会自动触发watchChange钩子执行并对项目进行重新构建。在当前打包过程结束时,Rollup 会自动清除 watcher 对象调用closeWacher钩子。
Output 阶段工作流
常用 Hook 实战
https://github.com/RunningLiLi/vite_learn/tree/master/packages/rollup