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

JS前端畫布與組件元信息數(shù)據(jù)流示例詳解

 更新時(shí)間:2023年02月06日 14:21:41   作者:黃子毅  
這篇文章主要為大家介紹了JS前端畫布與組件元信息數(shù)據(jù)流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

接下來(lái)需要解決兩個(gè)問(wèn)題:

  • 可視化搭建的其他業(yè)務(wù)元素如何與畫布交互。比如拓展屬性配置面板、圖層列表、拖拽添加組件、定位錨點(diǎn)、主題等等。
  • runtimeProps 如何訪問(wèn)到當(dāng)前組件實(shí)例的 props。

這兩個(gè)問(wèn)題非常重要,而恰好又可以通過(guò)良好的數(shù)據(jù)流設(shè)計(jì)一次性解決,接下來(lái)讓我們分別分析討論一下。

問(wèn)題一:可視化搭建的其他業(yè)務(wù)元素如何與畫布交互。比如拓展屬性配置面板、圖層列表、拖拽添加組件、定位錨點(diǎn)、主題等等

需要設(shè)計(jì)一個(gè) Hooks API,可以訪問(wèn)到畫布提供的方法、數(shù)據(jù)。在 React 設(shè)計(jì)中,訪問(wèn) Hooks API 需要在一定上下文內(nèi),所以可以將 <Designer> 拆為 <Designer><Canvas>,其中 <Designer> 提供 Hooks 上下文,<Canvas> 負(fù)責(zé)渲染畫布。這樣開(kāi)發(fā)者的使用方式就變成了這樣:

import { createDesigner } from 'designer'
const { Designer, Canvas, useDesigner } = createDesigner()
const EditPanel = {
  const { addComponent } = useDesigner()
  return <button onClick={() => addComponent(/** ... */)}>創(chuàng)建組件</button>
}
const App = () => {
  <Designer>
    <Canvas />
    <EditPanel />
  </Designer>
}

為了支持多個(gè) Designer 實(shí)例間隔離,通過(guò) createDesigner 創(chuàng)建一套上下文獨(dú)立的 API,這樣就可以讓畫布、配置面板同時(shí)用 Designer 實(shí)現(xiàn),用一套技術(shù)方案同時(shí)實(shí)現(xiàn)畫布與配置表單,這樣學(xué)習(xí)上下文、組件規(guī)范都可以統(tǒng)一為一套,表單、畫布能力也可以共享。

<Designer> 內(nèi)的組件可以通過(guò) useDesigner 直接訪問(wèn)數(shù)據(jù)與方法,比如上面例子在直接訪問(wèn)內(nèi)置方法 addComponent 時(shí),不需要附加任何參加,而 addComponent 方法也永遠(yuǎn)保持引用不變,此時(shí) useDesigner 不會(huì)導(dǎo)致 EditPanel 重渲染。

如果需要訪問(wèn)當(dāng)前組件樹(shù),并在組件樹(shù)變化時(shí)重渲染,可以通過(guò)如下方式訪問(wèn):

const EditPanel = {
  const { componentTree } = useDesigner(state => ({
    componentTree: state.componentTree
  }))
}

該寫法的效果是,當(dāng) state.componentTree 變化了,會(huì)觸發(fā) EditPanel 重新渲染,并拿到最新值。

同時(shí)也可以傳入第二個(gè)參數(shù) compare 自定義對(duì)比方法,默認(rèn)為 shallowEqual

useDesigner(
  (state) => ({
    componentTree: state.componentTree,
  }),
  isEqual
);

如此一來(lái),無(wú)論給畫布拓展多少 UI 元素都沒(méi)有問(wèn)題,而且 UI 元素可以自由的訪問(wèn)畫布方法與數(shù)據(jù)。

問(wèn)題二:runtimeProps 如何訪問(wèn)到當(dāng)前組件實(shí)例的 props

componentMeta.runtimeProps 中,我們構(gòu)造一個(gè) selector 函數(shù)用于訪問(wèn)當(dāng)前組件 props:

const divMeta = {
  componentName: "div",
  runtimeProps: ({ selector }) => {
    const name = selector(({ props }) => props.name)
    return {
      fullName: `full-${name}`
    }
  }
  element: /** ... */
};

