跳到内容

插件

插件是一个返回对象的函数 - 更具体地说,该对象可能包含增强和修改 Swagger UI 功能的函数和组件。

注意:语义化版本控制

Swagger UI 的内部 API *不是*我们公共协议的一部分,这意味着它们可能会在没有主要版本更改的情况下发生更改。

如果您的自定义插件包装、扩展、覆盖或使用任何内部核心 API,我们建议您在应用程序中指定要使用的 Swagger UI 的特定次要版本,因为它们在补丁版本之间*不会*更改。

例如,如果您通过 NPM 安装 Swagger UI,则可以通过使用波浪号来执行此操作

1
{
2
"dependencies": {
3
"swagger-ui": "~3.11.0"
4
}
5
}

格式

插件返回值可能包含以下任何键,其中 stateKey 是状态的名称

1
{
2
statePlugins: {
3
[stateKey]: {
4
actions,
5
reducers,
6
selectors,
7
wrapActions,
8
wrapSelectors
9
}
10
},
11
components: {},
12
wrapComponents: {},
13
rootInjects: {},
14
afterLoad: (system) => {},
15
fn: {},
16
}

系统提供给插件

假设我们有一个插件 NormalPlugin,它在 normal 状态命名空间下公开一个 doStuff 操作。

1
const ExtendingPlugin = function(system) {
2
return {
3
statePlugins: {
4
extending: {
5
actions: {
6
doExtendedThings: function(...args) {
7
// you can do other things in here if you want
8
return system.normalActions.doStuff(...args)
9
}
10
}
11
}
12
}
13
}
14
}

如您所见,每个插件都传递了对正在构建的 system 的引用。 只要 NormalPluginExtendingPlugin 之前编译,这将没有任何问题。

插件系统中没有内置的依赖项管理,因此,如果您创建一个依赖于另一个插件的插件,您有责任确保依赖插件在被依赖的插件*之后*加载。

接口

操作

1
const MyActionPlugin = () => {
2
return {
3
statePlugins: {
4
example: {
5
actions: {
6
updateFavoriteColor: (str) => {
7
return {
8
type: "EXAMPLE_SET_FAV_COLOR",
9
payload: str
10
}
11
}
12
}
13
}
14
}
15
}
16
}

定义操作后,您可以在可以获取系统引用的任何位置使用它

1
// elsewhere
2
system.exampleActions.updateFavoriteColor("blue")

操作接口允许在 Swagger UI 系统中的一块状态内创建新的 Redux 操作创建器。

此操作创建器函数将作为 exampleActions.updateFavoriteColor 公开给容器组件。 调用此操作创建器时,返回值(应该是 Flux 标准操作)将传递给 example 归约器,我们将在下一节中定义它。

有关 Redux 中操作概念的更多信息,请参阅 Redux 操作文档

归约器

归约器获取状态(这是一个 Immutable.js 映射)和一个操作,然后返回一个新的状态。

必须以它们处理的操作类型的名称(在本例中为 EXAMPLE_SET_FAV_COLOR)将归约器提供给系统。

1
const MyReducerPlugin = function(system) {
2
return {
3
statePlugins: {
4
example: {
5
reducers: {
6
"EXAMPLE_SET_FAV_COLOR": (state, action) => {
7
// you're only working with the state under the namespace, in this case "example".
8
// So you can do what you want, without worrying about /other/ namespaces
9
return state.set("favColor", action.payload)
10
}
11
}
12
}
13
}
14
}
15
}

选择器

选择器深入到其命名空间的状态,以检索或从状态派生数据。

它们是将逻辑保留在一个位置的简单方法,并且优于将状态数据直接传递到组件中。

1
const MySelectorPlugin = function(system) {
2
return {
3
statePlugins: {
4
example: {
5
selectors: {
6
myFavoriteColor: (state) => state.get("favColor")
7
}
8
}
9
}
10
}
11
}

