做者:我想寫文章啊
html
來源:https://juejin.im/post/6878099828497186823前端
1、寫在前面
現今的web開發經過先後端分離的技術拆分爲了web後端開發與web前端開發,值得指出的是,web前端開發早已不是傳統意義上的開發模式了,轉而變成了web客戶端開發,有過客戶端開發經驗的同窗應該知道這二者間的差異,客戶端開發關注的是:vue
-
應用的生命週期 -
組件化 -
開發模式與打包方法
組件化是客戶端開發最重要的內容,設計一套複用度高、擴展性好的組件系統,能夠顯著提升開發效率,而且能夠減小後期的維護成本。react
2、一個筆記組件的設計案例

就以我正在使用的筆記app爲例,上圖展現的筆記的閱讀與書寫區域,如何將這個區域抽象爲一個組件呢?讓咱們一步一步來分析。web
1. 最簡api
咱們爲該組件取個名字(取名很重要),就叫Note吧。無論是在閱讀狀態仍是在編輯狀態,該組件都要展現筆記的內容,由於筆記對象應該經過組件的接口傳入進來,由於咱們爲該組件設計第一個api:後端
屬性 | 說明 | 類型 | 是否必填 |
---|---|---|---|
data | 筆記對象數據 | object | 是 |
接下來,咱們簡單使用一下這個組件:api
爲了兼容vue與react的讀者,本頁面均使用JSX語法數組
const note = {
title: 如何製做一個組件?.md ,
content:
}
這樣,一個最簡api的筆記組件就搞定了,它的接口很是簡單,只須要提供一個data
屬性,就能夠展現出筆記內容,而且能夠點擊編輯進入書寫狀態。微信
通常而言,若是沒有更多的需求的話,咱們的筆記組件設計到這裏也就能夠了。**在設計組件時,務必遵循最小化原則,即儘量少地拋出接口。**由於使用組件的用戶可能有不少,一旦組件做者不當心拋出了一個不合理的接口,之後想要修改就幾乎不可能了(只能經過標記過期的方法提醒用戶,但這種作法每每是無奈之舉)。markdown
2. 知足數據獲取的多種狀況
如今,組件的使用者已經能夠經過很簡潔的api使用這個筆記組件了,可是如今問題來了:有的組件使用者只拿到了筆記的id,想要經過直接傳入id的方式使用組件。
此時,做爲組件做者,咱們評估了這個需求是合理的,因而,咱們擴展了筆記組件的api:
屬性 | 說明 | 類型 | 是否必填 | 默認值 |
---|---|---|---|---|
data | 筆記對象 | object | 否 | null |
dataId | 筆記對象id | string | 否 | null |
如今能夠經過傳入id的方式來使用組件了:
const noteId = 123
<Note dataId={noteId} />
請注意,api中的兩個屬性都是非必填的,由於不知道用戶會傳入哪一個屬性,爲了程序的嚴謹性,組件內部應當校驗兩個參數都不傳的狀況,並經過拋出錯誤告訴調用者。
這是組件設計的一個技巧,經過支持多種數據源使得調用更加簡單。可是這種設計也有其弊端所在,若是這種兼容性的擴展過多會使得組件的內部邏輯變得複雜,也會使得api變得難於理解,所以,對於兼容性的api擴展要謹慎,不可過量。
3. 兼容不一樣模式
組件的使用一如既往的優雅、簡單,可是如今又有用戶提出新的需求了:由於該組件是支持閱讀與編輯兩種模式的,在使用時,對於他人的筆記是不可編輯的,可否在指定的場景下只支持一種閱讀模式?
筆記組件因爲內部支持了兩種模式,既支持閱讀,也支持編輯。所以調用者只想使用一種模式也是合理的。因而,咱們繼續擴展組件的api:
屬性 | 說明 | 類型 | 是否必填 | 默認值 |
---|---|---|---|---|
mode | 模式,數組的第一項做爲初始模式,該參數不可爲空數組 | array | 否 | [ write , read ] |
如今,對於只想使用閱讀模式的用戶,能夠這麼調用:
const note = {}
const mode = [ read ]
<Note data={note} mode={mode} />
在設計api時,咱們在知足需求的前提下,支持了更多狀況。首先,使用者也可能只使用編輯模式,由於mode參數是支持隨意組合各類模式的,所以這種狀況也能知足。另外,若是組件之後擴展了更多模式,該api仍然能知足需求,只須要爲mode數組增長更多的模式項便可。這裏有一個更佳的設計是,當使用多個模式時,肯定哪一個模式做爲初始模式也是有必要的,所以,將mode數組的第一項做爲多模式下的初始模式,既知足了需求,又達到了api設計最小化的原則。
如今,咱們對用戶的需求進行了擴展,不只支持只使用閱讀模式,還支持各類模式任意組合和初始模式,可是這還不夠,組件的設計者應當針對需求想到更長遠的狀況,針對這個例子,咱們還能夠爲組件擴展一個模式改變的事件,讓調用者能夠捕捉到筆記組件從閱讀 -> 編輯或編輯 -> 閱讀(隨着模式的擴展,這種組合會更多)切換的時機:
事件 | 說明 | 回調參數 |
---|---|---|
modeChange | 模式切換時觸發 | (from: string, to: string) from表示切換前的模式,to表示切換後的模式 |
調用者可能在捕捉到模式切換事件時,作一些特定的工做:
function handleModeChange(from, to) {
// ...
}
<Note onModeChange={handleModeChange} />
4. 更多的支持
在編輯器中編輯筆記是html或markdown類型的,筆記組件支持將筆記導出爲一個PDF文檔。所以,設計時咱們能夠將組件的一些能力抽象爲api,再次擴展組件的api:
方法 | 說明 | 參數 |
---|---|---|
exportPDF | 導出筆記爲PDF文件 | - |
toggleFullscreen | 切換全屏顯示 | (value: boolean) 是否全屏展現 |
組件設計時,咱們能夠將可預見範圍內的組件的能力設計爲api,須要注意的,方法的參數與返回值也是api的一部分,應當謹慎設計。
除了擴展組件的能力外,咱們還能夠擴展組件的視圖。注意到閱讀
按鈕右側的工具欄了嗎?咱們假設這部分的視圖不屬於筆記組件,是經過api擴展而渲染出來的,這就是組件的子視圖設計,在web前端的組件化中,稱爲插槽。咱們能夠爲筆記組件擴展一個工具欄的插槽:
插槽 | 說明 | 參數 |
---|---|---|
toolbar | 工具欄子視圖 | { data } |
當調用者想要擴展筆記組件的工具欄時,能夠這麼使用:
const note = {}
<Note data={note}>
<MyToolbar />
</Note>
這樣,調用者就能夠根據本身的需求,在工具欄渲染本身想要的內容了。
3、組件設計四要素
上述案例講述了組件設計的整個流程,經過分析用戶的需求(或將來可能出現的需求),一步一步地設計出了一個複用度高、擴展性好的組件。若是你是一個組件設計的新手,你應該如何去思考、去設計一個優良的組件呢?
1. 先設計,後實現
咱們通篇在討論組件的設計,可是實際操做時,不少朋友會經過邊實現邊設計的方式來完成一個組件的製做,這是不合理的,由於自身能力與眼界的限制,實現可能會干擾你的設計,對於如下兩個經典矛盾,但願讀者能選擇後者,以追求合理性爲重。
這樣實現比較方便,不如將這個參數拋出讓用戶傳進來吧! 這樣設計比較合理,雖然實現難度可能會比較高,但我能夠經過文檔學習、求問他人的方式來實現它,或者直接讓他人來實現。
**提出問題比解決問題更難。**設計難於實現,你應當花70%的時間來設計而不是用來實現。有的設計者甚至不參與實現,設計者與實現者的身份也是隨時在轉換的,善於思考的實現者自己就是設計者。
2. 組件設計四要素
-
屬性 -
方法 -
事件 -
子視圖(插槽)
上述的案例基本涵蓋了這四個要素,這四要素共同組成了組件的api。須要注意的,除了基本的四要素外,咱們還須要注意這些也是組件api的一部分:
-
屬性的類型、是否必填、默認值(屬性類型肯定後再也不變化) -
方法的參數、返回值(須要考慮變化的狀況) -
事件回調函數的參數 -
插槽可獲取到的局部參數
在設計時,應當當心謹慎面對每個api的要素,哪個環節出現了設計缺陷,對於調用者都是如鯁在喉。
4、終極思惟:面向對象
儘管咱們經過一系列的理論講述了組件設計的方法,可是對於初學者而言,仍然難以設計出一個優良的組件,設計一個優良的組件須要大量的經驗,初學者每每考慮不全面,或因對需求的不瞭解,沒法預知將來的變化。
儘管如此,初學者仍然要耐心學習組件的設計,不積跬步無以致千里,通過一段時間的積累,我總結了一個設計組件的終極思惟,將面向對象的思想用於組件設計,將會事半功倍。
在開發領域,學會思考比埋頭幹活重要。咱們將這個理論用於組件設計中,如何經過面向對象的思惟來設計一個組件呢?
雖然咱們強調使用面向對象的思惟來設計組件,但彷彿面向對象思惟比組件設計更高深,咱們固然不會推薦你們用更加晦澀的理論來指導組件的設計,這裏,咱們將面向對象擬人化,提取出一個天然世界聯想法的思惟方法。
下面咱們就用這種方法,來設計一個快遞小哥的組件:
首先,快遞小哥有他的基本信息,這是該組件的屬性,基本信息中包含了他的任職單位、工做年限、姓名、聯繫方式等等。此外,快遞小哥有一些特有的行爲,例如送快遞、接收包裹等,咱們能夠將這部分抽取爲組件的方法,好比咱們調用快遞小哥的接收包裹方法,該方法有兩個參數,第一個參數是我要寄的東西即包裹,第二個參數是快遞單,描述了寄送相關的信息。除了基本信息和一些行爲外,快遞小哥組件還有一些特有的事件,當咱們的包裹到了時,他會打電話給咱們,這裏,組件拋出了一個快遞到達的事件,事件的參數是快遞單和包裹,快遞單描述了包裹的送達信息,包裹是快遞單中描述的接收人的東西。最後,快遞小哥組件有沒有子視圖呢?有。快遞小哥組件除了被咱們普通用戶調用外,還會被快遞公司所調用,不一樣的快遞公司會以不一樣的方式來包裝快遞小哥(例如經過不一樣服裝不一樣logo的方式),所以,快遞公司在調用該組件時,會將快遞小哥的服裝傳入一個名爲裝束的子視圖中,這樣,不一樣公司的快遞小哥就有不一樣的裝束了。
你可使用天然世界聯想法來思考一切關於面向對象與組件化相關的問題,只要計算機世界仍然是人構建的,咱們就仍然能夠按照天然世界的規則來感知計算機世界。
二進制世界歷來不是冰冷、無情的,每個二進制串都融入了編碼人的思惟模式、價值觀。
5、最後
從新回到開篇的問題,爲何說當今的web前端開發已變成web客戶端開發呢?由於組件化是全部客戶端開發的核心概念,只要這個端大部分的時間在作組件抽象的工做,咱們就能夠認爲本身在從事客戶端開發。
最後,組件化不是銀彈,不能爲你解決任何實際問題,它只是一種思惟方式。
本文分享自微信公衆號 - 前端迷社區(gh_c8466b051727)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。