聊聊前端 UI 組件:組件特徵

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文首發於"},{"type":"link","attrs":{"href":"https://ourai.ws","title":null},"content":[{"type":"text","text":"歐雷流"}]},{"type":"text","text":"。由於我會時不時對文章進行補充、修正和潤色,爲了保證所看到的是最新版本,請閱讀"},{"type":"link","attrs":{"href":"https://ourai.ws/posts/the-features-of-frontend-ui-components/","title":""},"content":[{"type":"text","text":"原文"}]},{"type":"text","text":"。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文是文章系列「"},{"type":"link","attrs":{"href":"https://ourai.ws/series/talking-about-frontend-ui-components/","title":""},"content":[{"type":"text","text":"聊聊前端 UI 組件"}]},{"type":"text","text":"」的第二篇,內容與本系列的上篇文章《"},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/63788ce9260cdbc214523651f","title":""},"content":[{"type":"text","text":"聊聊前端 UI 組件:核心概念"}]},{"type":"text","text":"》有所關聯,如果還沒看過,建議去看下。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文的主要內容是根據特徵對前端 UI 組件進行建模,讓我們儘可能充分地瞭解它的方方面面,併爲如何設計以及建立一個組件體系打下基礎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件構成"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從關注點分離的角度分解 UI 組件,並分析其各部分的易變性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"構成元素"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個完整的具備功能的 UI 組件的構成,有結構(structure)、表現(presentation)和行爲(behavior)這三個方面。爲什麼強調是「完整的具備功能的 UI 組件」?是因爲它是一個最全的特徵集合,而其他的「UI 組件」可能會缺少一些特徵,從而使分析不那麼完善。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到「結構」、「表現」與「行爲」這三個詞,作爲一名有經驗的前端開發者,很自然地就會想到很久很久之前開始一直提倡的前端開發的關注點分離——HTML 負責組織頁面結構,CSS 負責網頁內容的表現樣式,JS 則負責用戶與網頁之間的交互,它們各自扮演着不同且相輔相成的角色。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/22/22826761298495c9ab6020cf7c5d3377.png","alt":null,"title":"關注點分離","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然而在這裏,它們的含義會有所不同——"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"經 HTML 組織後的網頁內容是「結構」沒錯,但它僅僅是代碼層面的,未被 CSS 所影響的結構。最終的視覺呈現,也就是視覺層面的「結構」,應該還包括 CSS 的佈局類樣式,如定位(positioning)、浮動(float)等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CSS 中的那些非佈局類樣式,如顏色、字體、文本、邊框、尺寸、留白等類別的樣式,以及圖標、圖片,皆爲「表現」。這些一般還被稱爲「主題風格」或者「皮膚」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可在 JS 中運行的事件系統以及進行邏輯處理的函數和對象方法,算是「行爲」——這就是 UI 組件的交互邏輯了。當然了,與交互邏輯相融合的業務邏輯,也是「行爲」的一部分。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"易變性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據構成 UI 組件的每個部分的性質,會影響 UI 組件相應部分的易變性——對於組件複用來說,該部分是相對穩定的還是相對不穩定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"GUI 發展了幾十年,人機交互的圖形元素及佈局方式已經相對固定,只要不是出現像 Google Glass 之類的革命性交互設備,就不會發生重大改變。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"}],"text":"——歐雷《"},{"type":"link","attrs":{"href":"https://xie.infoq.cn/article/9814af3887ef2f2e20c755ee4","title":""},"content":[{"type":"text","text":"我來聊聊面向模板的前端開發"}],"marks":[{"type":"italic"}]},{"type":"text","marks":[{"type":"italic"}],"text":"》"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上所述,未來到底會出現什麼樣的變革性交互方式無從得知,但我認爲,只要是需要用眼去看且用手去操作,交互方式就逃不離這幾十年來未有啥變革的形式。因此,UI 組件的視覺結構和交互邏輯是最不易變的,且無論是交互模式還是觸發事件都是可枚舉的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果單純從最終的 HTML 結構上來看,它也算是不易變的,但在現代前端開發中,HTML 的結構基本是動態生成的,並且強依賴於 React、Vue 這類沒有統一標準的 JS 庫/框架。另外,還存在着像 "},{"type":"link","attrs":{"href":"https://developers.weixin.qq.com/miniprogram/dev/reference/wxml/","title":null},"content":[{"type":"text","text":"WXML"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https://opendocs.alipay.com/mini/framework/axml","title":null},"content":[{"type":"text","text":"AXML"}]},{"type":"text","text":" 這類平臺限定的視圖結構描述語言。由於寫法不一致,這就使頁面內容結構變得不那麼穩定。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一般來說,離用戶越近的東西越容易發生改變。在網站、應用中離用戶最近的是 GUI,而在 GUI 中離用戶最近的則是主題風格,這是對用戶來說最直觀的東西。主題風格會隨着 GUI 設計人員(通常是設計師)的審美和想法而改變,所以它是最易變的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因爲業務邏輯是進行業務相關處理的核心所在,如果業務規則變了,相應的代碼實現也得跟着改變,所以業務邏輯也是很易變的。業務邏輯對於一個網站、應用來說是十分必要且重要的,但對 UI 組件來說,它就沒那麼必要了,更談不上重要。在前端的 GUI 層面,業務邏輯理應是交互邏輯的延伸。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了方便,將 UI 組件各個構成部分的易變性及其影響因素整理如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/43/43a5c5c20208679509092a163bde4db1.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從表格中可以看出,將「易變性」分成了三個等級,按照從小到大的順序來分別解釋下:"}]},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"「不易變」——受交互方式影響,只要沒發生什麼革命性改變時就不太會變;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"「較易變」——受源碼語法影響,只要源碼的編寫方式確定就不會變;"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"「很易變」——受業務和人影響,只要業務領域、業務規則還有人的想法不變就不會變。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件分類"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以較爲抽象的視角對 UI 組件進行分類——"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"原子性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從一個 UI 組件的內部是否還包含其他 UI 組件這個角度來看,分爲「原子組件」和「複合組件」兩類。「原子組件」是不可再分的 UI 組件,即其內部不再包含其他的 UI 組件,像按鈕組件、圖標組件、分割線組件等;「複合組件」則是由一個以上的原子組件所構成的,如導航菜單組件、選項卡組件、對話框組件等。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"通用性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據 UI 組件的通用性,可分爲「通用組件」和「專用組件」。「通用組件」是能夠滿足大部分常規場景的 UI 組件,它們的集合通常會作爲「組件庫」整體打包發佈爲一個軟件包;「專用組件」是爲了解決某些特殊場景需求而存在的,像數據網格、各種編輯器等,這類一般都是單獨發包。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"功能性"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按照 UI 組件所起到的作用,大概可劃分爲以下幾類:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a9/a94e216bc887866fded353ebf4717889.png","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這種分類方式沒有一個嚴格的定義,就見仁見智了。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件繼承"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不像面向對象編程中的繼承那樣是行爲的複用,這裏所說的「繼承」是指表現的複用,並且還能夠「多重繼承」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在繼續往下之前,先引入一個「虛擬組件」的概念。正如它的名字所示,是一個虛擬的,實際不存在的,只是概念上的組件。它是幾個主題風格屬性的集合。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輸入框組件、下拉列表組件等都屬於"},{"type":"link","attrs":{"href":"https://html.spec.whatwg.org/#forms","title":null},"content":[{"type":"text","text":"表單控件"}]},{"type":"text","text":"(form control),它們都繼承自「表單控件」這個虛擬組件,如果各自沒有指定顏色、字體、邊框等主題風格屬性的話,將會按照虛擬組件中所設定的來顯示。類似地,下拉列表組件、下拉菜單組件等都有彈出層(pop-up),它們都繼承了「彈出層」這個虛擬組件。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"想必你已經發現了,下拉列表組件同時繼承了「表單控件」和「彈出層」這兩個虛擬組件,這就是上面提到的「多重繼承」。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那些所謂的「虛擬組件」,它們也遵循着同樣的繼承規則——如果自身沒有指定特定的主題風格屬性,則會按照父級所設定的顯示。那麼,虛擬組件的「父級」是啥呢?——是基礎風格。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然這裏所描述的繼承關係看起來有點像 CSS 中的"},{"type":"link","attrs":{"href":"https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_and_inheritance","title":null},"content":[{"type":"text","text":"繼承"}]},{"type":"text","text":",然而它們並不一樣。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文從前端 UI 組件的構成、分類及組件間的繼承關係等角度出發,通過分析組件的特徵來探討建立一個組件體系所需要關注的一些點。其中,UI 組件各構成元素的易變性是最應該被關注的,它會對組件體系的可複用性、可擴展性等產生很大影響。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實質上,HTML 與 WXML 和 AXML 等是同一級別的,都是平臺限定的視圖結構描述語言——WXML 是微信小程序的,AXML 是支付寶小程序的,而 HTML 則是「網頁瀏覽器」這個平臺的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在動態網頁中,尤其是使用了 "},{"type":"link","attrs":{"href":"https://mustache.github.io/","title":null},"content":[{"type":"text","text":"Mustache"}]},{"type":"text","text":"、"},{"type":"link","attrs":{"href":"https://handlebarsjs.com/","title":null},"content":[{"type":"text","text":"Handlebars"}]},{"type":"text","text":" 等模板引擎或 React、Vue 這類庫/框架時,最終的內容結構基本是 JS 生成的,這就強化了 JS 而弱化了 HTML 在內容結構上的控制力。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"各個 JS 庫/框架的 HTML 代碼生成方式不同,視圖結構描述語法不同,沒有統一的標準,這就造成了混亂——這也是對前端 UI 組件的可複用性最大的挑戰!"}]},{"type":"horizontalrule"},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":"center","origin":null},"content":[{"type":"text","marks":[{"type":"size","attrs":{"size":12}}],"text":"歡迎關注微信公衆號以及時閱讀最新的技術文章"}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/15/15ca057e8c81c613f5a34d41b84b7125.jpeg","alt":null,"title":"Coding as Hobby","style":[{"key":"width","value":"25%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章