外掛程式
外掛程式是一個回傳物件的函式,更具體來說,這個物件可能包含增強和修改 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 wrapSelectors9 }10 },11 components: {},12 wrapComponents: {},13 rootInjects: {},14 afterLoad: (system) => {},15 fn: {},16}
系統提供給外掛程式
假設我們有一個外掛程式 NormalPlugin
,它在 normal
狀態命名空間下公開 doStuff
動作。
1const 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 want8 return system.normalActions.doStuff(...args)9 }10 }11 }12 }13 }14}
如您所見,每個外掛程式都會傳遞正在建置的 system
的參考。只要 NormalPlugin
在 ExtendingPlugin
之前編譯,這將沒有任何問題。
外掛程式系統中沒有內建的相依性管理,因此如果您建立一個依賴於另一個外掛程式的外掛程式,您有責任確保相依的外掛程式在依賴的外掛程式之後載入。
介面
動作
1const MyActionPlugin = () => {2 return {3 statePlugins: {4 example: {5 actions: {6 updateFavoriteColor: (str) => {7 return {8 type: "EXAMPLE_SET_FAV_COLOR",9 payload: str10 }11 }12 }13 }14 }15 }16}
一旦定義動作,您就可以在可以取得系統參考的任何地方使用它
1// elsewhere2system.exampleActions.updateFavoriteColor("blue")
動作介面可讓您在 Swagger UI 系統的狀態片段內建立新的 Redux 動作建立器。
這個動作建立器函式將以 exampleActions.updateFavoriteColor
的形式公開給容器元件。當呼叫這個動作建立器時,回傳值(應該是 Flux 標準動作)將傳遞到 example
reducer,我們將在下一節中定義它。
如需 Redux 中動作概念的詳細資訊,請參閱 Redux 動作文件。
Reducers
Reducers 會取得狀態(這是一個 Immutable.js 地圖)和一個動作,然後回傳新的狀態。
Reducers 必須在它們處理的動作類型名稱下提供給系統,在此案例中為 EXAMPLE_SET_FAV_COLOR
。
1const 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/ namespaces9 return state.set("favColor", action.payload)10 }11 }12 }13 }14 }15}
選擇器
選擇器會進入其命名空間的狀態,以擷取或衍生狀態中的資料。
它們是將邏輯保持在一個地方的簡單方法,並且優先於將狀態資料直接傳遞到元件。
1const MySelectorPlugin = function(system) {2 return {3 statePlugins: {4 example: {5 selectors: {6 myFavoriteColor: (state) => state.get("favColor")7 }8 }9 }10 }11}
您也可以使用 Reselect 程式庫來記憶您的選擇器,建議用於任何會大量使用的選擇器,因為 Reselect 會自動記憶選擇器呼叫
1import { createSelector } from "reselect"2
3const MySelectorPlugin = function(system) {4 return {5 statePlugins: {6 example: {7 selectors: {8 // this selector will be memoized after it is run once for a9 // value of `state`10 myFavoriteColor: createSelector((state) => state.get("favColor"))11 }12 }13 }14 }15}
一旦定義選擇器,您就可以在可以取得系統參考的任何地方使用它
1system.exampleSelectors.myFavoriteColor() // gets `favColor` in state for you
元件
您可以提供要整合到系統中的元件地圖。
請留意您提供的元件的鍵名稱,因為您需要使用這些名稱在其他地方參照元件。
1class HelloWorldClass extends React.Component {2 render() {3 return <h1>Hello World!</h1>4 }5}6
7const MyComponentPlugin = function(system) {8 return {9 components: {10 HelloWorldClass: HelloWorldClass11 // components can just be functions, these are called "stateless components"12 HelloWorldStateless: () => <h1>Hello World!</h1>,13 }14 }15}
1// elsewhere2const HelloWorldStateless = system.getComponent("HelloWorldStateless")3const HelloWorldClass = system.getComponent("HelloWorldClass")
您也可以建立一個永遠回傳 null
的無狀態元件,來「取消」任何您不想要的元件
1const NeverShowInfoPlugin = function(system) {2 return {3 components: {4 info: () => null5 }6 }7}
如果當系統中不存在元件時您不希望收到警告,則可以使用 config.failSilently
。
請留意 getComponent
引數的順序。在下面的範例中,布林值 false
表示容器的存在,而第三個引數是用來抑制遺失元件警告的設定物件。
1const 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 code2// it's just here for clarity on what's behind the scenes3const MySpecPlugin = function(system) {4 return {5 statePlugins: {6 spec: {7 actions: {8 updateSpec: (str) => {9 return {10 type: "SPEC_UPDATE_SPEC",11 payload: str12 }13 }14 }15 }16 }17 }18}19
20// this plugin allows you to watch changes to the spec that is in memory21const 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 UI28 console.log("Here is my API definition", str)29 return oriAction(str) // don't forget! otherwise, Swagger UI won't update30 }31 }32 }33 }34 }35}
包裝選擇器
包裝選擇器可讓您覆寫系統中選擇器的行為。
它們是簽名為 (oriSelector, system) => (state, ...args) => result
的函式工廠。
此介面適用於控制哪些資料流入元件。我們在核心程式碼中使用此功能來根據 API 定義的版本停用選擇器。
1import { createSelector } from 'reselect'2
3// FYI: in an actual Swagger UI, the `url` spec selector is already defined4// it's just here for clarity on what's behind the scenes5const 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
19const 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 behavior28 return oriSelector(state, ...args)29 }30 }31 }32 }33 }34}
包裝元件
包裝元件可讓您覆寫在系統中註冊的元件。
包裝元件是簽名為 (OriginalComponent, system) => props => ReactElement
的函式工廠。如果您偏好提供 React 元件類別,(OriginalComponent, system) => ReactClass
也可以。
1const 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 plugin2
3// Here's our normal, unmodified component.4const 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.13const 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.31const 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
介面可讓您在系統的最上層注入值。
此介面會接受一個物件,該物件將在執行階段與最上層的系統物件合併。
1const MyRootInjectsPlugin = function(system) {2 return {3 rootInjects: {4 myConstant: 123,5 myMethod: (...params) => console.log(...params)6 }7 }8}
afterLoad
afterLoad
外掛程式方法可讓您在外掛程式註冊後取得系統的參考。
此介面在核心程式碼中使用,以附加由繫結選擇器或動作驅動的方法。您也可以使用它來執行需要您的外掛程式已準備就緒的邏輯,例如從遠端端點提取初始資料並將其傳遞給您的外掛程式建立的動作。
外掛程式內容(繫結至 this
)未記錄,但以下是如何將繫結動作附加為最上層方法範例
1const MyMethodProvidingPlugin = function() {2 return {3 afterLoad(system) {4 // at this point in time, your actions have been bound into the system5 // so you can do things with them6 this.rootInjects = this.rootInjects || {}7 this.rootInjects.myMethod = system.exampleActions.updateFavoriteColor8 },9 statePlugins: {10 example: {11 actions: {12 updateFavoriteColor: (str) => {13 return {14 type: "EXAMPLE_SET_FAV_COLOR",15 payload: str16 }17 }18 }19 }20 }21 }22}
fn
fn 介面可讓您在系統中新增輔助函式,以供其他地方使用。
1import leftPad from "left-pad"2
3const MyFnPlugin = function(system) {4 return {5 fn: {6 leftPad: leftPad7 }8 }9}