溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JS前端組件設(shè)計(jì)以業(yè)務(wù)為導(dǎo)向案例分析

發(fā)布時(shí)間:2023-03-13 11:27:44 來(lái)源:億速云 閱讀:121 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“JS前端組件設(shè)計(jì)以業(yè)務(wù)為導(dǎo)向案例分析”的相關(guān)知識(shí),小編通過實(shí)際案例向大家展示操作過程,操作方法簡(jiǎn)單快捷,實(shí)用性強(qiáng),希望這篇“JS前端組件設(shè)計(jì)以業(yè)務(wù)為導(dǎo)向案例分析”文章能幫助大家解決問題。

一個(gè)組件設(shè)計(jì)的例子

考慮一個(gè)業(yè)務(wù)場(chǎng)景。一個(gè)表單組件,用來(lái)采集用戶的基本信息,每個(gè)甲方爸爸都有自己的定制需求。比如樣式、比如要展示的控件種類。分析表單控件類型,假設(shè)分別可能有文本輸入框、單選框、多選框、下拉框等等。

我們很容易就能想到用動(dòng)態(tài)表單來(lái)實(shí)現(xiàn),支持自定義控件類型,支持自定義表單元素的樣式和行為。api設(shè)計(jì)為了易于擴(kuò)展,泛型顯然更具可重用性。

一個(gè)基本的表單類型像這樣:

type FieldProps<T> = {
  label: string;
  name: string;
  value: T;
  onChange: (name: string, value: T) => void;
};

創(chuàng)建一個(gè)接口,表示 Field 組件可以渲染的不同類型的表單元素:

interface FieldRenderer<T> {
  (props: FieldProps<T>): JSX.Element;
}

接著,我們可以創(chuàng)建一個(gè)泛型的 Field 組件,根據(jù)傳入的泛型類型 T,來(lái)確定 Field 組件要渲染的表單元素類型以及 props 的類型。

function Field<T>({ label, name, value, onChange, render }: FieldProps<T> & { render: FieldRenderer<T> }) {
  return (
    <div className="field">
      <label htmlFor={name}>{label}</label>
      {render({ label, name, value, onChange })}
    </div>
  );
}

創(chuàng)建一些不同的 Field 組件渲染器,比如 TextInput、SelectInput、CheckboxInput。

const TextInput: FieldRenderer<string> = ({ label, name, value, onChange }) => (
  <input type="text" id={name} name={name} value={value} onChange={(e) => onChange(name, e.target.value)} />
);
const SelectInput: FieldRenderer<string> = ({ label, name, value, options, onChange }) => (
  <select id={name} name={name} value={value} onChange={(e) => onChange(name, e.target.value)}>
    {options.map(option=><option value={option.value}>{option.label}</option>)}
  </select>
);

現(xiàn)在,我們可以在使用 Field 組件的時(shí)候,傳入不同的泛型類型,來(lái)渲染不同的表單元素了。

const DynamicForm = ({ fields, onSubmit }: { fields: FormField[], onSubmit: ()=>void }) => {
  //這里做一些映射之類的。。
  return (
    <form onSubmit={onSubmit}>
      {fields.map(field=><Field {...field} render={field.customComponent} />)}
    </form>
  )
}

以上,我們基本完成一個(gè)高度抽象化的組件設(shè)計(jì)。

現(xiàn)在,讓我們的目光從這個(gè)細(xì)節(jié)上挪開,切換到一個(gè)宏觀的視角上,重新審視項(xiàng)目整體架構(gòu)的組件設(shè)計(jì)。一個(gè)前端架構(gòu)通常會(huì)有其系統(tǒng)規(guī)范,比如統(tǒng)一的命名規(guī)范、代碼風(fēng)格,合理的文件組織結(jié)構(gòu),前端開發(fā)的基礎(chǔ)設(shè)施,性能優(yōu)化方案,依賴管理等。那么,我們?nèi)绾卧谶@個(gè)規(guī)范的框架下設(shè)計(jì)組件呢?

模塊化設(shè)計(jì)原則

從整體角度規(guī)劃方案,我們可以對(duì)項(xiàng)目進(jìn)行分層和模塊化的設(shè)計(jì),實(shí)現(xiàn)不同模塊之間的解耦合。

