給2019前端的5個建議

原發於知乎專欄,歡迎關注 zhuanlan.zhihu.com/p/55357377javascript

2019 農曆新年即將到來,是時候總結一下團隊過去一年的技術沉澱。過去一年咱們支撐的數據相關業務日新月異,其中兩個核心平臺級產品代碼量分別達到30+萬行和80+萬行,TS 模塊數均超過1000個,協同開發人員增長到20+人。因爲歷史緣由,開發框架同時基於 React 和 Angular,考慮到產品的複雜性、人員的短缺和技術背景各異,咱們嘗試了各類方法打磨工具體系來提高開發效率,如下是節選的5項主要方法。css

1、基於 Redux 的狀態管理

從2013年React發佈至今已近6個年頭,前端框架逐漸造成 React/Vue/Angular 三足鼎立之勢。幾年前還在爭論單向綁定和雙向綁定孰優孰劣,如今三大框架已經不約而同選擇單向綁定,雙向綁定淪爲單純的語法糖。框架間的差別愈來愈小,加上 Ant-Design/Fusion-Design/NG-ZORRO/ElementUI 組件庫的成熟,選擇任一你熟悉的框架都能高效完成業務。前端

那接下來核心問題是什麼?咱們認爲是 狀態管理。簡單應用使用組件內 State 方便快捷,但隨着應用複雜度上升,會發現數據散落在不一樣的組件,組件通訊會變得異常複雜。咱們前後嘗試過原生 Redux、分形 Fractal 的思路、自研類 Mobx 框架、Angular Service,最終認爲 Redux 依舊是複雜應用數據流處理最佳選項之一。java

慶幸的是除了 React 社區,Vue 社區有相似的 Vuex,Angular 社區有 NgRx 也提供了幾乎一樣的能力,甚至 NgRx 還能夠無縫使用 redux-devtools 來調試狀態變化。webpack

image.png

不管如何優化,始終要遵循 Redux 三原則:git

原則 方法 引起的問題
Single source of truth 組件 Stateless,數據來源於 Store 如何組織 Store?
State is read-only 只能經過觸發 action 來改變 State action 數量膨脹,大量樣板代碼
Changes are made with pure functions Reducer 是純函數 反作用如何處理,大量樣板代碼

這三個問題咱們是經過自研 iron-redux 庫來解決,如下是背後的思考:github

如何組織 Action?web

  1. action type 須要全局唯一,所以咱們給 action type 添加了 prefix,其實就是 namespace 的概念
  2. 爲了追求體驗,請求(Fetch)場景須要處理 3 種狀態,對應 LOADING/SUCCESS/ERROR 這 3 個action,咱們經過 FetchTypes 類型來自動生成對應到 3 個 action

如何組織 Store/Reducer?typescript

  1. reducer 和 view 沒必要一一對應,應用中同時存在組件樹和狀態樹,按照各自須要去組織,經過 connect 來綁定狀態樹的一個或多個分支到組件樹
  2. 經過構造一些預設數據類型來減小樣板代碼。對於 Fetch 返回的數據咱們定義了 AsyncTuple 這種類型,減小了樣板代碼
  3. 明確的組織結構,第1層是 ROOT,第2層是各個頁面,第3層是頁面內的卡片,第4層是卡片的數據,這樣劃分最深處基本不會超過5層

最終咱們獲得以下扁平的狀態樹。雖龐大但有序,你能夠快速而明確的訪問任何數據。redux

Redux 狀態樹
[Redux 狀態樹]

如何減小樣板代碼? 使用原生 Redux,一個常見的請求處理以下。很是冗餘,這是 Redux 被不少人詬病的緣由

const initialState = {
  loading = true,
  error = false,
  data = []
};

function todoApp(state = initialState, action) {
  switch (action.type) {
    case DATA_LOADING:
      return {
        ...state,
        loading: true,
        error: false
      }
    case DATA_SUCCESS:
      return {
        ...state,
        loading: false,
        data: action.payload
      }
    case DATA_ERROR:
      return {
        ...state,
        loading: false,
        error: true
      }
    default:
      return state
  }
}
複製代碼

使用 iron-redux 後:

class InitialState {
  data = new AsyncTuple(true);
}

function reducer(state = new InitialState(), action) {
  switch (action.type) {
    /** 省略其它 action 處理 */
    default:
      return AsyncTuple.handleAll(prefix, state, action);
  }
}
複製代碼

