最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS前端組件注冊(cè)與畫(huà)布渲染實(shí)例

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

引言

接著可視化搭建的理論抽象,我們開(kāi)始勾勒一個(gè)具體的 React 可視化搭建器。

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

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

只要注冊(cè)了組件元信息與組件樹(shù),可視化搭建的畫(huà)布就可以渲染出來(lái)了,這很好理解。

我們先看組件樹(shù)如何定義:

組件樹(shù)

組件樹(shù)里有各組件的實(shí)例,那么最好的設(shè)計(jì)是,組件樹(shù)與組件實(shí)例結(jié)構(gòu)是同構(gòu)的,稱(chēng)為 ComponentInstance - 組件實(shí)例:

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

上面的結(jié)構(gòu)既可以當(dāng)做單個(gè)組件的 組件實(shí)例信息,也可以認(rèn)為是一個(gè) 組件樹(shù),也就是組件樹(shù)的任何組件節(jié)點(diǎn)都可以拎出來(lái)成為一個(gè)新組件樹(shù),這就是同構(gòu)的含義。

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

  • componentName: 組件名,描述組件類(lèi)型,比如是個(gè)文本、圖片還是表格。
  • props: 該組件實(shí)例的所有配置信息,透?jìng)鹘o組件 props。
  • children: 子組件,類(lèi)型為 ComponentInstance[]。

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

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

除此之外,還有一個(gè)可選屬性 componentId,即組件唯一 ID。我們從可選性與必要性?xún)蓚€(gè)角度分析一下這個(gè)屬性:

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

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

組件元信息

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

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

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

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

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

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

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

上面的例子中,我們可以識(shí)別出 childrenheader 類(lèi)型嗎?可以識(shí)別一部分:

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

props.children 對(duì)應(yīng)了 componentInstance.children 描述,那么如何識(shí)別 header 是一個(gè)普通對(duì)象還是 React 實(shí)例呢?

Props 上的 ComponentTreeLike 屬性

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

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

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

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

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

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

這樣設(shè)計(jì)存在兩個(gè)缺陷:

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

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

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

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

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

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

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

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

componentMeta.propsType 之所以不采用 JSONSchema 結(jié)構(gòu),是因?yàn)榭蚣軟](méi)必要內(nèi)置對(duì) props 類(lèi)型校驗(yàn)的能力,這個(gè)能力可以交給業(yè)務(wù)層來(lái)處理,所以這里就可以采用簡(jiǎn)化版結(jié)構(gòu),方便書(shū)寫(xiě),也容易閱讀。

注意:propsType{} 表示 value 是對(duì)象,而 [] 表示 value 是數(shù)組。為數(shù)組時(shí),僅支持單個(gè)子元素,因?yàn)閱雾?xiàng)即是對(duì)數(shù)組每一項(xiàng)類(lèi)型的定義。

給組件注入函數(shù)

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

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

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

點(diǎn)擊按鈕后,會(huì)打印出 click。這是因?yàn)?runtimeProps 定義了函數(shù)類(lèi)型 onClick 在運(yùn)行時(shí)傳入了組件 props。

當(dāng)組件樹(shù)與 componentMeta.runtimeProps 同時(shí)定義了同一個(gè) key 時(shí),runtimeProps 優(yōu)先級(jí)更高。

總結(jié)

本節(jié)我們介紹了組件注冊(cè)與畫(huà)布渲染的基礎(chǔ)內(nèi)容,我們?cè)僦匦率崂硪幌隆?/p>

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

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

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

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

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

