文章目录  Graph PluginDriver 生成 PluginDriver 实例和 PluginCache 缓存 创建插件上下文 pluginContext 初始化 pluginContext 缓存设置、方法 插件中使用缓存 可替换的 replace pluginContext PluginDriver 提供 asyn、first、parallel 等类型 hook getSortedPlugins 运行时收集并存储插件对应 hook sync hook parallel hook this.runHookSync this.runHook 和 webpack 插件系统区别   
 
在依赖图谱 Graph 中创建 PluginDriver Graph 负责整个 Rollup 打包过程中模块的解析、转化、生成,所以在Graph 中创建 PlunginDriver 能够获得整个打包生命周期的模块信息 	const  graph =  new  Graph ( inputOptions,  watcher) ; 
整个 Rollup 构建生命周期中,通过 pluginDriver 这个实例去触发 hook export  default  class  Graph  { readonly acornParser:  typeof  acorn. Parser; readonly cachedModules =  new  Map < string,  ModuleJSON> ( ) ; entryModules :  Module[ ]  =  [ ] ; readonly fileOperationQueue:  Queue; readonly moduleLoader:  ModuleLoader; readonly modulesById =  new  Map < string,  Module |  ExternalModule> ( ) ; needsTreeshakingPass =  false ; phase :  BuildPhase =  BuildPhase. LOAD_AND_PARSE ; readonly pluginDriver:  PluginDriver; constructor  ( private  readonly options:  NormalizedInputOptions,  watcher :  RollupWatcher |  null )  { this . pluginCache =  options. cache?. plugins ||  Object. create ( null ) ; this . pluginDriver =  new  PluginDriver ( this ,  options,  options. plugins,  this . pluginCache) ;  } 
pluginContext 是交给用户在插件中可以调用的相关方法、属性 可以看到多个插件的上下文共享同一个 pluginCache、graph export  class  PluginDriver  { private  readonly pluginContexts:  ReadonlyMap< Plugin,  PluginContext> ; private  readonly plugins:  readonly Plugin[ ] ; private  readonly sortedPlugins =  new  Map < AsyncPluginHooks,  Plugin[ ] > ( ) ; private  readonly unfulfilledActions =  new  Set < HookAction> ( ) ; constructor ( private  readonly graph:  Graph, private  readonly options:  NormalizedInputOptions, userPlugins :  readonly Plugin[ ] , private  readonly pluginCache:  Record< string,  SerializablePluginCache>  |  undefined , basePluginDriver? :  PluginDriver)  { this . plugins =  [ ... ( basePluginDriver ?  basePluginDriver. plugins :  [ ] ) ,  ... userPlugins] ; const  existingPluginNames =  new  Set < string> ( ) ; this . pluginContexts =  new  Map ( this . plugins. map ( plugin  =>  [ plugin, getPluginContext ( plugin,  pluginCache,  graph,  options,  this . fileEmitter,  existingPluginNames) ] ) ) ; } 
} 
将每个 plugin 按照传递的配置设置缓存 key,存放进 Graph 的 pluginCache 集合中保存 export  function  getPluginContext ( plugin :  Plugin, pluginCache :  Record< string,  SerializablePluginCache>  |  void , graph :  Graph, options :  NormalizedInputOptions, fileEmitter :  FileEmitter, existingPluginNames :  Set< string> ) :  PluginContext { let  cacheable =  true ; if  ( typeof  plugin. cacheKey !==  'string' )  { if  (  plugin. name. startsWith ( ANONYMOUS_PLUGIN_PREFIX )  || plugin. name. startsWith ( ANONYMOUS_OUTPUT_PLUGIN_PREFIX )  || existingPluginNames. has ( plugin. name) )  { cacheable =  false ; }  else  { existingPluginNames. add ( plugin. name) ; } } let  cacheInstance :  PluginCache; if  ( ! pluginCache)  { cacheInstance =  NO_CACHE ; }  else  if  ( cacheable)  { const  cacheKey =  plugin. cacheKey ||  plugin. name; cacheInstance =  createPluginCache (  pluginCache[ cacheKey]  ||  ( pluginCache[ cacheKey]  =  Object. create ( null ) ) ) ; }  else  { cacheInstance =  getCacheForUncacheablePlugin ( plugin. name) ; } return  { addWatchFile ( id )  { if  ( graph. phase >=  BuildPhase. GENERATE )  { return  this . error ( errorInvalidRollupPhaseForAddWatchFile ( ) ) ; } graph. watchFiles[ id]  =  true ; } , cache :  cacheInstance, } ; 
} 
export  function  createPluginCache ( cache :  SerializablePluginCache) :  PluginCache { return  { delete ( id:  string)  { return  delete  cache[ id] ; } , get ( id:  string)  { const  item =  cache[ id] ; if  ( ! item)  return ; item[ 0 ]  =  0 ; return  item[ 1 ] ; } , has ( id :  string)  { const  item =  cache[ id] ; if  ( ! item)  return  false ; item[ 0 ]  =  0 ; return  true ; } , set ( id:  string,  value :  any)  { cache[ id]  =  [ 0 ,  value] ; } } ; 
} 
插件 cache 的内容都会放在 Graph 的 pluginCache 中,在分配缓存时已经根据插件的 key 进行了设置,所以在插件中可以直接 this.cache 进行使用而不必担心和其它插件的缓存冲突 { name :  "test-plugin" , buildStart ( )  { if  ( ! this . cache. has ( "cache" ) )  { this . cache. set ( "cache" ,  "cache something" ) ; }  else  { console. log ( this . cache. get ( "cache" ) ) ; } } , 
} 
pluginContext 会根据不同的 hook,动态增加属性、方法,比如 transform hook 在通过 pluginDriver.hookReduceArg0 调用 transform hook时,第四个参数即是替换后的 pluginContext 		code =  await  pluginDriver. hookReduceArg0 ( 'transform' , [ currentSource,  id] , transformReducer, ( pluginContext,  plugin) :  TransformPluginContext  =>  {  pluginName =  plugin. name; return  { ... pluginContext,  addWatchFile ( id :  string)  { transformDependencies. push ( id) ;  pluginContext. addWatchFile ( id) ; } , cache :  customTransformCache?  pluginContext. cache:  getTrackedPluginCache ( pluginContext. cache,  useCustomTransformCache) , } ; } ) ; 
rollup 根据不同场景提供了不同类型的 hook async:该钩子也可以返回一个解析为相同类型的值的 Promise;否则,该钩子被标记为 sync。 first:如果有多个插件实现此钩子,则钩子按顺序运行,直到钩子返回一个不是 null 或 undefined 的值。 sequential:如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 async,则此类后续钩子将等待当前钩子解决后再运行。 parallel:如果有多个插件实现此钩子,则所有这些钩子将按指定的插件顺序运行。如果钩子是 async,则此类后续钩子将并行运行,而不是等待当前钩子。  export  class  PluginDriver  { hookFirstSync< H  extends  SyncPluginHooks  &  FirstPluginHooks> ( hookName :  H , parameters :  Parameters< FunctionPluginHooks[ H ] > , replaceContext? :  ReplaceContext) :  ReturnType< FunctionPluginHooks[ H ] >  |  null  { } async  hookParallel< H  extends  AsyncPluginHooks  &  ParallelPluginHooks> ( hookName :  H , parameters :  Parameters< FunctionPluginHooks[ H ] > , replaceContext? :  ReplaceContext) :  Promise< void >  { } 
} Rollup 通过 getSortedPlugins 对插件的对应 hook 进行排序后收集在 this.sortedPlugins 集合中存储 根据 pre、normal、post 顺序排序每个hook 
private  getSortedPlugins (  hookName :  keyof FunctionPluginHooks |  AddonHooks, validateHandler? :  ( handler :  unknown,  hookName :  string,  plugin :  Plugin)  =>  void 
) :  Plugin[ ]  { return  getOrCreate (  this . sortedPlugins, hookName, ( )  =>  getSortedValidatedPlugins ( hookName,  this . plugins,  validateHandler)  ) ; 
} export  function  getOrCreate< K ,  V > ( map:  Map< K ,  V > ,  key :  K ,  init :  ( )  =>  V ) :  V  { const  existing =  map. get ( key) ; if  ( existing !==  undefined )  { return  existing; } const  value =  init ( ) ; map. set ( key,  value) ; return  value; 
} export  function  getSortedValidatedPlugins ( hookName :  keyof FunctionPluginHooks |  AddonHooks, plugins :  readonly Plugin[ ] , validateHandler =  validateFunctionPluginHandler) :  Plugin[ ]  { const  pre :  Plugin[ ]  =  [ ] ; const  normal :  Plugin[ ]  =  [ ] ; const  post :  Plugin[ ]  =  [ ] ; for  ( const  plugin of  plugins)  { const  hook =  plugin[ hookName] ; if  ( hook)  { if  ( typeof  hook ===  'object' )  { validateHandler ( hook. handler,  hookName,  plugin) ; if  ( hook. order ===  'pre' )  { pre. push ( plugin) ; continue ; } if  ( hook. order ===  'post' )  { post. push ( plugin) ; continue ; } }  else  { validateHandler ( hook,  hookName,  plugin) ; } normal. push ( plugin) ; } } return  [ ... pre,  ... normal,  ... post] ; 
} 
同步执行 hook,返回第一个非 null 的结果 	hookFirstSync< H  extends  SyncPluginHooks  &  FirstPluginHooks> ( hookName :  H , parameters :  Parameters< FunctionPluginHooks[ H ] > , replaceContext? :  ReplaceContext) :  ReturnType< FunctionPluginHooks[ H ] >  |  null  { for  ( const  plugin of  this . getSortedPlugins ( hookName) )  { const  result =  this . runHookSync ( hookName,  parameters,  plugin,  replaceContext) ; if  ( result !=  null )  return  result; } return  null ; } 
用于并行执行的 hook,忽略返回值 通过 Promise.all 并行执行 	async  hookParallel< H  extends  AsyncPluginHooks  &  ParallelPluginHooks> ( hookName :  H , parameters :  Parameters< FunctionPluginHooks[ H ] > , replaceContext? :  ReplaceContext) :  Promise< void >  { const  parallelPromises :  Promise< unknown> [ ]  =  [ ] ; for  ( const  plugin of  this . getSortedPlugins ( hookName) )  {  if  ( ( plugin[ hookName]  as  {  sequential? :  boolean } ) . sequential)  {  await  Promise. all ( parallelPromises) ; parallelPromises. length =  0 ; await  this . runHook ( hookName,  parameters,  plugin,  replaceContext) ; }  else  { parallelPromises. push ( this . runHook ( hookName,  parameters,  plugin,  replaceContext) ) ;  } } await  Promise. all ( parallelPromises) ;  } 同步调用时,通过 this.pluginContexts.get(plugin) 获取到插件上下文供插件开发者使用 	private  runHookSync< H  extends  SyncPluginHooks > ( hookName :  H , parameters :  Parameters< FunctionPluginHooks[ H ] > , plugin :  Plugin, replaceContext? :  ReplaceContext) :  ReturnType< FunctionPluginHooks[ H ] >  { const  hook =  plugin[ hookName] ! ; const  handler =  typeof  hook ===  'object'  ?  hook. handler :  hook; let  context =  this . pluginContexts. get ( plugin) ! ; if  ( replaceContext)  { context =  replaceContext ( context,  plugin) ; } try  { return  ( handler as  Function) . apply ( context,  parameters) ; }  catch  ( error_:  any)  { return  error ( errorPluginError ( error_,  plugin. name,  {  hook :  hookName } ) ) ; } } 
异步调用时,如果是结果也是 Promise,会暂存然后等待所有Promise都执行结束 	private  runHook< H  extends  AsyncPluginHooks  |  AddonHooks> ( hookName :  H , parameters :  unknown[ ] , plugin :  Plugin, replaceContext? :  ReplaceContext |  null ) :  Promise< unknown>  { const  hook =  plugin[ hookName] ; const  handler =  typeof  hook ===  'object'  ?  hook. handler :  hook; let  context =  this . pluginContexts. get ( plugin) ! ;  if  ( replaceContext)  {  context =  replaceContext ( context,  plugin) ; } let  action :  [ string,  string,  Parameters< any> ]  |  null  =  null ; return  Promise. resolve ( ) . then ( ( )  =>  { if  ( typeof  handler !==  'function' )  { return  handler; } const  hookResult =  ( handler as  Function) . apply ( context,  parameters) ;  if  ( ! hookResult?. then)  { return  hookResult; } action =  [ plugin. name,  hookName,  parameters] ;  this . unfulfilledActions. add ( action) ; return  Promise. resolve ( hookResult) . then ( result  =>  {  this . unfulfilledActions. delete ( action! ) ;  return  result; } ) ; } ) . catch ( error_  =>  { if  ( action !==  null )  { this . unfulfilledActions. delete ( action) ; } return  error ( errorPluginError ( error_,  plugin. name,  {  hook :  hookName } ) ) ; } ) ; } 
Rollup 通过抽象化一个 PluginDriver 的实例负责专门驱动插件的调用,并且 PluginDriver 和 Graph 绑定,能够共享打包过程的信息;webpack 通过 tapable 进行订阅发布,本身可以脱离 webpack 使用 在运行模式上,Rollup 是运行时根据 hookName 收集对应的插件 ,然后对插件进行排序后存储,通过 runHook 或 runHookSync 进行调用;webpack 通过订阅发布,先注册插件,然后在生命周期的流程中调用。总的来说 Rollup 的运行时收集比起 webpack 具有一点点内存优势