rerumaccusamus
Version:
The meta-framework suite designed from scratch for frontend-focused modern web development.
257 lines (194 loc) • 7.35 kB
Markdown
---
sidebar_position: 3
---
# Manager
有了 Hook 模型之后,我们就可以通过 Manager 来组织和管理它们,形成插件的模型。在 Manager 中,我们并不关心他们具体是哪些 Hook 模型,只是负责整合它们,收集插件,将插件中对应的函数添加到对应的 Hook 模型中,然后将所有 Hook 模型的运行函数收集整合起来,形成一个 Runner 对象。
## 基础
先通过一个例子整体了解一下:
```ts
import {
createManager,
createPipeline,
createWaterfall,
createWorkflow
} from '@modern-js/plugin'
// 1. 创建 Hook 模型
const foo = createPipeline<number, number>()
const bar = createWaterfall<number>()
const baz = createWorkflow<number, number>()
// 2. 通过 Hook 模型创建 manager
const manager = createManager({ foo, bar, baz });
// 3. 创建插件
const plugin1 = manager.createPlugin(() => {
return {
foo: (count, next) => {
return next(count + 1)
},
bar: (count) => {
return count + 1
},
baz: (count) => {
return count + 1
}
}
})
const plugin2 = manager.createPlugin(() => {
return {
foo(count, next) => {
return count * 2
},
bar(count) => {
return count * 2
},
baz(count) => {
return count * 2
}
}
})
// 4. 将插件添加至 Manager
manager.usePlugin(plugin1, plugin2)
// 5. 初始化 runner
const runner = manager.init()
// 6. 通过 runner 执行各个 Hook 模型
runner.foo(1) // 4
runner.foo(5) // 12
runner.bar(1) // 4
runner.bar(5) // 12
runner.baz(1) // [2, 2]
runner.baz(5) // [6, 10]
```
使用 Manger 的方式和顺序基本就和上面示例中所展示的一致:
1. 创建所需的 Hook 模型
2. 通过 Hook 模型创建 Manager
3. 创建插件
4. 将插件添加至 Manager
5. 初始化 Runner
6. 通过 Runner 执行各个 Hook 模型
其中创建插件时,是通过 Manger 的 `createPlugin` 函数创建,不同 Manager 的插件并不通用,需要传入只一个函数,该函数返回一个对象,对象中对应的是与 Hook 模型同名的函数。
需要注意的是一个插件中不需要为所有 Hook 模型都提供函数,没有提供的则会跳过,甚至可以不为任何一个 Hook 模型提供函数,只会执行初始化函数。
还提供了可以动态注册(在执行插件的时候)Hook 模型,这个的细节之后会提到,这里需要区分是动态注册 Hook 模型,而不是插件。
同样的 Manager 也同时提供了 Sync 版本和 Async 版本,但 Manager 的类型和 Hook 模型的类型没有关系,可以灵活组合,这里的 Sync 和 Async 只会影响插件初始化函数的类型。
## 动态注册 Hook 模型
为了支持动态注册,Manager 中提供了一个 `register` 函数,通过该函数就可以添加新的 Hook 模型,无法覆盖原有的同名 Hook 模型。下面通过一个例子来看一下:
```ts
import {
createManager,
createPipeline,
createWaterfall,
createWorkflow
} from '@modern-js/plugin'
// 1. 创建 Hook 模型
const foo = createPipeline<number, number>()
const baseHooks = { foo }
export interface Hooks {}
// 2. 通过 Hook 模型创建 manager
const manager = createManager<Hooks & typeof baseHooks>(baseHooks);
// 2.1.1 创建需要动态注册的 Hook 模型
const bar = createWaterfall<number>()
const baz = createWorkflow<number, number>()
// 2.1.2 注册新的 Hook 模型
manager.register({
bar,
baz
})
// 2.1.3
export interface Hooks {
bar: typeof bar;
baz: typeof baz;
}
// 3. 创建插件
const plugin1 = manager.createPlugin(() => {
// 2.2.1 创建需要动态注册的 Hook 模型
const bar = createWaterfall<number>()
const baz = createWorkflow<number, number>()
// 2.2.1 注册新的 Hook 模型
manager.register({
bar,
baz
})
return {
foo(count, next) => {
return next(count + 1)
},
bar(count) => {
return count + 1
},
baz(count) => {
return count + 1
}
}
})
const plugin2 = manager.createPlugin(() => {
return {
foo(count, next) => {
return count * 2
},
bar(count) => {
return count * 2
},
baz(count) => {
return count * 2
}
}
})
// 4. 将插件添加至 Manager
manager.usePlugin(plugin1, plugin2)
// 5. 初始化 runner
const runner = manager.init()
// 6. 通过 runner 执行各个 Hook 模型
runner.foo(1) // 4
runner.foo(5) // 12
runner.bar(1) // 4
runner.bar(5) // 12
runner.baz(1) // [2, 2]
runner.baz(5) // [6, 10]
```
如上面例子中,需要动态注册 Hook 模型,除了按照正常方式执行之外,需要创建需要动态注册的 Hook 模型和调用 `register` 来注册新的 Hook 模型,除此之外如果需要完善的 TypeScript 则还需要特别处理下面这里地方:
```ts
const foo = createPipeline<number, number>()
const baseHooks = { foo }
export interface Hooks {}
// 2. 通过 Hook 模型创建 manager
const manager = createManager<Hooks & typeof baseHooks>(baseHooks);
/** ... */
// 2.1.3
export interface Hooks {
bar: typeof bar;
baz: typeof baz;
}
```
提供一个方便扩展的 Hooks 空的 `interface`(L5),然后将这个 Hooks 和 `baseHooks` 的类型作为泛型参数传入,之后在注册完成之后还需要通过 TypeScript 的 Declaration Merging 特性来补充对应的类型(L13)。
动态注册的地方也需要注意,需要在 `manager.int` 结束之前,即可以在插件初始化函数中,但不能在插件 Hook 函数中。
## 插件函数中获得 Runner
除了动态注册之外,这里也提供了一个获取 Runner 的函数:`Manager.useRunner`,通过这个函数可以在插件 Hook 函数中拿到当前的 Runner。需要注意的是,执行在插件 Hook 函数中,其他地方使用则会报错。使用示例如下:
```ts
const foo = createPipeline();
// eslint-disable-next-line @typescript-eslint/no-shadow
const bar = createPipeline();
const manager = createManager({ foo, bar });
let count = 0;
const plugin = manager.createPlugin(() => ({
foo: () => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const runner = useRunner();
runner.bar();
},
bar: () => {
count = 1;
},
}));
manager.usePlugin(plugin);
const runner = manager.init();
runner.foo({});
console.log(count); // 1
```
可以看到,通过 `useRunner` 拿到 Runner,就可以在插件 Hook 函数中执行其他的 Hook
## 插件之间的关系:排序、互斥、必需
Modern.js 的插件系统还提供了排序的功能,在 `Manager.createPlugin` 的第二个参数中可以传入三个参数来控制插件顺序:
* `name: string`,设置当前插件的名字
* `pre: string[]`,需要排在当前插件前面的插件的名字
* `post: string[]`,需要排在当前插件后面的插件的名字
还有一个参数用来实现插件互斥的功能:
* `rivals: string[]`,遇到有这些名字的插件会报错
和一个参数实现必需功能
* `required: string[]`,添加的插件列表中没有这些名字的插件会报错