欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JS前端組件注冊與畫布渲染實例

 更新時間:2023年02月06日 14:26:11   作者:黃子毅  
這篇文章主要為大家介紹了JS前端組件注冊與畫布渲染實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

引言

接著可視化搭建的理論抽象,我們開始勾勒一個具體的 React 可視化搭建器。

假如我們將可視化搭建整體定義為 <Designer>,那么 API 可能是這樣的:

<Designer componentMetas={[]} componentTree={} />
  • componentMetas: 定義組件元信息的數(shù)組。
  • componentTree: 定義組件樹結(jié)構(gòu)。

只要注冊了組件元信息與組件樹,可視化搭建的畫布就可以渲染出來了,這很好理解。

我們先看組件樹如何定義:

組件樹

組件樹里有各組件的實例,那么最好的設計是,組件樹與組件實例結(jié)構(gòu)是同構(gòu)的,稱為 ComponentInstance - 組件實例:

{
  "componentName": "container",
  "children": [
    {
      "componentName": "text",
      "props": {
        "name": "我是一個文本組件"
      }
    }
  ]
}

上面的結(jié)構(gòu)既可以當做單個組件的 組件實例信息,也可以認為是一個 組件樹,也就是組件樹的任何組件節(jié)點都可以拎出來成為一個新組件樹,這就是同構(gòu)的含義。

我們定義了最最基礎的組件樹結(jié)構(gòu),以后所有功能都基于這三個要素來拓展:

  • componentName: 組件名,描述組件類型,比如是個文本、圖片還是表格。
  • props: 該組件實例的所有配置信息,透傳給組件 props
  • children: 子組件,類型為 ComponentInstance[]。

每一個概念都不可或缺,讓我們從概念必要性再分析一下這三個屬性:

  • componentName: 必須擁有的屬性,否則怎么渲染該節(jié)點都無從談起。所以相應的,我們需要組件元信息來定義每個組件名應該如何渲染。
  • props: 即便是相同組件名的不同實例,也可能擁有不同配置,這些配置放在 props 里足夠了,沒必要開額外的其他屬性存儲各種各樣的業(yè)務配置。
  • children: 理論上可以合并到 props.children,但因為子組件概念太常見,建議 childrenprops.children 這兩種位置同時支持,同時定義時,前者優(yōu)先級更高。

除此之外,還有一個可選屬性 componentId,即組件唯一 ID。我們從可選性與必要性兩個角度分析一下這個屬性:

  • componentId 的可選性:組件實例在 組件樹的路徑 就是天然的組件唯一 ID,比如上面的文本組件的組件唯一 ID 可以認為是 children.0
  • componentId 的必要性:用組件樹路徑代替組件唯一 ID 的壞處是,組件在組件樹上移動后其唯一性就會消失,此時就要用上 componentId 了。

一個好的可視化搭建實現(xiàn)是支持 componentId 的可選性。

組件元信息

接著上面說的,至少要定義一個組件名是如何渲染的,所以組件元信息(ComponentMeta)的必要結(jié)構(gòu)如下:

const textMeta = {
  componentName: "text",
  element: ({ name }) => <span>{name}</span>,
};
  • componentName: 定義哪個組件名的元信息。
  • element: 該組件的渲染函數(shù)。

實現(xiàn)這些最基礎功能后,雖然該可視化搭建器沒有人任何實質(zhì)性的功能,但至少完成了一個核心基礎工作:將組件樹結(jié)構(gòu)的描述與實現(xiàn)分開了。哪怕以后什么功能也不再增加,也永久的改變了開發(fā)模式,我們需要先定義組件元信息,再將其放置在組件樹上。

對于畫板工具軟件,如果不考慮布局等復雜的畫布功能,該結(jié)構(gòu)描述足以完成大部分工作的技術(shù)抽象:配置面板修改組件實例的 props 屬性,甚至布局位置也可以存儲在 props 上。

對于 element 的命名,可能會產(chǎn)生分歧,比如還有其他命名風格如 render、rendererreactNode 等等,但不管叫什么名字,只要是基于 React 響應式定義的,最終應該都殊途同歸,最多對于各類 Key 的名稱定義有所不同,這塊可以保留自己的觀點。

我們繼續(xù)聚焦組件元信息的 element 屬性,看以下 element 代碼:

const divMeta = {
  componentName: "div",
  element: ({ children, header }) => (
    <div>
      {children}
      {header}
    </div>
  ),
};

上面的例子中,我們可以識別出 childrenheader 類型嗎?可以識別一部分:

  • children: 一定是 React 實例,可以是一個或多個組件實例。
  • header: 可能是數(shù)字、字符串,也可能是 React 實例。

