跳到内容

插件

插件是一个函数,它返回一个对象——更具体地说,该对象可以包含增强和修改 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")

Action 接口允许在 Swagger UI 系统中某个状态片段内创建新的 Redux action creators。

此 action creator 函数将作为 exampleActions.updateFavoriteColor 暴露给容器组件。当此 action creator 被调用时,返回值(应为 Flux Standard Action)将传递给 example reducer,我们将在下一节中定义它。

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

Reducers

Reducers 接收一个状态(这是一个 Immutable.js map)和一个动作,然后返回一个新状态。

Reducers 必须以它们处理的动作类型的名称提供给系统,在本例中是 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 库来记忆(memoize)您的选择器,这对于任何将大量使用的选择器都推荐这样做,因为 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 指的是是否存在容器,第三个参数是用于抑制缺少组件警告的配置对象。

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

Wrap-Actions

Wrap Actions 允许您覆盖系统中某个动作的行为。

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

Wrap Action 的第一个参数是 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
}

Wrap-Selectors

Wrap Selectors 允许您覆盖系统中某个选择器的行为。

它们是具有签名 (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
}

Wrap-Components

Wrap Components 允许您覆盖系统中注册的组件。

Wrap Components 是具有签名 (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
}
© . All rights reserved.