您还可以使用 Reselect 库来记忆您的选择器,这对于任何会被大量使用的选择器都是建议的,因为 Reselect 会自动记忆选择器调用

1
import { createSelector } from "reselect"
2
3
const MySelectorPlugin = function(system) {
4
return {
5
statePlugins: {
6
example: {
7
selectors: {
8
// this selector will be memoized after it is run once for a
9
// value of `state`
10
myFavoriteColor: createSelector((state) => state.get("favColor"))
11
}
12
}
13
}
14
}
15
}

定义选择器后,您可以在可以获取系统引用的任何位置使用它

1
system.exampleSelectors.myFavoriteColor() // gets `favColor` in state for you

组件

您可以提供要集成到系统中的组件映射。

请注意您提供的组件的键名称,因为您需要使用这些名称在其他地方引用这些组件。

1
class HelloWorldClass extends React.Component {
2
render() {
3
return <h1>Hello World!</h1>
4
}
5
}
6
7
const MyComponentPlugin = function(system) {
8
return {
9
components: {
10
HelloWorldClass: HelloWorldClass
11
// components can just be functions, these are called "stateless components"
12
HelloWorldStateless: () => <h1>Hello World!</h1>,
13
}
14
}
15
}
1
// elsewhere
2
const HelloWorldStateless = system.getComponent("HelloWorldStateless")
3
const HelloWorldClass = system.getComponent("HelloWorldClass")

您还可以通过创建一个始终返回 null 的无状态组件来“取消”任何您不想要的组件

1
const NeverShowInfoPlugin = function(system) {
2
return {
3
components: {
4
info: () => null
5
}
6
}
7
}

如果您不希望在系统中不存在组件时收到警告,则可以使用 config.failSilently

请注意 getComponent 参数顺序。 在下面的示例中,布尔值 false 指的是容器的存在,第 3 个参数是用于禁止丢失组件警告的配置对象。

1
const thisVariableWillBeNull = getComponent("not_real", false, { failSilently: true })

包装操作

包装操作允许您覆盖系统中操作的行为。

它们是具有签名 (oriAction, system) => (...args) => result 的函数工厂。

包装操作的第一个参数是 oriAction,它是被包装的操作。 您有责任调用 oriAction - 如果不调用,则原始操作将不会触发!

此机制对于有条件地覆盖内置行为或侦听操作非常有用。

1
// FYI: in an actual Swagger UI, `updateSpec` is already defined in the core code
2
// it's just here for clarity on what's behind the scenes
3
const MySpecPlugin = function(system) {
4
return {
5
statePlugins: {
6
spec: {
7
actions: {
8
updateSpec: (str) => {
9
return {
10
type: "SPEC_UPDATE_SPEC",
11
payload: str
12
}
13
}
14
}
15
}
16
}
17
}
18
}
19
20
// this plugin allows you to watch changes to the spec that is in memory
21
const MyWrapActionPlugin = function(system) {
22
return {
23
statePlugins: {
24
spec: {
25
wrapActions: {
26
updateSpec: (oriAction, system) => (str) => {
27
// here, you can hand the value to some function that exists outside of Swagger UI
28
console.log("Here is my API definition", str)
29
return oriAction(str) // don't forget! otherwise, Swagger UI won't update
30
}
31
}
32
}
33
}
34
}
35
}

包装选择器

包装选择器允许您覆盖系统中选择器的行为。

它们是具有签名 (oriSelector, system) => (state, ...args) => result 的函数工厂。

此接口对于控制哪些数据流入组件非常有用。 我们在核心代码中使用它来根据 API 定义的版本禁用选择器。

1
import { createSelector } from 'reselect'
2
3
// FYI: in an actual Swagger UI, the `url` spec selector is already defined
4
// it's just here for clarity on what's behind the scenes
5
const MySpecPlugin = function(system) {
6
return {
7
statePlugins: {
8
spec: {
9
selectors: {
10
url: createSelector(
11
state => state.get("url")
12
)
13
}
14
}
15
}
16
}
17
}
18
19
const MyWrapSelectorsPlugin = function(system) {
20
return {
21
statePlugins: {
22
spec: {
23
wrapSelectors: {
24
url: (oriSelector, system) => (state, ...args) => {
25
console.log('someone asked for the spec url!!! it is', state.get('url'))
26
// you can return other values here...
27
// but let's just enable the default behavior
28
return oriSelector(state, ...args)
29
}
30
}
31
}
32
}
33
}
34
}

