【日記】重新出發,再入江湖——最理想的UI庫應該是什麼樣子

       從一個封閉的工作網絡出來,即將進入開放的世界;將壓抑的心情釋放,迎接光明的未來。生活的拐點可能向下,也可能向上,但人生的曲線只能蜿蜒向前!剛離職,離職的心情都差不多,這次是最輕鬆的一次。在家真的懶得打包行李,該擺出來的擺出來,該丟的就丟掉,只是不捨得這個房間,這個小區以及剛交往的舍友!   

      這次打開博客,其實是想記錄一下從昨天(2021.12.9)開始思考的問題:最理想的UI庫應該是什麼樣子?

起因:

1、純粹面向樣式:

早晨躺在牀上時,在B站上刷到張鑫旭對的Lulu Ui組件庫的介紹, LuLu UI 組件庫適用場景介紹_嗶哩嗶哩_bilibili  ,這個UI不以傳統的業務組件爲目標,以最小侵入,讓程序面向最原生的html 去開發。當然對新手不友好,但它是想給老手充分的自由去操控每一個細節。他一句原話:“致力於解決瀏覽器的視覺表現和交互體驗問題,而不是致力於解決實際的業務功能實現問題”。他舉例,一個應用使用純原生代碼後的樣式醜陋,在簡單啓用Lulu ui後,原業務JS代碼絲毫不需要修改,界面就變美觀了。

   去看一下文檔,Lulu有2個特點:

  • 我一開始認爲它可能是極端的,不需要添加任何類,直接美化所有的原生dom標籤, 其實不是這樣的,它是一個類bootstrap的框架,簡單的加上  ui-table 就是一個美化後的表格。
  • 一些組件是用web component實現的,比較明顯且合理的就是 datetime組件,基於瀏覽器原生<input>輸入框構建, 添加  is=“ui-datetime 這樣的屬性。從控制檯可以看到,input的內部shadow改造很少,整個日期彈窗還是彈在父頁面上的, 並沒有做到input的shadow中去,可能與web component的某些限制有關係!

Lulu的動機和實現都很清晰,也是希望做一個”純粹的面向樣式UI庫“, 這讓我想到了前幾個月看到的 【探索學習】可能是下一代的組件庫 - headlessui_嗶哩嗶哩_bilibili  的視頻。

2、純粹面向組件邏輯

     Headless Ui, 它走在另一個極端路上,該庫出自於TailWindLabs,真心符合TailWind的性格!

      在通常使用Element 或 AntV等庫時,組件和組件的結構、樣式是綁死的,你選擇使用它,就必須接受組件的樣式和組件實現時的dom結構。當你想完全還原美工設計稿效果時,少不得寫很多覆蓋樣式類,比如添加./deep/ 或添加 !important 來實現交付。其一我們都知道這樣修改樣式不好,其次設計稿與組件差別太大時,由於組件的dom結構固定,往往要放棄該組件!

     於是Headless的口號就出來了: 完全的無樣式,給你完整的可訪問控制的UI組件庫,它建議搭配Tailwind css 一起使用。也就是說它每一個組件只是純邏輯,所有的dom結構和dom的樣式,讓使用者自己編寫。比如以<Switch>組件爲例, 整個組件只關注2點:有一個布爾變量和變量切換狀態值的動作。至於你要把組件渲染爲勾選框,還是左右滑動的開關,抑或是兩張靜態的png圖片切換,那是使用者自己編寫了。

     如果在項目引入Headless的開發流程極可能是:首先需要一個有經驗的程序員,根據本項目的設計稿把所需要的組件逐一的封裝成傳統的UI組件庫那樣,再供其它人直接引用。這樣就回歸傳遞組件方式了。

 

思考

      做爲一個多年的老前端,見證過jQuery時候的插件輝煌年代,也正享受着目前各類Vue ,React等框架庫的強大功能,也在項目中手擼過許多組件,對於UI組件的開發,目前是實踐Composition API技術的忠實擁躉,還是有一些心得和話要說一說的。

