接触了一段时间的rollup,简单的记录下。
一、简单介绍
rollup的插件是一个对象,包含了名称和一些钩子函数。
在rollup打包过程中,会根据不同的钩子函数依次执行。
1.1 两个阶段
1.构建期:这个阶段是对源文件的转化,转换好的文件没有输出,放在内存中。
大概会经历 options -->buildStart -->resolvedId -->load -->transform -->moduleParsed -->buildEnd。
如果同一个钩子被不同的插件实现,会根据插件的顺序依次执行钩子函数。
2.输出期:这个阶段主要是对转换好的文件进行拼凑整合,可能加上统一的banner,footer等,生成chunk。然后再bundle输出到硬盘上。
大概会经过 outputOptions -->renderStart -->renderChunk -->genderateBundle -->writeBundle -->closeBundle.
列出了大部分,如果有import引用的话可能会循环执行某些步骤,这里不展开。
其实还有一个watch阶段,它可以在任一阶段触发和结束。它有两个钩子函数: watchChange,closeWatcher。
当你使用 rollup --watch 指令或者在配置文件配有watch: true的属性时,会在文件变化时重新构建。
有点HMR的意思,估计热更新也用到了这一特性。
1.2 钩子执行时机
rollup钩子根据执行时机又分为同步,异步;并行,串行。
同步,异步区别就是结果会不会返回promise。
并行(parallel):如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 async
,则此类后续钩子将并行运行,而不是等待当前钩子。
串行(sequential):如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 async
,则此类后续钩子将等待当前钩子解决后再运行。
并行串行主要是对异步有了进一步的区分。
1.3 相同钩子函数的顺序
钩子函数可以是一个函数,像这样load
function myPlugin1(){
name: 'test-plugin1',
// 默认顺序
load(id){
// xxx
}
}
也可以是一个load对象
function myPlugin2(){
name: 'test-plugin2',
load: {
// 可以配置更丰富的选项
order: 'pre',
// 具体的执行函数变到这儿
handler(id){
// xxx
}
}
}
配置成一个对象可以更灵活的使用插件。
这里 order 属性分为 pre,post。pre先执行,默认配置的再执行,post后执行。
二、插件使用
选取了 load 和 transform 两个钩子函数进行测试。
2.1 load
先看下load钩子的具体作用:
即传入一个文件名(id),看对该文件做不做修改。这会影响到下一步 transform 拿到的参数。
比如可以在 load 阶段生成js代码的ast,返回该ast。
这里以vite插件为例,因为vite插件保留了rollup插件的一些钩子函数,很类似。
在@vitejs/plugin-vue中,我们可以看到该插件在load阶段,对sfc文件的不同阶段,输出不同的结果。最终返回了code和sourcemap。
这里我在自己的项目中做个简单测试。这里当load钩子return null时,说明交给下一个load钩子继续执行,返回值不为null,对于这个文件后面相同的load钩子就不执行了。(这里不是绝对的,对于load,transform等钩子来说,一旦成功返回非null值,后面的就不执行了。对于renderChunk等钩子,即使前面的钩子返回结果,后面的钩子也有可能改变这次的结果,具体要看rollup的官方文档)
可以看到打印了所有的用到的文件名称。从我们的入口文件开始(vite默认项目根目录下的index.html)。这里我们就可以根据不同的文件类型,做出不同的处理
2.2 transform
这个阶段会真正的处理文件的内容。比如对sfc文件,template进行加工,转成js函数。对css内容,就转成引用。
在@vitejs/plugin-vue中,我们可以看到该插件在transform阶段,会分别对template、style、script文件进行处理。也是在这一步,template内容会转化成我们熟知的render函数。
以我的项目为例,在transform中我打印app.vue的内容。
可以看到,因为我的 testPlugin 在 vue插件后面执行,我这里拿到的就是经过vue插件转换后的文件,能看到此时的 _sfc_main 函数。
当我修改了transform的order属性时:(testPlugin报错是ts的,先忽略)
可以看到虽然testPlugin排在vue插件之后,却是在vue插件之前执行的,因为我们拿到的是还没有被转化的文件内容。
三、小结
我们简单的测试了下rollup插件的几个钩子。在rollup中所有的插件都是顺序执行,不同的钩子在不同的生命周期中发挥作用。对同一种钩子不同的插件可以写不同的处理,执行的顺序要看他们的排列顺序和order。另外rollup内置了一些方法,可以在钩子中获取到当前文件的状态,meta数据等,比如getModuleInfo 在构建期间,此对象表示有关模块的当前可用信息。这里不展开。
我只是粗略的了解rollup的插件机制,大家想深入的话,可以看看其他的著名插件,结合rollup官网,定能有所收获。
后续有其他深入理解,会再更新rollup相关。