聊聊 React 組件庫的技術選型與設計
{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最近在業務中開發了一套定製化的 C 端組件庫,在這個過程中遇到了一些組件庫技術選型和設計的問題,在參考公司內外的多個組件庫後確定了最終的方案。本文希望通過向讀者介紹技術選型的過程中的方案比較和組件庫設計中的考量,讓讀者在組件庫的技術選型和設計上有所啓發。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/f4\/f4fa8d8b2431f9a8d5d4e606e87a4c06.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"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":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"組件庫的技術選型"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"樣式方案選擇"}]},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/24\/248c15374c57d407c3d87cfdab05e576.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"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":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Sass\/Less"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是大家最熟悉的方式,它的優點是足夠靈活、開發成本低(絕大多數工程師都熟悉它們)、 完全支持外部覆蓋組件的樣式,缺點是難以調試(需要到 runtime 才能知道命中的規則),以及難以實現靜態分析。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"Atomic CSS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 UI 足夠標準化的情況下,使用 Atomic CSS 能實現更小的包體積大小,對於單個組件,除了極少數無法抽象的樣式以及自定義動畫,不再需要聲明其他樣式。當然它的缺點是代碼可讀性稍稍降低。同時開發者需要先熟悉項目的原子樣式,增加了一定的開發成本。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"CSS-in-JS"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CSS-in-JS 指包括 styled-component、Emotion、JSS 等在內的,在運行時通過 js 生成 css 樣式的第三方庫。CSS-in-JS 這種方案的優點在於能有效解決“組件樣式隨着數據變化”的問題。但是,它的缺點在於爲了支持從外部覆蓋內部元素的樣式,需要給內部元素加上 className,同時不支持 postcss,取而代之的是特定 CSS-in-JS 庫自己的 plugin 生態,少部分庫(如 emotion)沒有支持 rem 的工具庫。另外在做 SSR 和流式渲染時,都需要在 node 層增加提取樣式邏輯,增加了開發成本和額外的開銷。"}]},{"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":"strong"}],"text":"小結:在有成熟的 UI 規範的情況下,Atomic CSS 是一個不錯的選擇,其次,使用傳統的 sass\/less 來編寫樣式也利於維護(大部分前端開發者都熟悉它),在選用 CSS-in-JS 方案時則要考慮團隊的開發習慣和上手成本。"}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"icon 方案選擇"}]},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/d1\/92\/d1c58f2b3244e0d37bb1acaa3bafa492.png","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"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":"在選擇 icon 方案的時候,除了關注渲染質量,我們還應該關注它的靈活性,以便具有更好的適配能力。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"iconfont"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"iconfont 這種方案的優點在於兼容性最好,支持 IE6 及以上版本。但是,由於 iconfont 方案是將 icon 作爲文本來使用,"},{"type":"text","marks":[{"type":"strong"}],"text":"在 webkit 內核的瀏覽器下由於對文字有抗鋸齒,導致渲染失真"},{"type":"text","text":"。另外,由於將所有的 icon 打包成一個字體文件,不支持按需加載,包體積偏大。這樣很容易導致"},{"type":"text","marks":[{"type":"strong"}],"text":"在加載完成 icon font 後頁面的重刷新"},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.infoq.cn\/resource\/image\/d5\/3f\/d50b341260106b9ebee6d1982670ca3f.gif","alt":null,"title":"","style":[{"key":"width","value":"75%"},{"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","marks":[{"type":"strong"}],"text":"base64 引入"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"base64 也是一種常用的方法,但是由於將 svg 作爲背景圖引入,只能控制它的大小,不能覆蓋它的顏色,也更不能修改 svg 內部的元素,不夠靈活。對於常常採用 MPA 結構的端內 h5,不利於 icon 在不同 SPA 之間複用。同時 base64 字符串的長度是 svg 文件(優化後)的 1.3 倍左右。"}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"React Component、SVGUseElement 和直接寫入 svg 元素"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這三種方式本質上都是將 svg 作爲 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":"svg 的基本能力的兼容性除了在 IE11 以下不支持動畫和縮放,基本沒問題,而 svg effect(主要是使用 transform、filter 等屬性)在 android4.4 以上的支持良好。svg 的動畫性能有瓶頸,幸運的是我們可以使用 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":"直接寫入 SVG 元素的方式缺點在於"},{"type":"text","marks":[{"type":"strong"}],"text":"完全無法複用同一個 icon"},{"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":"而 SVGUseElement 的具體實現方式有使用元素、 元素和 SVG fragment identifiers 等方式,但總的來說,都是在頂部聲明 svg 元素,在需要使用的地方使用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.