1、開發的基本原則:

      前端開發中,大到一整個應用,中到一個頁面,小到一個組件,無一不是由”內在邏輯+外在樣式“構成的,也是我一直強調的原則——最小應用狀態,我叫稱其爲AppCore 模式 ,其實就是隻關注應用的直接狀態數據和應用的動作 。

       前端開發也要分成 ”前後端開發,邏輯和樣式要分離“

        前端的前端就是編寫dom結構和樣式, 前端的後端開發就是編寫內在的狀態和動作,而動作是觸發狀態的持續變化,從而產生一系列的應用快照。在編程時,只強調數據狀態與界面的直接綁定,一個數據快照對應一個界面快照,杜絕事件的交叉時的事件耦合代碼,避免引入副作用變量和延伸的狀態變量。

      事件交叉是指,爲處理兩個或多個事件先後觸發時,需要額外代碼維護這個邏輯。比如一些if語句,一些記錄變量等。其實每個事件都有自己關注的狀態數據,如果引入了上述的額外代碼,必然是未來bug的引子。

       延伸的狀態變量是指非指向直接狀態值的變量。有這些典型情況,比如對於某個appCore應用,有一個actived的變量, 開發者爲了綁定圖片效果,寫了一個activedUrl,這種就是延伸變量。 也或者界面有一系列按鈕,於是編寫一個變量: xxxBtnList:[ { id, text, icon,hoverColor,disabled........}]  這樣的東西,其實界面上渲染一組按鈕的話,只有按鈕動作是對應appCore中的動作,而這個xxxBtnLIst變量純粹是爲了綁定而憑空多出來的狀態值,其也歸屬於延伸變量。

      App Core模式下,追求極致的數據狀態與界面的直接綁定,追求極致的純數據流,也只有純數據纔有利於函數式的代碼書寫,減少代碼量和bug!     

2、目前組件庫長什麼樣

       毫無疑問,目前的各個Vue\React組件庫具有強大的功能,衆多的組件,詳細的文檔,能覆蓋複雜的業務場景,基本都是開箱即用,這些自不必說。

       但下面是我對現有組件庫的一些吐槽點:

     現有的組件庫中的類型:

  1. 簡單的、避免實現組件:簡單的比如Button  ,Icon, Card,el-Image 等,內部基本沒什麼狀態數據和狀態的切換,只是純樣式,此時使用類似bootstrap等CSS庫就可以解決!它們若被封裝爲組件,還會帶來微不足道的一點內存消耗和性能問題。又類似Card的組件,通常需要支持各種  title slot,body slot,開發組件時各種佔位,使用組件時再去填坑。  孰不知DOM中Div等標籤天生支持嵌套,額外引入一個組件,憑空多出來的屬性和概念,實屬浪費,形成額外的學習成本,認知成本!它只需要引入幾個工具類即可!同此理,el-row, el-col  , a-row ,  a-col 等引入更是多餘,Flex佈局百分百可以覆蓋它的應用場景,引入它們只是憑空多了一箇中間層,也帶來額外的學習成本,認知成本!
  2. 複雜的組件:複雜一點的組件就是table,tree等,它們有一些狀態和動作, 還有許多dom結構和css技巧來支撐組件的樣式展示。這些組件內需要引入許多狀態變量(也即我上述的延伸變量),來服務於樣式綁定,而樣式綁定也深深依賴於組件的邏輯編寫。比如表格的合併表頭,固定表頭,固定列, 列的寬度對齊,虛擬滾動,排序、過濾,格式化單元格。。。。。。所以每個組件內部的邏輯代碼編寫時,無時無刻的得考慮樣式的問題,造成很難嚼得懂。建議嚴格按照appCore的方式組件代碼。

     複雜的樣式系統:

       無論Element  還是Antv ,通常都是使用了less或sass技術 ,基於自定義css變量等,開發組件以及可定製主題樣式。這塊不太好吐槽,我也只知道這麼多技術,即使我來實現一個庫也會上這些技術。 唯一可吐槽的是某些框架的樣式太複雜了,讓我看不懂😂。 看不懂能怪別人嗎,怪呀,一個Button的樣式能寫2屏長,難道不是有點內捲了嘛。 簡單的應保持簡單化,無論有什麼理由,從簡應該是第一原因!