分層設(shè)計(jì)是指將整個(gè)系統(tǒng)分成分層模塊,每一層模塊都有自己的職責(zé)和功能。前端的分層設(shè)計(jì)主要涉及以下幾層:展示層、控制層、邏輯層、服務(wù)層。模塊化設(shè)計(jì)是指將整個(gè)系統(tǒng)分成小的模塊,每個(gè)模塊都有自己的功能,不同模塊之間通過明確的接口進(jìn)行通信和數(shù)據(jù)交換。在前端項(xiàng)目中,往往可以將不同的功能分配到不同的模塊中,甚至可以將某些通用的功能寫成獨(dú)立的模塊進(jìn)行引入。

在實(shí)際業(yè)務(wù)中,我們通常需要將分層設(shè)計(jì)與模塊化設(shè)計(jì)相結(jié)合。在不同的層次上實(shí)現(xiàn)代碼結(jié)構(gòu)的劃分和內(nèi)部邏輯的編寫,不同的功能分配到不同的模塊,通用的功能寫成獨(dú)立的模塊引入,將代碼封裝成可復(fù)用的單元。

業(yè)務(wù)組件的設(shè)計(jì)因素

業(yè)務(wù)組件是在實(shí)現(xiàn)業(yè)務(wù)過程中抽象出來(lái)的組件,作用是在應(yīng)用中復(fù)用業(yè)務(wù)邏輯。我們應(yīng)該進(jìn)行怎樣的抽象?簡(jiǎn)單的功能如果抽象成組件,是否是一種過度設(shè)計(jì)?我們嘗試從以下幾個(gè)角度來(lái)思考這些問題。

1. 狀態(tài)與接口

在設(shè)計(jì)接口時(shí),我們都知道,接口應(yīng)該簡(jiǎn)單清晰、易于擴(kuò)展。比如一個(gè)loading flag, 我們通常會(huì)用布爾值來(lái)切換loading狀態(tài),分別展示不同的UI界面。

比如一個(gè)發(fā)送驗(yàn)證碼的按鈕,我們可能用isEnd就能滿足展示不同按鈕文字的需求。但如果,我們分別需要在點(diǎn)擊按鈕前、點(diǎn)擊后的倒計(jì)時(shí)階段、倒計(jì)時(shí)進(jìn)入到指定的時(shí)刻、倒計(jì)時(shí)結(jié)束后執(zhí)行不同的邏輯。那么,我們就需要考慮將這個(gè)接口設(shè)計(jì)成字符串,以便于擴(kuò)展。

同理,一些使用loading布爾值的場(chǎng)景,是否可以考慮設(shè)計(jì)為字符串,以便滿足更加個(gè)性化的需求?站在用戶的角度,你是否已經(jīng)厭倦了在等待一份大體積的數(shù)據(jù)時(shí)看著一個(gè)動(dòng)畫圈圈在轉(zhuǎn)動(dòng)?

現(xiàn)在,我們來(lái)考慮組件的使用場(chǎng)景。

這個(gè)組件是否與業(yè)務(wù)邏輯綁定?比如一個(gè)登錄功能,可以是彈窗、也可以是單獨(dú)的頁(yè)面,它往往帶有以下功能:用戶名和密碼的前端校驗(yàn)規(guī)則、對(duì)后端響應(yīng)的處理、完成登錄后的邏輯處理。像這樣的組件,就不需要抽象,因?yàn)樗y以通過修改參數(shù)就直接在其它系統(tǒng)中使用。

這個(gè)組件的功能有可能被重用嗎?如果是,我們?nèi)绾巫鲱A(yù)先的接口設(shè)計(jì)?比如一個(gè)表格組件,通常包含以下狀態(tài):要展示的數(shù)據(jù)、對(duì)數(shù)據(jù)的排序規(guī)則、數(shù)據(jù)過濾規(guī)則、用戶選擇器。我們初期可能據(jù)此做了4個(gè)接口:數(shù)據(jù)、排序、過濾、選擇器。隨著業(yè)務(wù)的擴(kuò)展,我們可以預(yù)見后期的數(shù)據(jù)量開始加大,我們可能考慮增加一個(gè)分頁(yè)接口。但是分頁(yè)接口是否真的需要被集成在這個(gè)表格組件中?這是一個(gè)開放性的問題,相信不同的CRUD專家會(huì)有不同的解決方案。

2. 調(diào)用方式

