來自 GitChat 做者:ewind
更多IT技術分享,盡在微信公衆號:GitChat 技術雜談前端
前端是個挺特別的崗位,一方面它的技術棧更新幾乎是軟件開發領域中最快的,但另外一方面它的不可替代性相對而言卻並不算高。而且,雖然多數企業都有相對獨立的前端團隊,但團隊多半都有很多業務負擔,加上前端較高的迭代速度,一線業務同窗的成長多少容易遇到些瓶頸:需求都作不完了,還有時間讀源碼解析嗎?ios
其實咱們都知道,我的成長和團隊成長是分不開的。一方面在技術氛圍較好的團隊中我的的進步會更快,另外一方面團隊的技術積累也是由一個個成員們貢獻出來的。這就引出了咱們的問題:在業務團隊中,有什麼方法能讓我的和團隊在迭代中都獲得更好的成長呢?git
能想到最簡單的答案應該就是「按期技術分享」和「不加班,給你們更多的學習時間」這樣的吧。不僅是前端,相信這確定也是廣大開發同窗們喜聞樂見的。惋惜業務壓力擺在那裏,若是我的學習影響了短時間內的團隊產出,那麼老闆們的臉色也許會有些微妙……從另外一個角度上來講,前端同窗提高技能水平所需學習的各類模式、框架、類庫、工具,在開源社區都有很是詳盡的文檔和教程,如何提高我的能力的話題也更不是本文所能覆蓋的。不過,若是一個前端團隊可以經過一些技術層面上的方式,培養出由團隊成員共建的良好技術基礎的話,相信老闆們和開發同窗們均可以接受吧。這其實也正是本文所關注的。json
在介紹「怎麼作」以前,咱們不妨考慮下「作什麼」,即在什麼方向上去培養技術積累呢?axios
咱們知道 Full Stack Developer 的概念一直很火,那麼按照 Full Stack 的概念,前端團隊的所需的技術積累是怎樣的呢?這意味着前端團隊須要橫向地擴展本身的能力,拉通 DB 到 HTML 的流程。技術棧覆蓋面廣天然是好事,但是爲何仍是常常可以聽見對全棧的爭議呢?在不少須要快速迭代實現原型的場合,這樣的能力模型是很合適的。問題在於在一個系統的開發中,多數狀況下團隊成員負責開發的是獨立的模塊。而按照信息隱藏的理念,而只有在代碼模塊採用定義良好的接口來封裝,模塊的內部結構對外部不可見,複雜度被屏蔽而非暴露在他人模塊內部結構前的時候,開發的效率纔是最高的,也並非團隊中全部成員都須要瞭解系統總體的技術細節。而且,前端自己也是對開發職能分工的細化,在後端有成熟穩定團隊的前提下,前端在團隊層面按照 Full Stack 的方向積累技術,在投入和產出上未必是最優的。固然,這和許多大廠「只招全棧」的理念並不矛盾,畢竟在我的開發者層面的全棧所真正要求的並非雜而全,而是高效地解決各種問題的學習能力,以及對技術更加全面的深刻。後端
在團隊層面,和 Full Stack 相對的其實有一個 Full Life Cycle Engineer 的概念。這個理念實際上鼓勵的是讓前端同窗去更全面地參與產品的生命週期,並在生命週期的各個階段去更好地支持業務。這個情景下從項目的原型中前端就會以提供 UI 組件的方式參與,在實現了具體需求後也會參與到測試和發佈的流程中,而服務端渲染等特性也要求前端去支持起必定的運維職責。這種模式下,前端的職責並不只僅侷限在傳統的客戶端 HTML + CSS + JS 中,而是一系列與用戶交互相關的技術合集。這個背景下,能夠把前端團隊理解爲一個垂直的功能性的團隊,技術積累上也是更多地圍繞着咱們所面對的業務問題去作沉澱。套用《人月神話》裏外科手術式團隊的概念,若是說全棧的角色更接近於全能的「外科醫生」的話,那麼 Full Life Cycle Engineer 則更接近於縱向深刻的「代碼專家」。瀏覽器
圍繞着這個理念展開,不難發現許多技術點是在團隊層面上能夠去作積累的。而具體到落地,就更須要鼓勵團隊同窗們來解決比常規業務開發更難的工(zao)程(lun)化(zi)問題了。前端輪子是公認的多,幾乎全部常規開發所涉及的領域都存在很是多的現成輪子,那麼是否有必要去積累團隊內部的輪子,又在什麼方向去作呢?這其實視團隊狀況不一樣,是很是業務驅動的。好比,維護 C 端產品的團隊,可能更須要性能優化、監測告警方向的輪子,而開發 B 端中後臺產品的團隊,對狀態管理、統一組件庫一類的輪子需求度則會更高。在深刻業務的過程當中,幾乎總能找到特定的場景可以抽取出特定的複用模塊,或找到適合針對性優化的地方,這其實就至關於技術積累的起點了:在業務驅動下開始造(甚至發明)團隊內部的輪子。性能優化
其實造輪子對技術積累的促進,並不只僅體如今實現輪子這件事自己。好比,即使是一個很是簡單的 UI 按鈕,在將它實現爲可複用的代碼時,所須要作出的工程化努力均可以是很深度的。好比,即使一開始只在團隊內部使用,API 設計得簡單沒有關係,但做爲一個可複用的模塊,怎麼樣讓使用者不須要讀源碼就能用呢?這時候咱們須要最基本的文檔;怎麼樣保證升級後 API 穩定呢?這時候單元測試就體現出了意義;發佈爲模塊的話,樣式怎樣和組件一塊兒提供呢?這時候須要的是構建流程……這些和基本業務邏輯並不直接相關的工程化內容,即使只是開發出一個簡單的內部輪子時均可以漸進地去考慮和實現。而且,這些工程化的特性也正是作技術積累的良好切入點。若是編寫的代碼都是不須要複用的「一次性」代碼,那麼文檔就是多餘的;若是實現 UI 邏輯只是爲了知足基本的業務須要,那麼其實是沒有什麼機會和必要去將單元測試落地的;若是最終打出的包始終只是面向用戶而非面向開發者,那麼甚至也不須要考慮 dependency
和 devDependency
的區別…工做內容限制在一個狹窄的子集內的時候,成長顯然是會受限的。微信
固然了,以上引入的工程化特性客觀上都須要時間投入,也難免會有重複造輪子浪費人力之嫌。這時如何去作權衡和取捨呢?框架
咱們不妨評估一下引入新輪子的投入和產出。好比,分析一個沒有積累公共組件或依賴第三方組件庫的團隊,是否有開源項目中未能提供的 UI 組件,這些組件是否有複用的可能性和條件呢?若是有的話,引入工程化的發佈流程會帶來多少時間成本,是否可以方便其餘同窗在後續的使用中節約回來呢?再好比,是否存在業務場景是現有的開源輪子不能徹底符合需求的?若是本身動手,可以收穫多少易用性或性能上的提高呢?畢竟造輪子也是工程,而工程問題老是能夠評估、分析和 Tradeoff 的。
實際上,前端領域中,因爲業務場景的多樣性,開源輪子不能徹底匹配實際需求的狀況是很常見的。好比,Redux 是個用來支持複雜應用的狀態管理庫,在增查改刪的後臺應用中使用起來十分沉重;實現不需考慮兼容的簡單頁面時,本身封裝出的簡單 DOM 操做庫確定比 jQuery 或 Zepto 更加輕量;封裝登陸認證庫時若是所有交互都經過 JSONP,那麼依賴 fetch polyfill 或 axios 的成本還不如直接實現……熟悉業務後,這樣的場景老是容易發現的。
可是光有動機,並不能成爲決策的充分理由。一個團隊所面對的挑戰更毫不僅僅只是造一兩個輪子就可以解決的。這時候在團隊層面,討論出一份 Todo List 一般是頗有幫助的。咱們能夠經過討論,整理出一個團隊目前須要所須要解決的技術問題的任務列表,並按照「是否重要,是否緊急」的四象限方法梳理出任務的優先級,再去根據列表中各個任務的重要、緊急程度,來決定如何安排團隊的資源投入。畢竟不論輪子的設計是否簡潔、代碼是否優雅,只有可以更好地解決具體問題,可以更普遍地被業務所使用,造輪子纔可以得到更多的產出。
在決定了具體方向後,咱們就能夠開始動手了。對於怎樣實現業務邏輯,相信同窗們都已經頗有經驗了。那麼,怎樣從頭開始造一個輪子呢?這裏面從 0 到 1 的過程和經驗可能也是一些剛組建或者積累較少的小團隊所較爲欠缺的。
實際上,從 0 到 1 的過程,其實就至關於一個實現 Proof of Concept 的過程。一個 POC 原型只須要實現新輪子最核心的一兩個特性就足夠了,實現上並不須要考慮得大而全。而且,面向團隊內部使用的輪子通常也具備較高的定製性,不須要過多地按照普適性解決方案來設計。在實現這樣定製型較高的的模塊時,一般可以帶來設計上的簡單性並更好地節約時間。這方面 SalesForce 的經驗能夠做爲參考:在實現一個系統的時候,若是讓各類流程和參數都充分地可定製,那麼總體上開發所需工做量可達到簡單實現的 2-3 倍。其實,在內部面向業務需求定製的組件若是追求靈活性很高的設計,那麼 API 設計一般會更加複雜,從而使得每次在使用時也會相對地更加複雜(例如可以自由傳入子組件的 React 組件,其複雜度和使用難度都比普通的組件要高很多),這時開發組件 + 使用組件的整體投入甚至有可能小於代碼模塊化的產出。更加可行的方法論是經過權衡實際業務場景,本着 KISS 原則去來保證明現上的簡單性,同時輕微地在正確性、完整性和一致性上作一些讓步,這樣會更容易產出和維護可複用的模塊。同時,許多工程化內容的落地難點實際也是工具引入上從 0 到 1 的一次性投入,例如在從 0 到 1 跑通第一個測試用例後,添加和完善用例的難度就小得多了。而且,實現這些特性的過程當中一般也可以接觸到更新和更完善的工具鏈,將內部輪子中小規模運用並驗證的的新特性再去逐步推廣到業務項目中,也是實踐中可行的方式。
然而,光有一兩個由核心成員業餘開發出的類庫,並不必定可以促進團隊總體的技術積累。在通常的模式下,可複用代碼的開發一般會交給團隊中經驗相對豐富的同窗,而這些同窗自己極可能還有更重要的業務問題去解決,這時候即使有了一次性的產出,後續的維護和迭代也是一個問題。爲此,咱們探索的模式更傾向於基於 Monorepo 的共建。什麼是 Monorepo 呢?這是一個管理機構代碼的方式。它摒棄了原先一個模塊對應一個 Repo 的組織方式,而是將全部的模塊都放在一個 Repo 中來管理,這也是目前 Babel / React / Angular / 等流行項目所應用的開發方式。早期的團隊項目中每每會將各個模塊倉庫拆分管理,造成 Multirepo 的模式,這時對於各自獨立的業務項目沒有關係的,但對於複用的模塊而言,這種模式主要會存在這樣的一些問題:
而 Monorepo 的方式,則是將團隊內積累的可複用模塊整合至同一個倉庫中來維護,這樣在團隊同窗有維護和改進的需求時,跑通環境然後 up and go 的流程會獲得很大程度上的簡化。以組件庫爲例,不一樣組件之間可能會存在相互依賴,而 Monorepo 就在依賴配置上有更好的支持,也易於整合提供更完善的構建工具,簡化發佈的流程。這時,共建的模式就從「個別同窗做爲上游承擔全部需求」,轉化爲「提供一個完善了構建和測試的基本框架來實現共建」。團隊成員能夠根據業務狀況抽取出公共組件整合至組件庫內,這個開發新組件的過程相對於本身另起爐竈會更加簡化,而且更重要的是在這個環節中,通常性業務開發中常常缺失的單元測試和 Review 是真正有機會去實施的,相對於對業務代碼的 Review 常常因爲進度問題而難以實施,在可複用組件的層面去作 Review 是更加有意義的。同時,內部的組件庫對 bug 的響應也可以更加迅速,依據 SemVer 高效發佈 patch 版本也可以更好地支持業務。在組件庫使用範圍爲團隊內部的前提下,組件庫外部依賴過多形成業務方版本更新時出現問題的可能性也會小得多。
除開用於基本業務開發的組件庫,Monorepo 裏還可以根據實際的業務情景,裝下許多應對團隊其它需求的內容。好比,對於常常須要開發臨時性小項目的團隊,整合了內部服務的 boilerplate 腳手架就是一個很好的可複用模板。不過這裏較爲特殊的一點是,腳手架的配置通常須要暴露,且能夠假設使用者熟悉相關的配置細節,在 Webpack 等工具的使用方式幾乎已成爲通用領域知識的前提下,對成熟工具進行二次封裝的內部工具在學習成本和靈活性上比較容易遇到問題。這裏咱們一方面能夠保持組件庫的小而美,另外一方面則能夠結合 repo 中的其餘複用模塊,精簡腳手架中的代碼到其它模塊中。通常在實際業務場景中,腳手架所整合、接入的一些內部服務很容易在不一樣業務中暴露出一些問題,這時候使用腳手架的團隊成員也很容易經過 PR 的形式將 bugfix 同步回原倉庫,這個流程的參與門檻也並不高,但不能小看維護者增加對項目長期穩定性的幫助。做爲一個小技巧,咱們不妨去除掉每一個源碼文件中自動生成的 @author
註釋,代之以在根目錄中放置開源項目中常見的 CONTRIBUTOR
/ AUTHORS
文件或 package.json
中的 authors 字段,這裏的關注點就從「背鍋」轉化爲了「參與」。
咱們的團隊組建時間並不長,主要的業務場景是開發和維護較多的 PC 端業務系統,這裏分享一點技術積累的產出來拋磚引玉吧?
因爲通常而言團隊內部基本上都會但願能使用統一的編碼規範和技術棧,這時候腳手架實際上是一個很不錯的切入點。腳手架也是咱們最先實現、業務使用最普遍、同時有最多同窗參與維護的輪子。看看國內前端社區鋪天蓋地的「xx + yy + zz 項目模板」就知道,實現一個腳手架的難度並不高,但就提高開發效率,節約團隊同窗時間而言,它確實有在團隊內部推廣的價值。在開始階段的腳手架其實就是一個 Koa + React / Redux 的 Webpack 模板,隨着業務複雜度增長,腳手架中除了整合各類接入的服務外,還出現了愈來愈多的參數配置。在這個過程當中它開始逐漸變得沉重、不易上手。爲了改善這個問題,咱們除了按照「如何配置以知足常見需求」的方式完善了文檔外,還進行了登陸服務、代理服務的依賴化,將基礎性的業務需求以 Koa 中間件的形式抽離以實現更好的複用。
咱們對腳手架自己的定位更偏向於項目起始的模板,而不是一套對 Webpack 的封裝。實際上許多對 Grunt / Gulp / Webpack 等工具作二次封裝定製出後的新工具,雖然在必定程度上可以更快地開發出原型,但在須要團隊其它成員定製、維護時,團隊成員對主流工具的使用經驗難以應用到這些封裝後的工具上,尤爲在內部文檔不夠詳盡的時候,這些定製的工具每每更加難以推廣。所以,咱們在維護腳手架時,更加偏向於將 Webpack 的配置清晰地暴露給使用者,不在這裏摻雜額外的 Magic。目前,團隊過半同窗腳手架已經加入了腳手架的 AUTHORS 名單內,咱們但願它可以做爲一個長期的積累,獲得持續的維護。
React 組件庫是另外一個已有必定產出的團隊積累項目。組件庫的開發除了整合默認 UED 交互,知足業務需求外,更多地成爲了一個新技術的試驗田。組件庫中落地了單元測試、Publish Hook 等質量保障的特性,也更多地嘗試了新工具,更是首先引入了 Monorepo 的管理方式,貢獻組件的流程也實現了自動化。目前雖然組件規模有限,但咱們一樣以提高團隊技術積累的願景在持續維護。
固然了,團隊中並非全部同窗都有時間和精力可以貢獻新組件,維護組件庫也不是強制性的要求。但對於沒有參與實際組件開發的同窗,也並不是沒有參與的方式。例如,每位同窗能夠在本身負責的業務中使用組件庫中的組件,反饋問題、提出 Issue,或者也能夠交流、Review 組件的 API 設計。開源項目對 Issue 的響應通常在天級,而組內的扁平交流可以將組件 bugfix 更新的速度提升到小時級,對於業務迭代較快的團隊,具有內部組件庫的高效響應能力是頗有幫助的。
腳手架和組件庫都是面向客戶端 JavaScript 的技術,而在前端同窗須要對所負責的體驗作更高程度的優化時,咱們的能力範圍也須要拓展到 Node 服務端。雖然對於 Node 層的存在曾經有過必定的爭議,但實踐證實 Node 在性能上對於 Web 服務這樣主要瓶頸在於 IO 的服務是足夠的,而維護性上「回調地獄」也早已在語言機制的改進中獲得瞭解決。目前對 Node 層的使用,主要目的而非深刻到後端所專一的數據接口中去替代後端,而是爲了提升開發效率,解決前端服務部署與渲染的問題,提高前端在整個應用生命週期中的服務能力。在這種場景下,前端對 Node 的使用實際上是很輕量的,但做爲 Web 服務,Node 端一樣須要接入許多較爲基礎的服務,如性能、日誌、監控、告警等。和瀏覽器端一言不合造輪子的方式不一樣的是,這類內容多數已有成熟的服務,在 Node 端所需實現的主要也是一些接入服務所需的代碼。一樣地,這些內容也很適合在 Monorepo 中提供可供快速驗證的 demo,或整合到團隊的 boilerplate 項目中,提供從本地調試到線上部署的支持。這時共建的模式一樣適用,而且在團隊業務覆蓋面較廣時,許多服務的接入更可能首先來自於業務同窗,然後再整合至 Monorepo 中。這種模式也是很合適的。
從先後端分離到 Node 出現,再到各種框架全家桶興起,以及如今的服務端渲染和 PWA,近年來前端的技術發展確實至關快速,可謂「惟一不變的是變化自己」。本文中想要探索的其實也是前端團隊在技術的演進中積累技術沉澱的若干一些小技巧和較爲通常性的規律,但願拋磚引玉能對有耐心讀到這裏的前端同窗們有所幫助 :-)
【 做者招募 】
咱們 GitChat 通過半年的運營,在產品形態上不斷改進。
此次,咱們將系統性的優化 GitChat 一些內容,在原有 Chat 基礎上推出新的內容分享產品:
GitQ 精品課程
若是您在 IT 技術上有本身獨到的學習心得 或 自成體系的技術成長套路,咱們很是期待您能在 GitQ 上進一步實現:
GitQ 定位:具有專業性、成體系的IT類課程
GitQ 形態:獨家文章(6~12篇)+讀者圈答疑
GitQ 訂價:9.99 / 19.99 等【做者如何開設 GitQ 精品課?】
提交以下內容:課程名稱 / 用戶定位 / 課程大綱
GitChat 進行內容評估與課程策劃
雙方肯定課程細節、交付時間
GitChat 完成課程的上線、宣傳推廣
【做者注意事項】GitQ 精品課內的文章,在課程上線一年內屬於 GitChat 獨家使用。
按約定時間準時交付文章。
課程上線後,積極在讀者圈回答用戶的提問。
【更多諮詢】
可在文末留言,或加微信聯繫 GitChat 主編:ztx415