天天看點

Slack使用React重寫Web用戶端

slack使用react重寫了web用戶端。在這篇文章中,他們以重寫emoji選擇器為例,展示了react在性能和代碼可維護性上給他們帶來的巨大好處,以及給使用者帶來的體驗更新。檢視英文原文: rebuilding slack’s emoji picker in react。

slack正在将web用戶端遷移到react。在最開始,我們的前端使用了jquery和handlebars。後來,社群開發出更好的方案用于建立可伸縮的、基于資料驅動的使用者界面。jquery的“渲染後修改”模式直截了當,但無法與底層的模型保持同步。不同的是,react的“渲染後再渲染”模式可以保證渲染和模型的一緻性。slack也緊跟業界的步伐,不斷改進前端的性能和可靠性。

我們認為,要引入react,最好的辦法就是先用react重寫現有産品裡的某個特性——這樣我們就可以比較出新的開發流程和結果與原先的有什麼不同。我們需要重寫一個元件,這個元件必須具備互動性,能夠自包含,并能夠展現react在性能方面的優勢。我們很快就找到了一個絕佳的元件——被重度使用且極度複雜的emoji選擇器。

Slack使用React重寫Web用戶端

  virtual dom的優勢

閱讀這篇文章要求對react有一定的了解,如果你不熟悉react,建議先閱讀一下react的官方文檔。簡單地說,react是一個javascript代碼庫,可以用它友善地開發聲明式的、基于資料驅動的使用者界面。它的api很簡單,主要由一個元件類組成,這個類包含了一些生命周期方法。元件本身不會生成html,相反,它們會生成類似dom的樹,叫作virtual dom。react會比較兩個virtual dom,并使用最少的操作将其中的一棵樹轉換成另一棵樹。例如,你可以告訴react基于新的模型資料重新渲染整個視圖,它就會以最快的速度幫你更新文本節點,就好像它有一個精靈軍團在幫你完成dom的更新操作一樣。

react擅長于将元件在單個模闆上的各種行為合并在一起。舉個例子,假設一個slack頻道變為未讀狀态時,你通過javascript來更新頻道的邊欄:

找出這個頻道的id在dom裡查找這個頻道對應的節點将節點狀态切換成未讀(應用css類)

這個過程很簡單,不過你還得為其他事件編寫不同的處理邏輯,比如“create”、“join”、“leave”和“rename”。相反,react把這5中情況合并在一起統一處理:

使用新的模型資料重新渲染頻道邊欄。

我們不需要為每一種dom操作編寫代碼,而是重新渲染整個元件,react會為我們完成這個過程。react通過讓代碼變得更通用(一刀切的模闆)來簡化開發。

emoji選擇器

emoji是slack ui的一個組成部分,是最理想的react元件。它動态、離散,隻需要少量的輸入——一組emoji、預設皮膚和使用者的emoji使用曆史。剛好現有的emoji選擇器需要進行性能調優,因為現在不管emoji會不會出現在視圖裡都需要進行渲染。在查找emoji時需要切換每個emoji的可見性,在重度使用時性能很成問題。新的slack團隊準備了1374個預設emoji,這還不包括自定義emoji(在寫這篇文章的時候,slack團隊總共有3126個emoji,有些團隊甚至更多)。重寫emoji選擇器将會對slack的日常使用産生重大影響。

Slack使用React重寫Web用戶端

我們選擇在storybook裡開發新的元件,storybook自稱是一個“會讓你喜歡上它的ui開發環境”。它不要求你改變開發方式,但會讓開發、測試和代碼審查變得更有趣。你可以在storybook裡通過指定不同的屬性來定義不同版本的元件。我們為emoji選擇器增加了一個新皮膚和幾種emoji查找方式。

元件布局

react emoji選擇器的根元件是有狀态的,而子元件則是無狀态的。我們按照慣例把每個元件導出到單獨的檔案裡。結構如下所示:

header

分類頁籤:列出了emoji的類别,每個類别都有一個“jump to”連結。搜尋框:通過emoji的名稱或别名過濾emoji。

body

固定的頭部:顯示目前類别頁籤的名稱。emoji清單:所有類别的emoji虛拟清單。