props.children 對應了 componentInstance.children 描述,那么如何識別 header 是一個普通對象還是 React 實例呢?

Props 上的 ComponentTreeLike 屬性

ComponentTreeLike 指的是:組件 props 屬性上,識別出 “像組件實例的屬性”,并將其轉(zhuǎn)換為真正的組件實例傳給組件。

假設一個正常的 props.header 值為 "some text",那么組件 props 實際拿到的 props.header 值也是字符串 "some text"

{
  "componentName": "div",
  "props": {
    "header": "some text"
  }
}
const divMeta = {
  componentName: "div",
  element: ({ header }) => (
    <div>
      {header} {/** 字符串 "some text" */}
    </div>
  ),
};

如果將 props.header 寫成類 children 結(jié)構(gòu),可視化搭建框架就會識別為組件實例,將其轉(zhuǎn)化為真正的 React 實例再傳給組件:

{
  "componentName": "div",
  "props": {
    "header": [
      {
        "componentName": "text"
      }
    ]
  }
}
const divMeta = {
  componentName: "div",
  element: ({ header }) => (
    <div>
      {header} {/** React 組件實例,此時會渲染出組件實例 */}
    </div>
  ),
};

這樣設計是基于一個原則:組件樹應該能描述出任何組件想要的 props 屬性。我們反過來站在 element 角度來看,假設你注入了一個 Antd 等框架組件,如果在不改一行源碼的情況下,就希望接入平臺,那平臺必須滿足可配置出任何 props 的能力。除了基礎變量外,更復雜的還有 React 組件實例與函數(shù),現(xiàn)在我們解決了傳組件實例的問題,至于如何傳函數(shù),我們下一小節(jié)再講。

這樣設計存在兩個缺陷:

  • 由于 ComponentTreeLike 會自動轉(zhuǎn)成實例,所以沒有辦法讓組件拿到 ComponentTreeLike 的原始值。
  • 由于 ComponentTreeLike 位置不確定,為了避免深層解析產(chǎn)生的性能損耗,只解析 props 的第一級節(jié)點會導致嵌套層級較深的 ComponentTreeLike 無法被解析到。

如果要解決這兩個缺陷,就需要在組件元信息上定義 Props 的類型,比如:

const divMeta = {
  componentName: "div",
  propsType: {
    header: "element",
    content: ["element"],
    tabs: [
      {
        panel: "element",
      },
    ],
  },
};

解釋一下上面的例子代表的含義:

  • header: 是單個 React Element。
  • content: 是 React Element 數(shù)組。
  • tabs: 是一個數(shù)組結(jié)構(gòu),每一項是對象,其中 panel 是 React Element。

這樣配合以下組件樹的描述,就可以精確的將對應 element 類型轉(zhuǎn)化為組件實例了,而對于基本類型 primitive 保持原樣傳給組件:

{
  "componentName": "div",
  "props": {
    "header": {
      "componentName": "text"
    },
    "names": ["a", "b", "c"],
    "content": [
      {
        "componentName": "text"
      },
      {
        "componentName": "text"
      }
    ],
    "tabs": [
      {
        "title": "tab1",
        "panel": {
          "componentName": "text"
        }
      }
    ]
  }
}

如此一來,沒有定義為 Element 的屬性不會處理成 React 實例,第一個問題就自然解決了。通過配置更深層嵌套的結(jié)構(gòu),第二個問題也自然解決。

componentMeta.propsType 之所以不采用 JSONSchema 結(jié)構(gòu),是因為框架沒必要內(nèi)置對 props 類型校驗的能力,這個能力可以交給業(yè)務層來處理,所以這里就可以采用簡化版結(jié)構(gòu),方便書寫,也容易閱讀。

注意:propsType{} 表示 value 是對象,而 [] 表示 value 是數(shù)組。為數(shù)組時,僅支持單個子元素,因為單項即是對數(shù)組每一項類型的定義。

給組件注入函數(shù)

現(xiàn)在已經(jīng)能給 componentMeta.element 傳入任意基礎類型、React 實例的 props 了,現(xiàn)在還缺函數(shù)類型或者 Set、Map 等復雜類型問題需要解決。

由于組件樹結(jié)構(gòu)需要序列化入庫,所以必須為一個可以序列化的 JSON 結(jié)構(gòu),而這個結(jié)構(gòu)又需要暴露給開發(fā)者,所以也不適合定義一些 hack 的序列化、反序列化規(guī)則。因此要給組件 props 注入函數(shù),需要定義在組件元信息上,由于其定義了額外的 props 屬性,且不在組件樹中,所以我們將其命名為 runtimeProps:

