Skip to content

⚠️ Important Notice

This post was last updated on: which was . Please pay attention to its timelines.

Rollup的插件机制

更多参考官方文档

Rollup构建过程

会经历两个阶段build以及output

  • build阶段:对应api就是rollup.rollup()。会返回一个bundle对象,对象中存储了文件的内容以及依赖关系,但是并没有对文件进行打包。
  • output阶段:对应api就是bundle.write/generate。执行该api才会对文件进行打包

Rollup插件在这两个阶段的表现是不同的

Rollup插件的类型

Rollup构建分成了两个阶段,每个阶段都有插件hook在执行,所以按照这个来分类,可以将插件分为两种:Build Hook以及Output Hook

另外还能根据Hook的执行方式来划分:Async\Sync\Parallel\Sequential\First

  • Async、Sync

分别表示异步hook和同步hook,顾名思义,同步hook中是不能有异步逻辑的。

如果是Async钩子应该返回一个Promise。

  • Parallel并行

如果有多个插件实现该类型hook,那么会按照插件的顺序来运行。

如果该hook还是Async的话,会并行运行。

  • Sequential串行

如果有多个插件实现该类型hook,那么会按照插件的顺序来运行。

如果该hook是Async,会等待该hook执行完成,再运行下一个插件的该hook。

  • First

如果有多个插件实现该类型hook,那么这些不同插件中的相同hook会按顺序运行,直到某一个hook返回一个非null非undefined的值

对象类型的hook

rollup插件的hook除了是一个函数,还能用一个对象来表示

js
function testPlugin() {
  return {
    name: 'test-plugin',
    resolveId: {
      order: 'pre',
      handler(source) {
        // 逻辑
      }
    }
  }
}

当是一个对象的时候,必须要有一个handler函数来实现具体的逻辑。通过对象来实现hook,可以添加一些可选属性,来更精细的控制hook的执行:

  • order:pre | post | null

该属性来控制hook的执行顺序。如果有多个插件实现该hook,会按照order的顺序来执行这些插件

  • pre是先运行
  • post是最后运行
  • null是在用户指定的位置运行。

多个相同的order,会按照用户指定的顺序来排序

  • sequential: boolean

该属性应用于parallelhook,比如有abcde五个插件,都实现了相同的parallel并行hook,但是c插件中的hook加了个sequential: true。那么Rollup就会先并行运行a和b,然后单独运行c,最后并行运行d和e

具体的Rollup hook

Rollup提供的hook很多,这里只介绍几个常用的,需要的话去官方文档查阅即可

Rollup打包有两个阶段build和output,每个阶段都有很多hook执行。

Build阶段大致流程

  1. 首先会触发options读取配置,然后执行buildStart钩子,开始构建
  2. 然后进入resolveId钩子开始解析文件路径
  3. 然后执行load钩子加载文件内容
  4. 接着执行transform钩子对文件内容进行转换,经过转换之后已经可以拿到转换后的文件内容
  5. 随后执行moduleParsed钩子进行AST分析,判断import内容
    • 如果动态import,会执行resolveDynamicImport钩子解析路径
      • 解析成功,回到load
      • 解析不成功,回到resolveId钩子。
  6. 所有的import都解析完成之后,会执行buildEnd表示Build阶段结束

在解析路径时(resolveIdresolveDynamicImport),路径可能会被标记为external,表示不打包,会直接跳过后续的load、transform等处理。

还有watch模式的两个hook:watchChangecloseWatcher。 当配置了rollup --watch的时候,会先初始化一个watcher对象,当文件内容变化时watcher对象自动触发watchChange钩子,并对项目进行重新构建,当前打包过程结束之后,Rollup会清除watcher对象并且调用closeWatcher钩子。

可以看到该阶段主要工作是解析路径、加载文件、转换文件等工作,并没有对文件进行打包处理

Output阶段大致流程

该阶段在API的表现就是调用了bundle.generate/bundle.write

  1. 首先会执行所有插件的outputOptions钩子,对传入的 output配置进行解析转换
  2. 然后并发执行renderStart钩子,进入打包阶段
  3. 接着并发执行banner、footer、intro、outro这四个钩子,作用就是往产物的固定位置插入(头部和尾部)一些自定义内容
  4. 随后还会对每一个import进行扫描,针对动态import执行renderDynamicImport钩子,自定义动态import的内容
  5. 解析完内容之后,对每个要生成的chunk执行augmentChunkHash钩子,用来判断是否需要更改chunk的hash
  6. 然后还会判断有没有import.meta语句,针对import.meta.url会调用resolveFileUrl钩子来自定义url的解析逻辑;
  7. 对于其他的import.mete属性会调用resolveImportMeta来自定义解析逻辑。
  8. 接着就会生成所有的chunk内容,每一个都会依次调用renderChunk钩子来操作产物
  9. 然后就会调用generateBundle钩子,该钩子的入参就已经包含了所有的打包产物信息(chunkasset等),在这个钩子里可以删除一些chunkasset
    • 如果是通过bundle.write进入的output阶段,那么还是将产物存储到磁盘中,并且触发writeBundle钩子。至此打包其实就已经完成了,但是需要手动bundle.close()才会触发closeBundle钩子,之后打包才真正结束。

该阶段的工作主要是对产物进行处理,包括插入一些自定义内容、修改产物的hash、自定义url解析等。

当在任何阶段出现错误,都会触发renderError钩子,然后执行closeBundle钩子结束打包。

上一次更新: