原来rollup这么简单之插件篇
大家好,我是小雨小雨,致力于分享有趣的、实用的技术文章。
内容分为翻译和原创,如果有问题,欢迎随时评论或私信,希望和大家一起进步。
大家的支持是我创作的动力。
1|0计划
rollup系列打算一章一章的放出,内容更精简更专一更易于理解
这是rollup系列的最后一篇文章,以下是所有文章链接。
- rollup.rollup
- rollup.generate + rollup.write
- rollup.watch
- tree shaking
- plugins <==== 当前文章
2|0TL;DR
rollup的插件和其他大型框架大同小异,都是提供统一的标准接口,通过约定大于配置定义公共配置,注入当前构建结果相关的属性与方法,供开发者进行增删改查操作。为稳定可持续增长提供了强而有力的铺垫!
但不想webpack区分loader和plugin,rollup的plugin既可以担任loader的角色,也可以胜任传统plugin的角色。rollup提供的钩子函数是核心,比如load、transform对chunk进行解析更改,resolveFileUrl可以对加载模块进行合法解析,options对配置进行动态更新等等~
3|0注意点
所有的注释都在这里,可自行阅读
!!!提示 => 标有TODO为具体实现细节,会视情况分析。
!!!注意 => 每一个子标题都是父标题(函数)内部实现
!!!强调 => rollup中模块(文件)的id就是文件地址,所以类似resolveID这种就是解析文件地址的意思,我们可以返回我们想返回的文件id(也就是地址,相对路径、决定路径)来让rollup加载
rollup是一个核心,只做最基础的事情,比如提供默认模块(文件)加载机制, 比如打包成不同风格的内容,我们的插件中提供了加载文件路径,解析文件内容(处理ts,sass等)等操作,是一种插拔式的设计,和webpack类似
插拔式是一种非常灵活且可长期迭代更新的设计,这也是一个中大型框架的核心,人多力量大嘛~
4|0主要通用模块以及含义
- Graph: 全局唯一的图,包含入口以及各种依赖的相互关系,操作方法,缓存等。是rollup的核心
- PathTracker: 引用(调用)追踪器
- PluginDriver: 插件驱动器,调用插件和提供插件环境上下文等
- FileEmitter: 资源操作器
- GlobalScope: 全局作用局,相对的还有局部的
- ModuleLoader: 模块加载器
- NodeBase: ast各语法(ArrayExpression、AwaitExpression等)的构造基类
5|0插件机制分析
rollup的插件其实一个普通的函数,函数返回一个对象,该对象包含一些基础属性(如name),和不同阶段的钩子函数,像这个样子:
这里是官方建议遵守的约定.
我们平常书写rollup插件的时候,最关注的就是钩子函数部分了,钩子函数的调用时机有三类:
- const chunks = rollup.rollup执行期间的Build Hooks
- chunks.generator(write)执行期间的Output Generation Hooks
- 监听文件变化并重新执行构建的rollup.watch执行期间的watchChange钩子函数
除了类别不同,rollup也提供了几种钩子函数的执行方式,每种方式都又分为同步或异步,方便内部使用:
- async: 处理promise的异步钩子,也有同步版本
- first: 如果多个插件实现了相同的钩子函数,那么会串式执行,从头到尾,但是,如果其中某个的返回值不是null也不是undefined的话,会直接终止掉后续插件。
- sequential: 如果多个插件实现了相同的钩子函数,那么会串式执行,按照使用插件的顺序从头到尾执行,如果是异步的,会等待之前处理完毕,在执行下一个插件。
- parallel: 同上,不过如果某个插件是异步的,其后的插件不会等待,而是并行执行。
文字表达比较苍白,咱们看几个实现:
- 钩子函数: hookFirst
使用场景:resolveId、resolveAssetUrl等
- 钩子函数: hookFirstSync
使用场景:resolveFileUrl、resolveImportMeta等
- 钩子函数: hookSeq
使用场景:onwrite、generateBundle等
- 钩子函数: hookParallel
使用场景:buildStart、buildEnd、renderStart等
- 钩子函数: hookReduceArg0
使用场景: outputOptions、renderChunk等
通过观察上面几种钩子函数的调用方式,我们可以发现,其内部有一个调用钩子函数的方法: runHook(Sync),该函数执行插件中提供的钩子函数。
实现很简单:
当然,并不是每个人刚开始都会使用插件,所以rollup本身也提供了几个必需的钩子函数供我们使用,在Graph实例化的时候与用户自定义插件进行concat操作:
那rollup提供了哪些必需的钩子函数呢:
过一眼发现都是最基本处理路径解析内容的钩子函数。
不仅如此,rollup给钩子函数注入了context,也就是上下文环境,用来方便对chunks和其他构建信息进行增删改查。
文档中也写得很清楚,比如:
- 使用this.parse,调用rollup内部中的acron实例解析出ast
- 使用this.emitFile来增加产出的文件,看这个例子.
我们通过transform操作来简单看下,之前对ast进行transform的时候,调用了transform钩子:
runHook中有一句判断,就是对上下文环境的使用:
至于rollup是什么时机调用插件提供的钩子函数的,这里就不啰嗦了,代码中分布很清晰,一看便知.
还有 rollup 为了方便咱们变化插件,还提供了一个工具集,可以非常方便的进行模块的操作以及判断,有兴趣的自行查看。
6|0插件的缓存
插件还提供缓存的能力,实现的非常巧妙:
然后创建缓存后,会添加在插件上下文中:
之后我们就可以在插件中就可以使用cache进行插件环境下的缓存,进一步提升打包效率:
不过需要注意的一点是options
钩子函数是没有注入上下文环境的,它的调用方式也和其他钩子不一样:
7|0总结
rollup系列到此也就告一段落了,从开始阅读时的一脸懵逼,到读到依赖收集、各工具类的十脸懵逼,到现在的轻车熟路,真是一段难忘的经历~
学习大佬们的操作并取其精华,去其糟粕就像打怪升级一样,你品,你细品。哈哈
在这期间也是误导一些东西,看得多了,就会发现,其实套路都一样,摸索出它们的核心框架
,再对功能缝缝补补,不断更新迭代,或许我们也可以成为开源大作的作者。
如果用几句话来描述rollup的话:
读取并合并配置 -> 创建依赖图 -> 读取入口模块内容 -> 借用开源estree规范解析器进行源码分析,获取依赖,递归此操作 -> 生成模块,挂载模块对应文件相关信息 -> 分析ast,构建各node实例 -> 生成chunks -> 调用各node重写的render -> 利用magic-string进行字符串拼接和wrap操作 -> 写入
精简一下就是:
字符串 -> AST -> 字符串
如果改系列能对你一丝丝帮忙,还请动动手指,鼓励一下~
拜了个拜~
__EOF__

本文链接:https://www.cnblogs.com/yangzhuxian/p/13371637.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