footer

emoji預覽:目前選擇的emoji大圖預覽。皮膚選擇器:顯示目前的皮膚,并可以切換到其他皮膚。快捷動作(可選的):emoji的子集,用于快速回複消息。

react為編寫無狀态元件提供了兩種方式:purecomponent類和function。function更為簡單一些,不過它們在每次合并時都會進行渲染,會影響性能。react團隊計劃對function進行優化,不過目前最好還是避免使用它們。于是我們選擇了purecomponent,它預定以了shouldcomponentupdate方法,這個方法可以防止在遇到相同屬性時進行更新操作。

react是一個視圖層,把它與自己開發的應用內建要比把它與标準的架構內建直截了當得多。我們不應該破壞emoji選擇器的封裝性,這樣才能很好地與slack現有的模式內建在一起——我們希望這個元件就像是從一個端到端的react應用裡拿出來的一樣。為了保持選擇器的純淨,我們在現有的子產品系統裡建立了一個輕量級的擴充卡。擴充卡挂載選擇器元件,抽取模型資料,并監聽來自外部的信号。采用這種模式,我們可以在開發新功能的同時逐漸地遷移代碼庫。

新的開發流程

雖然使用react進行開發是一件很愉悅的事情,但将它內建到我們已有的開發流程裡卻不是那麼一回事——至少在一開始不是那麼令人愉快。在那個時候,slack使用的是自己開發的前端建構管道,沒有所謂的導入、依賴或者複雜的轉換(比如transpilation)。我們決定采用jsx文法和es2015+,并使用babel和webpack在本地建構emoji選擇器的資源。

我們預期簽入本地編譯的代碼會很痛苦,但我們低估了接連發生的合并沖突和依賴管理問題是多麼令人抓狂。最後,我們嘗試将webpack內建到我們的開發和staging環境裡,目标是無縫地替代已有的工作流。為此,我們做了如下的工作。

基于webpack-dev-server開發了一個服務,當相關資源和依賴發生變更時,自動編譯本地開發伺服器上的資源。支援将webpack資源加載到單元測試裡(這樣就有可能為react元件編寫測試用例)。重構生産環境的建構流程,将webpack資源推送到我們的cdn。

通過重寫emoji選擇器,迫使我們反思我們的建構管道如何能夠以一種更健壯、更具伸縮性的方式打包資源。

性能

Slack使用React重寫Web用戶端

我們在少量的團隊裡部署了新的元件,并觀察結果。我們觀察了emoji選擇器在使用者使用不同的5種互動方式下的渲染速度,對于大部分的操作,react表現出了顯著的速度提升。以下列出了選擇器在正正常模團隊裡的不同渲染時間。

第一次挂載:-270毫秒(減少了85%)第二次挂載:-158毫秒(減少了91.3%)搜尋(多個結果):+27毫秒(增加了259%)搜尋(一個結果):-25毫秒(減少了53.2%)重置搜尋:-68毫秒(減少了70.1%)

最大的改進來自“第一次挂載”,從318毫秒到48毫秒,減少了270毫秒,也就是85%。這要極力歸功于react-virtualized——一個虛拟清單代碼庫——減少重新渲染emoji的數量。在預設視圖上,react emoji選擇器比dom少渲染了85%。

或許最讓人感到吃驚的變化來自“搜尋(多個結果)”,時間從17毫秒增加到了44毫秒,增加了27毫秒。舊選擇器隻是把不比對的emoji隐藏起來,也就是說,當比對到大部分emoji時會相對較快。但它的缺點也是顯而易見的,“搜尋(一個結果)”和“重置搜尋”就讓它的缺點原形畢露,因為此時它需要隐藏更多的emoji。

未來

使用react重寫emoji選擇器加快了渲染速度,同時簡化了代碼,讓代碼更容易維護。我們正在使用react重寫剩餘的代碼。我們還有很多工作要做,這次重寫将為使用者的日常體驗帶來積極的影響,為此我們感到非常興奮。與此同時,我們積累了react的實踐經驗,可以幫助平台更進一步。

本文轉自d1net(轉載)

繼續閱讀