相關(guān)文章

  • 你可能不知道的typescript實(shí)用小技巧

    你可能不知道的typescript實(shí)用小技巧

    作為前端程序員,TS已經(jīng)成為一項(xiàng)必不可少的技能,本文旨在介紹 TS中的一些實(shí)用技巧,提高大家對(duì)這門(mén)語(yǔ)言更深的認(rèn)知,這篇文章主要給大家介紹了關(guān)于typescript實(shí)用小技巧的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • js代碼運(yùn)行報(bào)錯(cuò)Warning:To?load?an?ES?module,?set?"type":"module"in?the?package.json?or?use?the?.mjs

    js代碼運(yùn)行報(bào)錯(cuò)Warning:To?load?an?ES?module,?set?"type&q

    最近在學(xué)習(xí)ES6的過(guò)程中,和運(yùn)行javascript文件時(shí)進(jìn)行了報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于js代碼運(yùn)行報(bào)錯(cuò)Warning:To?load?an?ES?module,?set?"type":"module"in?the?package.json?or?use?the?.mjs的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • HTML5游戲引擎LTweenLite實(shí)現(xiàn)的超帥動(dòng)畫(huà)效果(附demo源碼下載)

    HTML5游戲引擎LTweenLite實(shí)現(xiàn)的超帥動(dòng)畫(huà)效果(附demo源碼下載)

    這篇文章主要介紹了HTML5游戲引擎LTweenLite實(shí)現(xiàn)的超帥動(dòng)畫(huà)效果,詳細(xì)分析了LTweenLite的下載,動(dòng)畫(huà)效果的實(shí)現(xiàn)步驟,并附帶完整的demo實(shí)例源碼供讀者下載,需要的朋友可以參考下
    2016-01-01
  • JavaScript實(shí)現(xiàn)數(shù)字格式化的示例詳解

    JavaScript實(shí)現(xiàn)數(shù)字格式化的示例詳解

    在處理數(shù)字時(shí),為了提高可讀性,我們通常會(huì)將整數(shù)部分的數(shù)字以千分位或百分位分隔,本文將詳細(xì)介紹如何使用JavaScript來(lái)實(shí)現(xiàn)數(shù)字的千分位和百分位分隔,需要的可以參考下
    2024-04-04
  • JavaScript對(duì)象的淺拷貝與深拷貝實(shí)例分析

    JavaScript對(duì)象的淺拷貝與深拷貝實(shí)例分析

    這篇文章主要介紹了JavaScript對(duì)象的淺拷貝與深拷貝,結(jié)合實(shí)例形式分析了javascript淺拷貝與深拷貝的原理、實(shí)現(xiàn)方法與相關(guān)操作技巧,需要的朋友可以參考下
    2018-07-07
  • 詳解JavaScript的內(nèi)置對(duì)象

    詳解JavaScript的內(nèi)置對(duì)象

    本文對(duì)JavaScript對(duì)象的屬性、對(duì)象的方法、對(duì)象的屬性、訪(fǎng)問(wèn)對(duì)象屬性的語(yǔ)法進(jìn)行了實(shí)例分析介紹,有需要的朋友可以看下
    2016-12-12
  • js時(shí)間日期和毫秒的相互轉(zhuǎn)換

    js時(shí)間日期和毫秒的相互轉(zhuǎn)換

    js時(shí)間日期和毫秒的相互轉(zhuǎn)換,需要的朋友可以參考一下
    2013-02-02
  • javaScript實(shí)現(xiàn)可縮放的顯示區(qū)效果代碼

    javaScript實(shí)現(xiàn)可縮放的顯示區(qū)效果代碼

    這篇文章主要介紹了javaScript實(shí)現(xiàn)可縮放的顯示區(qū)效果代碼,涉及JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)操作頁(yè)面元素屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-10-10
  • 詳解ES6中class的實(shí)現(xiàn)原理

    詳解ES6中class的實(shí)現(xiàn)原理

    這篇文章主要介紹了詳解ES6中class的實(shí)現(xiàn)原理,幫助大家更好的理解和學(xué)習(xí)JavaScript es6,感興趣的朋友可以了解下
    2020-10-10
  • js程序中美元符號(hào)$是什么

    js程序中美元符號(hào)$是什么

    $符號(hào)在php中是表示變量的特征字符, 在js中它也有很多作用, 一般我們用來(lái)命名一個(gè)函數(shù)名稱(chēng),獲取id的
    2008-06-06

最新評(píng)論

丁青县| 琼中| 历史| 易门县| 鸡东县| 钦州市| 宝兴县| 泰州市| 和硕县| 丰宁| 曲阳县| 赫章县| 洱源县| 洞头县| 郸城县| 合水县| 津市市| 商丘市| 永寿县| 富锦市| 黄骅市| 吉水县| 自治县| 沧州市| 喀什市| 清徐县| 门源| 五指山市| 锡林郭勒盟| 金川县| 承德县| 岑溪市| 墨脱县| 新蔡县| 阿尔山市| 陵水| 光泽县| 枣庄市| 如东县| 巨鹿县| 定结县|