首先支持從 runtimeProps 回調(diào)里拿到 selector,并且該 selector 支持傳入一個(gè)回調(diào)函數(shù),該回調(diào)函數(shù)的參數(shù)中 props 指向當(dāng)前組件實(shí)例的 props,通過(guò)該方法就可以訪問(wèn)組件 props 了。

該 selector 僅在 props.name 改變時(shí)重新執(zhí)行,并且也遵循 compare 對(duì)比規(guī)則,即當(dāng) props.name 變化時(shí),selector 回調(diào)函數(shù)的返回值通過(guò) compare 與上一次值進(jìn)行對(duì)比,如果沒(méi)有變化就返回上一次的舊值,變化了則返回新值。默認(rèn)對(duì)比函數(shù)為 shallowEqual,與 useDesigner 類似,也可以在第二個(gè)參數(shù)位置覆寫 compare 方法。

那組件元信息如何訪問(wèn)內(nèi)置靜態(tài)方法呢?由于靜態(tài)方法引用不變,因此可以在 selector 同級(jí)直接傳入:

const divMeta = {
  componentName: "div",
  runtimeProps: ({ addComponent }) => {
    return {
      add: () => {
        /** addComponent(...) */
      }
    }
  }
  element: /** ... */
};

如此一來(lái),我們就將數(shù)據(jù)流與組件元信息打通了,即 UI 可以通過(guò) useDesigner 訪問(wèn)與操作數(shù)據(jù)流,組件元信息也可以直接拿到方法,或通過(guò) selector 拿到數(shù)據(jù),相應(yīng)的也可以訪問(wèn)與操作數(shù)據(jù)流。這樣的設(shè)計(jì)在以后拓展更多組件元信息函數(shù)時(shí),都可以繼承下來(lái),開(kāi)發(fā)者只要學(xué)習(xí)一次語(yǔ)法,就可以獲得非常強(qiáng)力的拓展性。

拓展應(yīng)用狀態(tài)與靜態(tài)方法

剛才介紹了一些內(nèi)置的狀態(tài)(componentTree)與方法(addComponent),在下一接會(huì)系統(tǒng)介紹筆者梳理了哪些內(nèi)置狀態(tài)與方法。首先拋開(kāi)內(nèi)置狀態(tài)與方法不談,應(yīng)用肯定需要定義自己的狀態(tài)與方法,我們可以提供兩種模式給用戶。

第一種是應(yīng)用的狀態(tài)與方法定義在外部,對(duì)應(yīng)受控模式。

假設(shè)你的應(yīng)用在對(duì)接 Designer 之前就已經(jīng)用 Redux、Dva、Zustand 等狀態(tài)管理庫(kù),那么就可以使用受控模式直接接入:

const App = () => {
  // 偽代碼,不管是 useState 還是其他數(shù)據(jù)流管理狀態(tài),假這里拿到了數(shù)據(jù)與方法
  const { getAppInfo } = useSomeLib();
  const { userName } = useSomeLib("userName");
  return <Designer actions={{ getAppInfo }} state={{ userName }} />;
};

將方法傳給 actions,狀態(tài)傳給 state。

第二種是應(yīng)用的狀態(tài)與方法通過(guò) <Designer> 定義,對(duì)用非受控模式。

假設(shè)你的應(yīng)用之前沒(méi)有使用任何數(shù)據(jù)流,那么也可以直接將 Designer 的數(shù)據(jù)流作為項(xiàng)目數(shù)據(jù)流使用:

import { createMiddleware, createDesigner } from "designer";
const middleware1 = createMiddleware({
  state: { userName: "bob " },
  actions: { getAppInfo: () => {} },
});
const { Designer } = createDesigner(middleware1);
const App = () => {
  return <Designer />;
};

通過(guò) createMiddleware 創(chuàng)建一個(gè)中間件定義狀態(tài)與函數(shù),傳入 createDesigner 即可生效。

也可以在 createMiddleware 里通過(guò)第二個(gè)參數(shù)定義自定義 hooks,或者拿到方法更改 State:

const middleware1 = createMiddleware(
  {
    state: { userName: "bob " },
  },
  ({ setState }) => {
    const setUserName = React.useCallback((newName: string) => {
      setState((state) => ({
        ...state,
        userName: newName,
      }));
    });
    return { setUserName };
  }
);