代碼量減小三分之二!!
主要作了這2點:

  1. 引入了預設的 AsyncTuple 類型,就是 {data: [], loading: boolean, error: boolean}  這樣的數據結構;
  2. 使用 AsyncTuple.handleAll  處理 LOADING/SUCCESS/ERROR 這 3 種 action,handleAll 的代碼很簡單,使用 if 判斷 action.type 的後綴便可,源碼在這裏

曾經 React 和 Angular 是兩個很難調和的框架,開發中浪費了咱們大量的人力。經過使用輕量級的 iron-redux,徹底遵循 Redux 核心原則下,咱們內部 實現了除組件層之外幾乎全部代碼的複用。開發規範、工具庫達成一致,開發人員可以無縫切換,框架差別帶來的額外成本降到很低

2、全面擁抱 TypeScript

TypeScript 目前可謂大紅大紫,根據 2018 stateofjs,超過 50% 的使用率以及 90% 的滿意度,甚至連 Jest 也正在從 Flow 切換到 TS。若是你尚未使用,能夠考慮切換,絕對能給項目帶來很大提高。過去一年,咱們從部分使用 TS 變爲全面切換到 TS,包括咱們本身開發的工具庫等。

TS 最大的優點是它提供了強大的靜態分析能力,結合 TSLint 能對代碼作到更加嚴格的檢查約束。傳統的 EcmaScript 因爲沒有靜態類型,即便有了 ESLint 也只能作到很基本的檢查,一些 typo 問題可能線上出了 Bug 後才被發現。

下圖是一個前端應用常見的4層架構。 代碼和工具全面擁抱 TS 後,實現了從後端 API 接口到 View 組件的全鏈路靜態分析,具備了完善的代碼提示和校驗能力。

image.png
[先後端協做簡圖]

除了上面講的 iron-redux,咱們還引入 Pont 實現前端取數,它能夠自動把後端 API 映射到前端可調用的請求方法。

Pont 實現原理:(法語:橋) 是咱們研發的前端取數層框架。對接的後端 API 使用 Java Swagger,Swagger 能提供全部 API 的元信息,包括請求和響應的類型格式。Pont 解析 API 元信息生成 TS 的取數函數,這些取數函數類型完美,並掛載到 API 模塊下。最終代碼中取數效果是這樣的:

image.png

Pont 實現的效果有:

  1. 根據方法名自動匹配 url、method,而且對應到 prams、response 類型完美,並能自動提示
  2. 後端 API 接口變動後,前端相關聯的請求會自動報錯,不再擔憂後端悄悄改接口前端不知曉
  3. 不再須要先後端接口約定文檔,使用代碼保證前端取數和後端接口定義徹底一致

另外 iron-redux 能接收到 Pont 接口響應數據格式,並推導出整個 Redux 狀態樹的靜態類型定義,Store 中的數據完美的類型提示。效果以下:

image.png

最終 TS 讓代碼更加健壯,尤爲是對於大型項目,編譯經過幾乎就表明運行正常,也給重構增長了不少信心

3、迴歸 Sass/Less

2015 年咱們就開始實踐 CSS Modules,包括後來的 styled-components 等,到 2019 年 css-in-js 方案依舊爭論不休,雖然它確實解決了一些 CSS 語言天生的問題,但同時增長了很多成本,新手不夠友好、全局樣式覆蓋成本高漲、僞類處理複雜、與AntD等組件庫結合有坑。與此同時 Sass/Less 社區也在飛速發展,尤爲是 Stylelint 的成熟,能夠經過技術約束的手段來避免 CSS 的 Bad Parts。

  1. 全局污染:約定每一個樣式文件只能有一個頂級類,如 .home-page{ .top-nav {/**/}, .main-content{ /**/ } }。若是有多個頂級類,可使用 Stylelint rule 檢測並給出警告。
  2. 依賴管理不完全。藉助 webpack 的 css-loader,已夠用。
  3. JS 和 CSS 變量共享。關於 JS 和 Sass/Less 變量共享,咱們摸索出了本身的解法:
// src/styles/variables.js
module.exports = {
  // 主顏色
  'primary-color': '#0C4CFF',
  // 出錯顏色
  'error-color': '#F15533',
  // 成功顏色
  'success-color': '#35B34A',
};
複製代碼
// webpack.config.js
const styleVariables = require('src/styles/variables');