const divMeta = {
  componentName: "div",
  runtimeProps: () => ({
    onClick: () => { console.log('click') }
  })
  element: ({ onClick }) => (
    <button onClick={onClick}>
      點擊我
    </button>
  ),
};

點擊按鈕后,會打印出 click。這是因為 runtimeProps 定義了函數(shù)類型 onClick 在運行時傳入了組件 props。

當組件樹與 componentMeta.runtimeProps 同時定義了同一個 key 時,runtimeProps 優(yōu)先級更高。

總結(jié)

本節(jié)我們介紹了組件注冊與畫布渲染的基礎內(nèi)容,我們再重新梳理一下。

首先定義了 <Designer /> API,并支持傳入 componentTreecomponentMetas,有了組件樹與組件元信息,就可以實現(xiàn)可視化搭建畫布的渲染了。

我們還介紹了如何在組件元信息定義組件的渲染函數(shù),如何給渲染函數(shù) props 傳入基本變量、React 實例以及函數(shù),讓渲染函數(shù)可以對接任何成熟的組件庫,而不需要組件庫做任何適配工作。

但這只是可視化搭建的第一步,在真正開始做項目后,你還會遇到越來越多的問題,比如除了渲染畫布,還要在業(yè)務層定義屬性配置面板、組件拖拽列表、圖層列表、撤銷重做等等功能,這些功能如何拿到畫布屬性?如何與畫布交互?runtimeProps 如何基于項目數(shù)據(jù)流給組件注入不同的屬性或函數(shù)?如何根據(jù)組件 props 的變化動態(tài)注入不同函數(shù)?如何保證注入的函數(shù)引用不變?

要解決這些問題,需要在本章的基礎上實現(xiàn)一套系統(tǒng)的數(shù)據(jù)流規(guī)則以及配套 API,這也是下一講的內(nèi)容,更多關于JS組件注冊畫布渲染的資料請關注腳本之家其它相關文章!

版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享 3.0 許可證

相關文章

  • 微信小程序服務器日期格式化問題

    微信小程序服務器日期格式化問題

    一般服務器獲取到日期都是中國標準時間,需要對其進行格式化,這篇文章主要介紹了微信小程序服務器日期格式化問題,需要的朋友可以參考下
    2020-01-01
  • JavaScript調(diào)用C語言的幾種方式

    JavaScript調(diào)用C語言的幾種方式

    本文主要介紹了JavaScript調(diào)用C語言的幾種方式,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-02-02
  • Bootstrap模態(tài)窗口源碼解析

    Bootstrap模態(tài)窗口源碼解析

    這篇文章主要為大家詳細解析了Bootstrap模態(tài)窗口源碼,基本每行都加了注釋,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • javascript筆記之匿名函數(shù)和閉包

    javascript筆記之匿名函數(shù)和閉包

    這篇文章主要為大家詳細介紹了javascript筆記之匿名函數(shù)和閉包的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Javascript的無new構(gòu)建實例詳解

    Javascript的無new構(gòu)建實例詳解

    這篇文章主要介紹了Javascript的無new構(gòu)建實例詳解的相關資料,小編感覺介紹的非常詳細,具有參考借鑒價值,感興趣的朋友一起學習吧
    2016-05-05
  • javaScript中with函數(shù)用法實例分析

    javaScript中with函數(shù)用法實例分析

    這篇文章主要介紹了javaScript中with函數(shù)用法,實例分析了javascript中with的功能、定義及相關使用技巧,需要的朋友可以參考下
    2015-06-06
  • js實現(xiàn)輪播圖效果 純js實現(xiàn)圖片自動切換

    js實現(xiàn)輪播圖效果 純js實現(xiàn)圖片自動切換

    這篇文章主要為大家詳細介紹了js實現(xiàn)輪播圖效果,圖片自動切換,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-08-08
  • javascript實現(xiàn)自定義滾動條效果

    javascript實現(xiàn)自定義滾動條效果

    這篇文章主要為大家詳細介紹了javascript實現(xiàn)自定義滾動條效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 解決iframe嵌套第三方網(wǎng)址不能訪問的各種報錯

    解決iframe嵌套第三方網(wǎng)址不能訪問的各種報錯

    在一些場景下,我們的網(wǎng)站需要通過iframe標簽嵌入第三方廠家的頁面,這時候就得通過iframe標簽去引入需要嵌入網(wǎng)頁的網(wǎng)址了,這篇文章主要給大家介紹了關于解決iframe嵌套第三方網(wǎng)址不能訪問的各種報錯,需要的朋友可以參考下
    2024-09-09
  • document.compatMode介紹

    document.compatMode介紹

    對于document.compatMode,很多朋友可能都根我一樣很少接觸,知道他的存在卻不清楚他的用途。
    2009-05-05

最新評論