Designer 內(nèi)部采用最樸素的 Redux 管理狀態(tài),提供了最基礎(chǔ)的 getStatesetState 獲取與修改狀態(tài),基于它們封裝業(yè)務(wù)函數(shù)即可。

無(wú)論是受控模式,還是非受控模式(亦或兩種模式同時(shí)使用),定義的狀態(tài)與方法都可以在以下兩個(gè)位置訪問(wèn),第一個(gè)位置是 useDesigner

const {
  /** 自定義函數(shù) */,
  setUserName,
  /** 自定義函數(shù) */
  getAppInfo,
  /** 內(nèi)置函數(shù) */
  addComponent,
  // 內(nèi)置變量
  componentTree,
  // 自定義變量
  userNamee
} = useDesigner(state => ({
  componentTree: state.componentTree,
  userName: state.userName
}))

第二個(gè)位置是組件元信息上的回調(diào)函數(shù),比如 runtimeProps

const divMeta = {
  componentName: "div",
  runtimeProps: ({
    selector,
    /** 自定義函數(shù) */,
    setUserName,
    /** 自定義函數(shù) */
    getAppInfo,
    /** 內(nèi)置函數(shù) */
    addComponent
   }) => {
    const {
      /** 內(nèi)置變量 */
      componentTree,
      /** 自定義變量 */
      userName
    } = selector(({ state }) => ({
      componentTree: state.componentTree,
      userName: state.userName
    }))
    return { componentTree, userName }
  }
  element: /** ... */
};

至此,我們實(shí)現(xiàn)了一套完整的數(shù)據(jù)流定義,包括:

  • 不同 Designer 之間上下文隔離。
  • 可無(wú)縫對(duì)接項(xiàng)目數(shù)據(jù)流,也可作為獨(dú)立數(shù)據(jù)流方案提供。
  • 內(nèi)置變量與函數(shù)與自定義變量、函數(shù)混合。
  • 無(wú)論在 UI 通過(guò) useDesigner,還是在組件元信息通過(guò) selector 都可訪問(wèn)這些變量與函數(shù)。

總結(jié)

一個(gè)基本可用的可視化搭建框架在本章就算設(shè)計(jì)完了。但這只是可視化搭建問(wèn)題的冰山一角,未來(lái)的章節(jié),筆者會(huì)逐漸為大家介紹更多可視化搭建的設(shè)計(jì)。

但無(wú)論框架未來(lái)怎么發(fā)展,也永遠(yuǎn)會(huì)基于這前三章的基本設(shè)定,總結(jié)一下,這三章的基本設(shè)定就是:設(shè)計(jì)一個(gè)邏輯與 UI 分離的可視化搭建協(xié)議,數(shù)據(jù)流、組件元信息、組件實(shí)例是永遠(yuǎn)的鐵三角,數(shù)據(jù)流可以對(duì)接任意已存在的實(shí)現(xiàn),或基于 Designer 規(guī)范實(shí)現(xiàn),組件元信息與組件實(shí)例僅存儲(chǔ)最基本信息,得益于數(shù)據(jù)流的自定義能力,以及無(wú)論何處都有完全的數(shù)據(jù)流訪問(wèn)能力,使業(yè)務(wù)框架既遵循規(guī)則,又可以千變?nèi)f化。

拋開(kāi)具體 API 設(shè)計(jì)或者命名不談,一個(gè)有簡(jiǎn)潔、抽象,又提供極少量 API 卻能滿足所有業(yè)務(wù)定制訴求,是可視化搭建永遠(yuǎn)追求的目標(biāo)。只要熟悉了這套規(guī)范,就可以幾乎僅根據(jù)業(yè)務(wù)表現(xiàn),一眼猜出是基于哪些 API 封裝實(shí)現(xiàn)的,那么維護(hù)成本與理解成本將大大降低,規(guī)范的意義就體現(xiàn)在這里。

也許有同學(xué)會(huì)覺(jué)得,現(xiàn)在各個(gè)大廠都有無(wú)數(shù)可視化搭建的實(shí)現(xiàn),可視化搭建概念都已經(jīng)爛大街了,為什么還要重新設(shè)計(jì)一個(gè)呢?

因?yàn)橐苍S數(shù)量不代表質(zhì)量,維護(hù)的時(shí)間越久,參與的同學(xué)越多,越容易使設(shè)計(jì)變得冗余,概念變得復(fù)雜,要對(duì)抗這些遞增的熵,唯有不斷重新設(shè)計(jì),從零開(kāi)始反思方案。