組件的調(diào)用方式有多種,比如在模版文件中引入組件標(biāo)簽直接調(diào)用,或通過函數(shù)調(diào)用(常見的message/loading類組件),或者通過接口調(diào)用(Ant Design的DatePicker)等。并沒有一套通用的標(biāo)準(zhǔn)來(lái)指定某種類型的組件的調(diào)用方式,總體還是取決于項(xiàng)目的需求和場(chǎng)景。

3. 測(cè)試

假設(shè)一個(gè)組件需要訪問api,這很常見。我們應(yīng)該如何設(shè)計(jì)以便于在測(cè)試組件時(shí)隔離組件的功能?

import APIService from './APIService';
function MyComponent({ apiService }) {
  const fetchData = () => {
    const data = apiService.get('/data');
    // 處理數(shù)據(jù)并返回結(jié)果
  }
  return <>{/* 渲染組件的內(nèi)容 */}</>
}
// 渲染組件時(shí),可以將 APIService 實(shí)例作為 props 傳遞
const apiService = new APIService();
ReactDOM.render(<MyComponent apiService={apiService} />, document.getElementById('root'));

在組件內(nèi)部,我們定義了一個(gè) fetchData 函數(shù),它可以在需要的時(shí)候調(diào)用 apiService.get() 來(lái)獲取數(shù)據(jù)。此時(shí),我們可以輕松地模擬 apiService,以進(jìn)行單元測(cè)試,而不會(huì)對(duì) MyComponent 的實(shí)現(xiàn)產(chǎn)生任何影響。

大多數(shù)時(shí)候,一個(gè)單一職責(zé)的組件的功能測(cè)試,是要比復(fù)合組件更容易的。使用標(biāo)準(zhǔn)和通用的API和數(shù)據(jù)格式,并將組件的功能和狀態(tài)限制在組件內(nèi)部,確保組件可以獨(dú)立地進(jìn)行測(cè)試。此外,我們還要考慮邊界測(cè)試,比如組件接收到無(wú)效的或非預(yù)期的參數(shù)該如何處理?

當(dāng)然,組件不是顆粒度越細(xì)越好。是否遵循單一職責(zé)原則,不應(yīng)該以功能點(diǎn)的數(shù)量,而是以功能和目標(biāo)來(lái)衡量。

4. 文檔

編寫一份前端組件設(shè)計(jì)文檔,明確記錄項(xiàng)目的設(shè)計(jì)標(biāo)準(zhǔn)和項(xiàng)目迭代過程中的變更,創(chuàng)建標(biāo)準(zhǔn)的組件庫(kù)以便于多人協(xié)作時(shí)的組件復(fù)用。尤其在一個(gè)多人協(xié)作的項(xiàng)目里,文檔能夠提供統(tǒng)一的開發(fā)規(guī)范,使得不同的開發(fā)人員在編寫不同的代碼時(shí)能保持一致的開發(fā)習(xí)慣。

分析業(yè)務(wù)邏輯

我們已經(jīng)有了設(shè)計(jì)的原則和需要考慮的因素,下面我們開始分析業(yè)務(wù)邏輯。

通常我們會(huì)有原型圖展示各個(gè)業(yè)務(wù)操作的流程和業(yè)務(wù)環(huán)節(jié)的邏輯關(guān)系,在設(shè)計(jì)組件時(shí)我們需要考慮如何支持這些流程和操作。比如根據(jù)業(yè)務(wù)邏輯,將整個(gè)項(xiàng)目分解為多個(gè)獨(dú)立的組件,劃分組件的接口和參數(shù),指定其數(shù)據(jù)類型等。

此外,我們還要分析各種數(shù)據(jù)的處理和存儲(chǔ)方式。這里我們只討論前端的管理方式。比如一個(gè)給定的系統(tǒng),我們通常會(huì)有用戶數(shù)據(jù)、產(chǎn)品數(shù)據(jù)、訂單數(shù)據(jù)等。我們可能會(huì)考慮使用狀態(tài)管理工具、Context API、組件間的通信、本地存儲(chǔ)等方式來(lái)管理這些數(shù)據(jù)。不同的場(chǎng)景需要采用不同的數(shù)據(jù)管理方法。

最后,我們要分析用戶交互的影響,考慮控制用戶的交互范圍。比如表單校驗(yàn)、提高反饋信息和錯(cuò)誤處理。

關(guān)于“JS前端組件設(shè)計(jì)以業(yè)務(wù)為導(dǎo)向案例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí),可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會(huì)為大家更新不同的知識(shí)點(diǎn)。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

js
AI