通过插件定制工程能力

插件工程能力通过 src/index.ts 定义,结构如下

  1. module.exports = ({ context, onGetWebpackConfig, log, onHook }, options) => {
  2. // 第一项参数为插件 API 提供的能力
  3. // options:插件自定义参数
  4. };

该方法会接收两个参数,第一个参数是插件提供的 API 接口,推荐按照解构方式使用,第二个参数 options 是插件自定义的参数,由插件开发者决定提供哪些选项给用户配置。

插件 API

通过插件提供的 API,可以方便拓展和自定义能力。

context

包含运行时的各种环境信息:

  • command 当前运行命令,start/build/test
  • commandArgs script 命令执行时接受到的参数
  • rootDir 项目根目录
  • userConfig 用户在 build.json 中配置的内容
  • pkg 项目 package.json 中的内容

onGetWebpackConfig

通过 onGetWebpackConfig 获取 webpack-chain 形式的配置,并对配置进行自定义修改:

  1. module.exports = ({onGetWebpackConfig, registerTask}) => {
  2. registerTask('default', webpackConfig);
  3. onGetWebpackConfig((config) => {
  4. config.entry('xxx');
  5. });
  6. }

onHook

通过 onHook 监听命令运行时事件,onHook 注册的函数执行完成后才会执行后续操作,可以用于在命令运行中途插入插件想做的操作:

  1. module.exports = ({ onHook }) => {
  2. onHook('before.build.load', () => {
  3. // do something before dev
  4. });
  5. onHook('after.build.compile', (stats) => {
  6. // do something after build
  7. });
  8. }

目前支持的命令执行生命周期如下:

start 命令

生命周期 参数 调用时机
before.start.load {
args: array // 启动参数
}
获取webpack配置之前
before.start.run - webpack执行之前
after.start.compile {
url: string // serverUrl,
stats: WebpackAssets
}
编译结束,每次重新编译都会执行
before.start.devServer {
url: string // serverUrl,
devServer: WebpackDevServer
}
中间件加载后,webpack dev server 启动前
after.start.devServer {
url: string // serverUrl,
devServer: WebpackDevServer,
err: Error
}
webpack dev server 启动后

build 命令

生命周期 参数 调用时机
before.build.load {
args: array // 启动参数
}
获取 webpack 配置之前
before.build.run - webpack 构建执行之前
after.build.compile {
err: Error,
stats: WebpackAssets
}
构建结束

test 命令

生命周期 参数 调用时机
before.test.load {
args: array // 启动参数
}
获取 jest 配置之前
before.test.run - jest 执行之前
after.test {
result // jest执行结果
}
测试命令执行结束

log

统一的 log 工具,底层使用 npmlog ,便于生成统一格式的 log。

  1. log.info('start');
  2. log.verbose('debug');
  3. log.error('exit');

扩展 API

除了以上由 build-scripts 内置支持的 API,我们还通过 icejs 对插件 API 做了扩展,扩展的 API 需要通过以下方式调用:

  1. module.exports = ({ applyMethod }) => {
  2. // 第一个参数对应 API 名称,第二个参数对应 API 参数
  3. applyMethod('addIceExport', { source: `./config`, exportName });
  4. }

目前扩展的 API 仅支持同步调用。

addIceExport

ice 里注册模块,实现 import { foo } from 'ice'; 的能力:

  1. // 实现 import { request } from 'ice'; request 由插件的 `./request/request` 文件实现
  2. applyMethod('addIceExport', { source: './request/request', exportName: 'request' })

removeIceExport

addIceExport 对应:

  1. this.applyMethod('removeIceExport', 'store');

addPageExport

ice/Home 里注册模块,实现 import { foo } from 'ice/Home',目前主要用于页面级状态管理的实现:

  1. this.applyMethod('addPageExport', 'Home', { source: './models', 'store' })

一般情况下需要循环向每个页面添加。

removePageExport

addPageExport 对应

getPages

获取 src/pages 下的一级页面列表:

  1. // ['Home', 'About']
  2. const pages = this.applyMethod('getPages', this.rootDir);

watchFileChange

监听 src 下的文件变化:

  1. applyMethod('watchFileChange', 'src/config.*', async (event: string) => {
  2. if (event === 'unlink' || event === 'add') {
  3. // do something
  4. }
  5. });

watchFileChange

监听 src 下的文件变化:

  1. applyMethod('watchFileChange', 'src/config.*', async (event: string) => {
  2. if (event === 'unlink' || event === 'add') {
  3. // do something
  4. }
  5. });

插件参数

用户可以在 build.json 中指定插件参数:

  1. {
  2. "plugins": [
  3. ["build-plugin-foo", {
  4. "type": "bar"
  5. }]
  6. ]
  7. }

那么在 build-plugin-foo 里就可以获取到这个参数:

  1. module.exports = ({ context, log }, options) => {
  2. const { type } = options;
  3. log.info(type); // => bar
  4. }

插件通信

插件间需要进行通信的场景诉求:

  1. 不同插件之间需要知道彼此的存在来确定是否执行相应的逻辑
  2. 多个插件共有的配置信息可以抽出来,在某个插件中进行配置

使用 setValuegetValue 两个API来实现,分别用于数据的存取。

setValue

类型:(key: string | number, value: any) => void,示例:

  1. // build-plugin-test
  2. module.exports = ({ setValue }) => {
  3. setValue('key', 123);
  4. }

getValue

类型:(key: string | number) => any,示例:

  1. module.exports = ({getValue}) => {
  2. const value = getValue('key'); // 123
  3. }

同时在 icejs 中也内置了几个变量:

  1. const projectType = getValue('PROJECT_TYPE'); // ts|js
  2. const iceDirPath = getValue('ICE_TEMP'); // 对应 .ice 的路径