下一講理論思考會(huì)少一些,介紹可視化搭建框架會(huì)考慮內(nèi)置哪些變量與方法,更多關(guān)于JS畫布與組件元信息數(shù)據(jù)流的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

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

相關(guān)文章

  • JS中輕松遍歷對(duì)象屬性的幾種方式

    JS中輕松遍歷對(duì)象屬性的幾種方式

    這篇文章主要給大家介紹的是JS中輕松遍歷對(duì)象屬性的幾種方式,文章從自身可枚舉屬性、Object.values() 返回屬性值、Object.entries()來(lái)展開(kāi)介紹,感興趣的小伙伴可以參考一下
    2021-09-09
  • 微信小程序(應(yīng)用號(hào))開(kāi)發(fā)新聞客戶端實(shí)例

    微信小程序(應(yīng)用號(hào))開(kāi)發(fā)新聞客戶端實(shí)例

    這篇文章主要介紹了微信小程序(應(yīng)用號(hào))開(kāi)發(fā)新聞客戶端實(shí)例的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • 一篇文章教你學(xué)會(huì)js實(shí)現(xiàn)彈幕效果

    一篇文章教你學(xué)會(huì)js實(shí)現(xiàn)彈幕效果

    彈幕效果隨著b站的越做越強(qiáng),出現(xiàn)了越來(lái)越多的仿照b站的視頻站點(diǎn)。然而這些視頻站仿照的最多的只有一點(diǎn)!那就是彈幕,現(xiàn)在也越來(lái)越多的人喜歡上了彈幕本文就教你如何制作
    2021-08-08
  • 微信小程序(五)頁(yè)面生命周期詳細(xì)介紹

    微信小程序(五)頁(yè)面生命周期詳細(xì)介紹

    這篇文章主要介紹了 微信小程序頁(yè)面生命周期的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • 帶你理解JavaScript 原型原型鏈

    帶你理解JavaScript 原型原型鏈

    理解js中原型、原型鏈這個(gè)概念,絕對(duì)是幫助我們更深入學(xué)習(xí)js的必要一步,比如,如果js開(kāi)發(fā)者想理解js繼承,new關(guān)鍵字原理,甚至封裝組件、優(yōu)化代碼,弄明白js中原型、原型鏈更是前提條件。本篇文章,用最簡(jiǎn)潔的文字,清楚明白講解原型鏈相等關(guān)系和原型、原型鏈存在的意義
    2021-09-09
  • 微信小程序 下拉菜單的實(shí)現(xiàn)

    微信小程序 下拉菜單的實(shí)現(xiàn)

    這篇文章主要介紹了微信小程序 下拉菜單的實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下
    2017-04-04
  • 微信小程序 解析網(wǎng)頁(yè)內(nèi)容詳解及實(shí)例

    微信小程序 解析網(wǎng)頁(yè)內(nèi)容詳解及實(shí)例

    這篇文章主要介紹了微信小程序 解析網(wǎng)頁(yè)內(nèi)容詳解及實(shí)例的相關(guān)資料,這里使用爬蟲對(duì)復(fù)雜的網(wǎng)頁(yè)進(jìn)行抓取,遇到些問(wèn)題,這里整理下并解決,需要的朋友可以參考下
    2017-02-02
  • ajax與302響應(yīng)代碼測(cè)試

    ajax與302響應(yīng)代碼測(cè)試

    服務(wù)器端的響應(yīng)是302 Found,在ajax的回調(diào)函數(shù)中能夠獲取這個(gè)狀態(tài)碼嗎?能夠從Response Headers中得到Location的值進(jìn)行重定向嗎?讓我們來(lái)一起動(dòng)手寫寫代碼看看實(shí)際情況吧。
    2013-10-10
  • 一文詳解Electron?快捷鍵使用技巧及示例

    一文詳解Electron?快捷鍵使用技巧及示例

    這篇文章主要為大家介紹了Electron?中的快捷鍵使用技巧及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • JS前端輕量fabric.js系列之畫布初始化

    JS前端輕量fabric.js系列之畫布初始化

    這篇文章主要為大家介紹了JS前端輕量fabric.js系列之畫布初始化示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08

最新評(píng)論