引言

Rollup是一个JavaScript模块打包器,它能够将多个模块打包成一个独自的文件,以便在浏览器中运用。与其他打包工具比较,Rollup的主要优势在于它能够生成更小、更快的代码。在本文中,我们将深化了解Rollup的插件机制。


关于Rollup的作业原理、运用方法、摇树优化(tree shaking)能够看这篇 深化了解rollup(一)快速开端

关于Rollup的装备能够看这篇 深化了解rollup(二)常用装备


rollup插件机制概述

Rollup 插件是一个目标,具有特点]构建钩子输出生成钩子中的一个或多个,并遵从我们的约好。插件应作为一个导出一个函数的包进行发布,该函数能够运用插件特定的选项进行调用并回来此类目标。

插件答应你经过例如在打包之前进行转译代码或在node_modules文件夹中查找第三方模块来自界说 Rollup 的行为。

特点

  • name: 插件的称号,用于在警告和过错消息中标识插件。

  • version: 插件的版别,用于插件间通信场景。

约好

  • 插件应该有一个清晰的称号,并以rollup-plugin-作为前缀。

  • package.json中包括rollup-plugin关键字。

  • 假如插件运用“虚拟模块”(例如用于辅佐函数),请运用前缀模块 ID。这能够避免其他插件测验处理它。

构建钩子履行方法

钩子是在构建的各个阶段调用的函数。钩子能够影响构建的运转方法,供给关于构建的信息,或在构建完成后修正构建。有不同品种的钩子:

  • async:该钩子也能够回来一个解析为相同类型的值的 Promise;不然,该钩子被标记为sync
  • first:假如有多个插件完成此钩子,则钩子按次序运转,直到钩子回来一个不是nullundefined的值。
  • sequential:假如有多个插件完成此钩子,则一切这些钩子将按指定的插件次序运转。假如钩子是async,则此类后续钩子将等候当时钩子处理后再运转。
  • parallel:假如有多个插件完成此钩子,则一切这些钩子将按指定的插件次序运转。假如钩子是async,则此类后续钩子将并行运转,而不是等候当时钩子。

除了函数之外,钩子也能够是目标。在这种情况下,实践的钩子函数必须指定为handler。这答应你供给更多的可选特点,以改动钩子的履行:

  • order: “pre” | “post” | null

假如有多个插件完成此钩子,则能够先运转此插件("pre"),最终运转此插件("post"),或在用户指定的方位运转(没有值或 null)。

export default function resolveFirst() {
  return {
    name: 'resolve-first',
    resolveId: {
      order: 'pre',
      handler(source) {
        console.log(source);
        return null;
      }
    }
  };
}

输出生成钩子

输出生成钩子能够供给有关生成的产物的信息并在构建完成后修正构建。它们的作业方法和类型与构建钩子相同,但是关于每个调用bundle.generate(outputOptions)bundle.write(outputOptions),它们都会独自调用。仅运用输出生成钩子的插件也能够经过输出选项传递,并且因而仅针对某些输出运转。

钩子履行次序

深化了解rollup(三)插件机制

  1. 经过 options 钩子读取装备,并进行装备的转化,得到处理后的装备目标。

  2. 调用 buildStart 钩子,考虑了一切 options钩子装备的转化,包括未设置选项的正确默认值,正式开端构建流程。

  3. 调用 resolveId 钩子解析模块文件途径。rollup中模块文件的id便是文件地址,所以,类似resolveId这种便是解析文件地址的意思。从inputOptioninput装备指定的入口文件开端,每当匹配到引进外部模块的语句(如:import moudleA from './moduleA')便依次履行注册插件中的每一个 resolveId 钩子,直到某一个插件中的 resolveId 履行完后回来非 null 或非 undefined 的值,将停止履行后续插件的 resolveId 逻辑并进入下一个钩子。

  4. 调用load钩子加载模块内容,resolveId中的途径一般为相对途径,load中的途径为处理之后的绝对途径。

  5. 接着判断当时解析的模块是否存在缓存,若不存在则履行一切的 transform 钩子来对模块内容进行进行自界说的转化;若存在则判断shouldTransformCachedModule特点,true则履行一切的 transform 钩子,false则进入moduleParsed钩子逻辑。。

  6. 拿到最终的模块内容,进行 AST 分析,调用 moduleParsed 钩子。假如内部没有imports内容,进入buildEnd环节。假如还有imports内容则持续,假如是一般的 import,则履行resolveId 钩子,持续回到过程3-调用resolveId;假如是动态 import,则履行resolveDynamicImport 钩子解析途径,假如解析成功,则回到过程4-load加载模块,不然回到过程3经过 resolveId 解析途径。

  7. 直到一切的 import 都解析完毕,Rollup 履行buildEnd钩子,Build阶段完毕。

插件示例

// rollup-plugin-example.js
export default function myExample () {
  return {
    name: 'my-example',
    options (options) {
      console.log({ options })
    },
    buildStart (options) {
      console.log("buildStart:", options)
    },
    resolveId (source,importer) {
      console.log("resolveId(source):", source)
      console.log("resolveId(importer):", importer)
      return null; 
    },
    load (id) {
      console.log({ id })
      return null; 
    },
    transform(code,id) {
      console.log("transform");
      console.log("---",code)
      console.log("---",id)
    },
    moduleParsed (info) {
      console.log("moduleParsed:", info)
    },
    buildEnd() { 
      console.log("buildEnd");
    }
  };
}

调用虚拟模块插件示例

const virtualModuleId = 'virtual-module';
// rollup约好插件运用“虚拟模块”,运用前缀模块 ID。这能够避免其他插件测验处理它。
const resolvedVirtualModuleId = '' + virtualModuleId;
export default function virtualModule() {
  return {
    name: 'virtual-module', 
    resolveId (source) {
      if (source === 'virtual-module') { 
        return resolvedVirtualModuleId; // 告知Rollup,这个ID是外部模块,不要在此处查找它
      }
      return null; // 其他ID应按一般方法处理
    },
    load (id) {
      console.log({ id })
      if (id === resolvedVirtualModuleId) { 
        // return 'export default "This is virtual!"'; // 告知Rollup,如何加载此模块
        return 'export default function fib(n) { return n <= 1 ? n : fib(n - 1) + fib(n - 2); }'
      }
      return null; // 其他ID应按一般方法处理
    },
  };
}

界面调用

// index.js
import fib from "virtual-module";
console.log(fib(10))

build之后

// index.js
function fib(n) {return n <= 1 ? n : fib(n - 1) + fib(n - 2)}
console.log(fib(10));

总结

Rollup的插件机制经过界说钩子函数来扩展其功用,钩子函数在不同的阶段履行不同的操作。开发者能够根据自己的需求编写自界说插件,并将其添加到Rollup装备中。经过运用插件机制,能够完成各种功用扩展,例如修正装备选项、解析模块途径、加载模块内容、转化模块代码等。