優維低程式碼技術專欄,
是一個全新的、技術為主的專欄,由優維技術委員會成員執筆,基於優維7年低程式碼技術研發及運維成果,主要介紹低程式碼相關的技術原理及架構邏輯,目的是給廣大運維人提供一個技術交流與學習的平臺。
連載第十六期
《高階指引:Custom Templates 自定義模板》
▽
# 背景
在較早的 Legacy Templates 構件模板中(以下稱 Legacy Templates),我們定義了一種使用 JavaScript Functions 定義的構件模板,這一層抽象在一定程度上封裝了複雜的構件配置。
Legacy Templates 使用函式定義,因此擁有了高度靈活的動態展開能力。但函式定義使得框架無法將它們像普通構件一樣地利用起來,因此受到了諸多限制,例如模板無法享受 Provider 重新整理帶動更新、無法對模板繫結事件處理、無法讓使用者編排一個自定義模板等等。
因此有必要設計一種自定義模板(以下稱 Custom Templates),它使用靜態結構宣告(JSON or YAML),並且擁有普通構件的所有能力,以便在 Brick Next 中編排者可以像普通構件一樣去使用它們。
# 原理
Custom Templates 的定義和 Storyboard 中的構件配置基本保持一致,可以在它內部配置多個構件,並且可以為這些構件配置插槽。在系統渲染該模板時,將按模板定義展開,並維護一份模板外部與模板內部的屬性和事件等的的對映關係。
模板的展開過程如下圖所示:
上圖中
紫色邊框
表示的是 Custom Template,模板在執行時按模板的定義被展開,
綠色邊框
的構件是模板內部定義好的構件,展開前的模板的
tools
插槽下的
橙色邊框
的構件按模板定義被移植到了
basic-bricks。micro-view
的
toolbar
插槽中。並且模板構件的屬性和事件等也會按模板定義對映到相關內部構件中。
# 示例
例如一個簡單的模板定義:
# A custom template definition。name: “some-package。my-custom-template”bricks: - brick: “basic-bricks。micro-view” properties: pageTitle: “My Awesome Page” slots: content: type: “bricks” bricks: - brick: “my。awesome-brick” - brick: “my。another-brick” events: something。happen: action: “console。log”
在定義好 Custom Templates 後,它們可以像普通構件一樣使用:
# A storyboard partial。brick: “some-package。my-custom-template”# All lifeCycles and other configs。lifeCycle: 。。。
Custom Templates 可以代理內部構件的屬性、事件、插槽和方法,以此實現將 Storyboard 上為該模板配置的屬性和事件等對映到模板內部的指定構件上。
例如以下包含 proxy 設定的模板定義:
# A custom template definition with proxy。name: “some-package。my-custom-template”proxy: properties: pageName: ref: “micro-vew” refProperty: “pageTitle” # Set `refTransform` instead of `refProperty`, to transform a property。 # E。g。: # refTransform: { # pageTitle: ‘<% `${DATA。pageName} - Awesome Page` %>’ # } # Tips: the transformed source data is `{ [tplPropName] : tplPropValue }`。 events: awesome。happen: ref: “awesome-brick” refEvent: “something。happen” slots: tools: ref: “micro-view” refSlot: “toolbar” # Optional `refPosition` (number): # Insert the slotted bricks to the specified position of inner brick slot。 # If `refPosition >= 0`, counts from the start。 # If `refPosition < 0`, counts from the end。 # The `refPosition` is relative to the slotted bricks specified in template definition。 methods: tellAwesomeStories: ref: “awesome-brick” refMethod: “tellStories”bricks: - brick: “basic-bricks。micro-view” ref: “micro-view” # Notice here! The ref id should be unique inside a custom template。 slots: content: type: “bricks” bricks: - brick: “my。awesome-brick” ref: “awesome-brick” # And another ref。 events: inner。happen: # Use `targetRef` to point to another brick inside a custom template。 targetRef: “micro-view” method: “updateView”
在 Storyboard 中使用:
# A storyboard partial。brick: “some-package。my-custom-template”properties: pageName: “My Awesome Page”events: awesome。happen: action: “console。log”slots: tools: type: “bricks” bricks: [。。。]
在該 Storyboard 中:
模板的
pageName
屬性將被對映到模板內部的
micro-view
的
pageTitle
屬性上;
模板內部的
awesome-brick
觸發的
something。happen
將觸發模板的
awesome。happen
事件;
模板的
tools
插槽將被裝載到模板內部的
micro-view
的
toolbar
插槽中;
呼叫模板的
tellAwesomeStories
將實際呼叫模板內部的
awesome-brick
的
tellStories
方法。
# 將模板的一個屬性對映到多個不同的內部構件屬性
有時候我們期望將模板的一個屬性對映到多個不同的內部構件屬性上,這時我們可以配置
extraOneWayRefs
:
proxy: properties: pageName: ref: “micro-vew” refProperty: “pageTitle” extraOneWayRefs: - ref: “button” refProperty: “buttonName” - ref: “link” refProperty: “linkName”
對於以上配置,模板對外的屬性
pageName
除了對映到
micro-view
的
pageTitle
上,還會同時對映到
button
和
link
的對應屬性上。
注意:
正如該配置的名稱所說,
extraOneWayRefs
是單向的,如果直接變更其內部構件的屬性不會向上反饋到模板上。
# 屬性合併
有時候我們希望自定義模板代理的內部構件的屬性可以進行資料合併,而不是整體的屬性對映。例如我們定義一個自定義模板,它內部有一個表格構件,它的
columns
初始定義了一些通用列,同時希望編排者可以按需為表格增加其它的列定義。
現已支援透過
mergeProperty
等配置即可實現屬性合併,例如定義一個自定義模板:
name: “tpl-extensible-table”proxy: properties: appendColumns: ref: “table” mergeProperty: “columns” mergeType: “array” # “array” or “object” mergeMethod: “append” # “append” or “prepend” or “insertAt” # mergeArgs: # optional args for methods like “insertAt”bricks: - brick: “presentational-bricks。brick-table” properties: # Predefined default columns columns: - dataIndex: “id” title: “ID” - dataIndex: “name” title: “Name”
編排時:
bricks: - brick: “tpl-extensible-table” properties: # User-defined extra columns appendColumns: - dataIndex: “email” title: “Email
詳細說明:
mergeProperty
: 要合併到的內部構件的屬性名。
mergeType
:
支援兩種型別:
”array“
和
”object“
。
mergeMethod
:
當
mergeType
===
”array“
時支援三種方法:
”append“
,
”prepend“
,
”insertAt“
;
當
mergeType
===
”object“
時只支援一種方法:
”extend“
。
mergeArgs
:
當
mergeType
===
”array“ && mergeMethod
===
”insertAt“
時傳入一個索引位置,位置為負數時,從結尾開始計數,預設為
[ -1 ]
。
注意:
當使用
mergeProperty
時,屬性是從模板到內部構件單向流動的,即如果直接更改內部構件的相關屬性,無法影響直接從模板讀取的屬性。
# 屬性作為內部變數
有時候我們希望可以定義一些模板例項級別的變數,模板內部的構件可以在屬性、事件等地方消費這些變數。注意它和 Context 上下文 的區別,後者相當於全域性變數,無法將其作用域限制在某個具體的模板的例項內部。
現已支援將指定的模板屬性標記為可供內部構件引用的變數,例如定義一個自定義模板:
name: ”tpl-flexible-table“proxy: properties: # 1。 將屬性標記為 `asVariable: true`。 dataIndexOfName: asVariable: truebricks: - brick: ”presentational-bricks。brick-table“ properties: columns: # 2。 在求值佔位符中使用 `TPL。*` 引用標記好的屬性。 - dataIndex: ”<% TPL。dataIndexOfName ?? ‘name’ %>“ title: ”Name“
編排時:
bricks: - brick: ”tpl-flexible-table“ properties: dataIndexOfName: ”hostname“ - brick: ”tpl-flexible-table“ properties: dataIndexOfName: ”username“
⊙ NOTE
在內部構件的屬性中引用 TPL。* 時,只能在構件初始時拿到其初始值,此時該屬性變數相當於一個常量,當模板例項的對應屬性在執行時重新賦值時,該內部構件的屬性不會發生變化。另一方面,在事件回撥裡則始終能拿到當事件發生時、最新的 TPL。*。更多資訊請參考 Context 上下中的注意事項。
# 開發
執行
yarn yo
;
選擇
a new custom template
(如果沒有該選項,請先執行
yarn renew
然後再從第一步開始);
輸入你的 Custom Template 名稱;
一個新的 Custom Template 就初始化好了:
// `。/bricks/YOUR-PKG/src/custom-templates/YOUR-TEMPLATE。ts`import { getRuntime } from ”@next-core/brick-kit“;getRuntime()。registerCustomTemplate(”YOUR-PKG。YOUR-TEMPLATE“, { bricks: [], proxy: {},});