// ...
      {
        test: /\.scss$/,
        use: [
          'style-loader',
          'css-loader?sourceMap&minimize',
          {
            loader: 'sass-loader',
            options: {
              data: Object.keys(styleVariables)
                .map(key => `\$${key}: ${styleVariables[key]};`)
                .join('\n'),
              sourceMap: true,
              sourceMapContents: true
            }
          }
        ]
      }
//...
複製代碼

在 scss 文件中,能夠直接引用變量

// page.scss
.button {
  background: $primary-color;
}
複製代碼

4、開發工具覆蓋全鏈路

2019 年,你幾乎不可能再開發出 React/Angular/Vue 級別的框架,也不必再造 Ant-Design/Fusion-Design/Ng-Zorro 這樣的輪子。難道就沒有機會了嗎?

固然有,結合你自身的產品開發流程,依舊有不少機會。下面是常規項目的開發流程圖,任何一個環節只要深挖,都有提高空間。若是你能經過工具減小一個或多個環節,帶來的價值更大。

image.png

單拿其中的【開發】環節展開,就有不少可擴展的場景:

image.png

一個有表明性的例子是,咱們開發了國際化工具 kiwi。它一樣具備 TS 的類型完美,很是強大的文案提示,另外還有:

  1. VS Code 插件 kiwi linter,自動對中文文案標紅,若是已有翻譯文案能自動完成替換
  2. Shell 命令全量檢查出沒有翻譯的文案,批量提交給翻譯人員
  3. Codemod 腳本自動實現舊的國際化方案向 Kiwi 遷移,成本極低

除了以上三點,將來還計劃開發瀏覽器插件來檢查漏翻文案,利用 Husky 在 git 提交前對漏翻文案自動作機器翻譯等等。

將來若是你只提供一個代碼庫,那它的價值會很是侷限。你能夠參照上面的圖表,開發相應的擴展來豐富生態。若是你是新手,推薦學習下編譯原理和對應的擴展開發規範。

5、嚴格完全的 Code Review

過去的一年,咱們一共進行了 1200+ 屢次 Code Review(CR),不少同事從剛開始很差意思提 MR 到後來追着別人 Review,CR 成爲每一個人的習慣。經過 CR 讓項目中任何一行代碼都至少被兩人觸達過,減小了絕大多數的低級錯誤,提高了代碼質量,這也是幫助新人成長最快的方式之一。

image.png

【其中一個項目MR截圖】

Code Review 的幾個技巧:

  1. No magic
  2. Explicit not implicit
  3. 覆蓋度比深度重要,覆蓋度追求100%
  4. 頻率比儀式感重要,坐公交蹲廁所打開手機均可以 Review 別人代碼,不須要專門組織會議
  5. 粒度要儘量小,一個組件一個方法都可,能夠結合 Git Flow
  6. 24h 小時內處理,無問題直接 merge,有問題必定要留 comment,而且提供 action
  7. 對於亟待上線來不及 Review 的代碼,能夠先合併上線,上線後再補充 Review
  8. 須要自上而下的推進,具備完善的規範,同時按期總結 Review 經驗來豐富開發規範
  9. CR 並不僅是爲了找錯,看到好的代碼,不要吝嗇你的讚美
  10. 本質是鼓勵開發者間更多的溝通,互相學習,營造技術文化氛圍

總結

以上5點固然不是咱們技術的所有。除此以外咱們還實踐了移動端開發、可視化圖表/WebGL、Web Worker、GraphQL、性能優化等等,但這些還停留在術的層面,將來到必定程度會拿出來分享。

若是你也準備或正在開發複雜的前端應用,同時團隊人員多樣技術背景各異,能夠參考以上5點,使用 Redux 實現規範清晰可預測的狀態管理,深耕 TypeScript 來提高代碼健壯性和可維護性,藉助各類 Lint 工具迴歸簡單方便的 CSS,不斷打磨本身的開發工具來保證開發規範高效,並嚴格完全實行 Code Review 促進人的交流和提高。

Links

  1. Pont:github.com/nefe/pont
  2. Kiwi:github.com/nefe/kiwi
  3. iron-redux: github.com/nefe/iron-r…
  4. The State of JavaScript 2018

咱們還在招聘,歡迎郵件簡歷,來信必回。shaoyin.ssy@alibaba-inc.com

相關文章
相關標籤/搜索