跳至內容

外掛程式

外掛程式是一個回傳物件的函式,更具體來說,這個物件可能包含增強和修改 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 reducer,我們將在下一節中定義它。

如需 Redux 中動作概念的詳細資訊,請參閱 Redux 動作文件

Reducers

Reducers 會取得狀態(這是一個 Immutable.js 地圖)和一個動作,然後回傳新的狀態。

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 程式庫來記憶您的選擇器,建議用於任何會大量使用的選擇器,因為 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 })

包裝動作

包裝動作可讓您覆寫系統中動作的行為。

它們是簽名為 (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
}