3、理想的UI框架是什麼

      最上面的2個UI框架庫引發我思考,兩者各走一極端,一個只關注於視覺表現和用戶交互體驗,不向業務類組件低頭;另一個只關注組件的邏輯,組件樣式交於用戶,在使用時需要額外的編寫樣式代碼,所以這兩個方案都不是一套完整的方案。做爲一個整體組件方案,必須要達到“開箱即用,覆蓋常用的業務場景,還要達到性能最優,學習成本低,不侵入業務以及向原生的技術看齊,甚至是跨框架開發”。

      做爲一名夢想家(非實幹家),我設想了一套UI框架的方法,暫時列述一下,以後有想法,會持續補充。

  1. 增強的reset.css,  要不叫做  reset-pro.css。 
     首先是以Lulu UI的出發點,簡單引入CSS,實現一些基礎DOM樣式的優化顯示即可。此一步不要做與業務有關的東西,比如 primary/error colors ,或者 圓形圖片等,保留純粹的html原汁原味。
    其次它包含點:1、做重置元素的樣式,重置padding,margin,box-size, 優化默認的form元素,table元素樣式等。
                              2、添加常用的工具類,比如常見了right,clear,或佈局類 f-row, f-pos-between, fi-1 ,響應式斷點, 或者 hide-lg, show-md 這些,參見Bootstrap、Tailwind.css等做作法。 關於響應式斷點應該不應該設計爲className,應該是仁者見仁的問題,我平時用響應式的少。我是更傾向於在body 上,類似於Modernizr.js的原理,動態的添加響應式類名來實現,讓用戶自己的業務CSS處理,瀏覽器好像是有關於媒介查詢的API(你知道嗎?Javascript也有媒體查詢API - 簡書 (jianshu.com)
  2. 增強的HTML,要不叫做html-pro.js吧。
    Html 缺失的一些元素,比如彈窗和日期時間,顏色選擇器這種組件,引入一些代碼,統一補齊瀏覽器缺失的功能,爲後續的組件開發,打好基礎。
  3. 全局的css自定義變量。
    首先root節點中,確定一組全局的顏色,間距,border,陰影,動畫時長等 css自定義變量,方便後續的定製主題做準備,之後所有組件使用的一些變量儘量使用全局的css變量。
  4. 簡單業務組件
    1、對於一些簡單業務組件,就完全不要編寫一個組件來與之對應了,直接用class 輔助類來實現,比如 el-button\ el-image\el-link 。
           原因1是,每一個組件封裝有性能成本;
           原因2是,以vue爲例,我自己試着寫了Button的一個組件,發現它內部沒什麼狀態,假如我給組件定義了一組屬性: {size, type,plain,round,circle,loading,disabled } 。組件內代碼就是把props中接收過來,直接轉成相應的類名,再綁定到模板中去,感覺組件什麼實質性的操作都沒有,操作了個寂寞。 再者disabled屬性更是button原生屬性, native-type="button/submit/reset" 也是原生屬性,我們應該鼓勵讓用戶瞭解這些知識,而不是自己封裝一套屬性掩蓋了原生知識。使用類似bootstrap的類名組件實現這些業務場景
    <button disabled="disabled" type="button" class="el-button el-button--primary is-disabled">
      <span>主要按鈕</span>
    </button>
    假如文檔中,把按鈕的各個狀態按className的寫法展示,用戶複製粘貼,效果也是一樣的,沒必要爲了組件而組件
    2、對於佈局類組件,堅決不實現。 佈局類組件,無論是<el-container> 還是<el-row> <el-col>,都是很多餘的,應該鼓勵用戶學習佈局的css 知識,就像鼓勵用戶瞭解原生屬性一樣。Flex,Grid應該讓大家掌握,組件提供的柵格組件並沒有減輕用戶的認知成本!在增加的reset.css中,添加了常用的輔助類,讓用戶使用這些進行頁面佈局。
    3、圖標組件。 主流的框架庫一直是使用字體,較新的Ant design又提出使用svg,我也是支持使用Svg的,它有2,3種優勢是強於字體,但我又不太習慣Ant 封裝的那套引用方式。我建議把每個圖標的path定義爲字符串常量,逐個export出來,方便搖樹。 之後封裝一個<xx-svg :path="star"/>   這裏假設,star就是某一個字符常量。 假如用戶要擴充自己的圖標,只需要靜態引入相應的字符串變量即可!
  5. 複雜業務組件
    表單和表單項組件:表單元素在原生的Html中,也是很特殊的存在,有很多屬性和方法。表單的組件的開發,我目前還真沒實際的參與和設計經驗。Element的表單行爲可能最容易讓新手適應的,AntV的Form和Element設計基本一致。Angular提出過2種表單開發的模型,就是“模板驅動”和“響應式表單”,各有優劣點。表單項的開發,文本框、單選、多選、時間、文件等等組件,主要考慮雙向綁定值,校驗規則,曾經 Noform的框架有個ppt,對錶單的分析和研究非常全面,理解得透徹。
    彈出類+容器類組件:dialog,popover, toast,  tabs, collapse,drawer ,tooltip 等組件
    數據類組件: table,tree,list
    特殊+裝飾物類組件:時間軸,badge,tag,rate評分,輪播圖,頭像等等。

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章