包装组件

包装组件允许您覆盖系统中注册的组件。

包装组件是具有签名 (OriginalComponent, system) => props => ReactElement 的函数工厂。 如果您更喜欢提供 React 组件类,那么 (OriginalComponent, system) => ReactClass 也可以工作。

1
const MyWrapBuiltinComponentPlugin = function(system) {
2
return {
3
wrapComponents: {
4
info: (Original, system) => (props) => {
5
return <div>
6
<h3>Hello world! I am above the Info component.</h3>
7
<Original {...props} />
8
</div>
9
}
10
}
11
}
12
}

这是另一个示例,其中包含一个将要包装的组件的代码示例

1
///// Overriding a component from a plugin
2
3
// Here's our normal, unmodified component.
4
const MyNumberDisplayPlugin = function(system) {
5
return {
6
components: {
7
NumberDisplay: ({ number }) => <span>{number}</span>
8
}
9
}
10
}
11
12
// Here's a component wrapper defined as a function.
13
const MyWrapComponentPlugin = function(system) {
14
return {
15
wrapComponents: {
16
NumberDisplay: (Original, system) => (props) => {
17
if(props.number > 10) {
18
return <div>
19
<h3>Warning! Big number ahead.</h3>
20
<Original {...props} />
21
</div>
22
} else {
23
return <Original {...props} />
24
}
25
}
26
}
27
}
28
}
29
30
// Alternatively, here's the same component wrapper defined as a class.
31
const MyWrapComponentPlugin = function(system) {
32
return {
33
wrapComponents: {
34
NumberDisplay: (Original, system) => class WrappedNumberDisplay extends React.component {
35
render() {
36
if(props.number > 10) {
37
return <div>
38
<h3>Warning! Big number ahead.</h3>
39
<Original {...props} />
40
</div>
41
} else {
42
return <Original {...props} />
43
}
44
}
45
}
46
}
47
}
48
}

rootInjects

rootInjects 接口允许您在系统的顶层注入值。

此接口采用一个对象,该对象将在运行时与顶级系统对象合并。

1
const MyRootInjectsPlugin = function(system) {
2
return {
3
rootInjects: {
4
myConstant: 123,
5
myMethod: (...params) => console.log(...params)
6
}
7
}
8
}

afterLoad

afterLoad 插件方法允许您在插件注册后获取对系统的引用。

此接口在核心代码中用于附加由绑定选择器或操作驱动的方法。 您还可以使用它来执行需要您的插件已准备就绪的逻辑,例如从远程端点获取初始数据并将其传递到您的插件创建的操作。

绑定到 this 的插件上下文是未记录的,但是下面是如何将绑定操作作为顶级方法附加的示例

1
const MyMethodProvidingPlugin = function() {
2
return {
3
afterLoad(system) {
4
// at this point in time, your actions have been bound into the system
5
// so you can do things with them
6
this.rootInjects = this.rootInjects || {}
7
this.rootInjects.myMethod = system.exampleActions.updateFavoriteColor
8
},
9
statePlugins: {
10
example: {
11
actions: {
12
updateFavoriteColor: (str) => {
13
return {
14
type: "EXAMPLE_SET_FAV_COLOR",
15
payload: str
16
}
17
}
18
}
19
}
20
}
21
}
22
}

fn

fn 接口允许您将辅助函数添加到系统中,以便在其他地方使用。

1
import leftPad from "left-pad"
2
3
const MyFnPlugin = function(system) {
4
return {
5
fn: {
6
leftPad: leftPad
7
}
8
}
9
}