怎樣寫好 Flutter 代碼

緣起

代碼規範作爲一個研發團隊的核心基因,怎樣在團隊中高效傳承是一個挑戰。Flutter 作爲移動端持續火熱的新型框架,正吸引越來越多的研發同學進入這個領域。但是面對全新的技術框架和之前幾乎很少接觸的 dart 語言,怎樣寫好 Flutter 代碼困擾了無數 Flutter 初學者。文本就着重分享一下閒魚 Flutter 團隊在基礎代碼規範方面所做的一些實踐。

Flutter 靜態代碼掃描

Native 開發同學都知道 Android/iOS 都有配套的 Linter 檢查機制。開發者可以通過 Linter 檢查,統一代碼風格,檢查代碼中的明顯缺陷。Flutter 同樣具備這樣的能力。通過 Linter 檢查機制,是統一代碼風格最簡單高效的方式。

Flutter Linter 機制

Flutter Linter 檢查的機制植根於 Dart Linter 檢查,同時做了大量的功能拓展。Flutter 的 Linter 規則記錄在工程一級目錄的 analysis_options.yaml 文件中。大家可以通過對該文件的定製來獲得適合自己團隊的代碼規則。

Flutter Linter 規則都從屬於三個主要的集合:

1.ERROR

規則標識的是代碼中可能出現的錯誤

2.Style

規則標識的是代碼風格的問題

3.Pub

規則標識的是 Flutter 包管理相關的問題

需要注意的是,考慮到實際開發場景中的穩定性。調整 Linter 規則的時候需要關注規則對應的成熟度。目前主要的成熟度等級包括:

常見的 Linter 規則集

1.effective_dart

effective_dart 是 dart 語言配套的 Linter 規則。規則來源於 Effective Dart 文檔。該文檔中有對 Dart 語言高效使用的各種規範。effective_dart[2] 規則比較老,目前已經廢棄。

2.pedantic

google 內部 dart 開發規範集合。目前也已經廢棄。

3.flutter_lints

是 Flutter 團隊推薦的 Flutter 相關規則集。推薦大家在 Flutter apps, packages, and plugins 中廣泛使用。本身是 dart recommended 規則集合的拓展版本。該規範會影響到發佈到 pub.dev[5] 中 package 的得分。

4.lints[6]

Dart 團隊推薦使用的規則集。核心包括兩個子集:core & recommended。

閒魚規範的取捨

在衆多 Liner 規則中,選擇符合團隊需要的規則是一個很大的挑戰。閒魚在做的過程中,遵循了一些基本原則。

表達簡潔

在語義等效的情況下,充分利用 dart 語言的各種高級語法來降低表達的複雜度。簡潔表達不僅能提升編碼效率,同時也可以提升代碼的閱讀效率。舉幾個典型例子:

閒魚代碼庫中有大量的代碼都是使用 new 關鍵字來進行類初始化的。這一方面是因爲閒魚切入 Flutter 比較早,有歷史包袱;另一方面也是因爲客戶端同學將其他語言(例如 java)的編碼習慣帶到了 Flutter。雖然要修改的地方有很多,但是爲了表達簡潔,我們依然堅定的加入這條規則。

dart 語法中有很多 null 處理的語法糖,能大幅提升判空的效率,提升代碼的健壯性。這裏包括?的判斷,?? 的判空,以及 null safety。需要不斷引導團隊同學更多使用這樣的語法來提升效率,而不是繼續使用 if else 的冗雜表達。

減少歧義

代碼表達需要準確,同學之間的理解儘可能一致。減少代碼之外的 “以爲”,增加字裏行間上的 “確定”。典型列子如下:

是否要明確變量類型,團隊內部不同技術棧背景的同學有比較大的分歧。前端背景的同學傾向於通過推斷的方式進行變量命名,好處是表達簡潔。客戶端背景同學傾向於直接明確定義,好處是直觀,減少不必要的推斷。最終經過激烈討論,大家還是決定採用明確變量類型的方案。從實踐角度出發,直接明確變量類型雖然從表達上略冗雜,但是歧義確實更少。

一個變量或者方法如果重寫了,請明確加上 override 註解。一個變量如果確定是 const 變量,請加上 const 關鍵字。一個變量如果沒有再被賦值,請定義成 final。堅持最小化表達,有如下幾點收益:

最小化變量狀態是指用最嚴格的屬性描述變量。例如一個變量從之前的多處賦值,改爲一處賦值。那麼變量其實自帶了 final 的隱形屬性。最小化狀態表達要求必須加上 final 關鍵字,而不是推斷是 final。因爲一旦加上 final 關鍵字,後續在賦值就會報錯。這能減少不必要的理解成本,最大限度避免變量狀態的隱形改變。

增加的 final const 關鍵字能讓編譯器做更多優化,提升代碼的整體性能。

風格一致

多人開發的團隊中,每個人都有自己的喜好。代碼管理應該避免出現 “破窗效應”。一個人把代碼寫爛了,後面的同學照着寫都爛了。正所謂,無規矩不成方圓。基礎代碼的表達,不追求絕對的對與錯,追求的是在風格上的儘可能統一。這裏有太多典型例子:

  1. 命名規範大體遵循駝峯的方式【camel_case_types,non_constant_identifier_names,constant_identifier_names 】

  2. 控制流中儘量使用大括號【curly_braces_in_flow_control_structures】

  3. import 順序按照先 dart 引用,再 package 引用,再相對引用方式分模塊,模塊內引用按字母表排序【directives_ordering】

  4. required 關鍵字標識的變量請排到前面 【always_put_required_named_parameters_first】

  5. flutter 佈局中優先使用 SizedBox 而不是 Container【sized_box_for_whitespace】

  6. flutter 顏色定義使用 8 位 16 進制整數標識顏色值【use_full_hex_values_for_flutter_colors】

  7. flutter widget 在構造函數中加入 key 參數【use_key_in_widget_constructors】

這裏的每一條都不具有非這樣不可的理由。但是在閒魚寫代碼就必須遵循這個規範。不爲別的就爲了風格的統一,爲了提升團隊同學業務輪轉的效率。

代碼質量

Linter 檢查的另一個重要的目標是發現潛在的代碼缺陷。這對代碼管理來說就更爲重要了,因爲這直接關係到穩定性的大局。穩定性是開發人員的底線,再怎麼小心也不爲過。同樣舉幾個典型的例子:

  1. 避免給 void 賦值 【void_checks】

  2. 變量比較之前先判斷類型 【unrelated_type_equality_checks】

  3. 避免 catch 空實現【empty_catches】

  4. 避免使用隱形類型傳遞 【avoid_shadowing_type_parameters】

  5. 避免 await 非 future 對象 【await_only_futures】

  6. 集合的 remove 需要傳遞符合集合的類型的參數【list_remove_unrelated_type】

如果這些問題代碼帶到生產環境,輕者出現 exception,重則功能不可用。防患於未然,在代碼最開始編寫階段,就及時處理這些問題,代碼才能更健壯。

維護成本

效率對一個團隊固然很重要,但是追求一時的效率,而不顧及代碼生命週期內整體的效率是非常短視的行爲。我們今天越發關注代碼在後期的維護成本。基於此,文檔 & 註釋的重要性正越發凸顯出來。一個好的註釋,能省下很多的答疑,很多的試錯,很多的猜疑。不斷引導大家寫並維護文檔及註釋,是閒魚代碼規範明確的目標。舉幾個明確的例子:

1.Deprecated 函數需要給出明確的註釋 [provide_deprecation_message]

  1. 註釋中的變量引用,符合引用的約束 [comment_references]

規則權威

閒魚團隊 Liner 規則的制定,不是一家之言。邀請了團隊所有同學參與到規則的制定過程。一個規則的權威性來源於所有執行者發自內心的認同。舉一個典型例子:

在討論標準格式化的時候,團隊同學集中討論了一個規則:

【- lines_longer_than_80_chars # 爭議規則,每行長度不超過 80 字符】

大家認同統一格式化代碼風格的重要性。大家爭議的焦點是:80 字符在很多高分辨率顯示器上太短了。這會導致很多不必要的換行,降低代碼閱讀體驗。我們聽取了開發同學反饋,把該條規範去掉了。改爲統一 android studio 配置來執行。最終標準是 12 號字,一行最多 160 字符。

寫在最後

當然僅僅做到上面的部分,僅僅是萬里長征的第一步。基礎代碼規範這件事,我們並不追求規則的多,而是更在意規則實際的效率收益。規則制定的過程中,我們聽取了開發同學真實的反饋,刪除了很多並不必要的規則。簡單,高效,並不爲了規範而規範是我們核心的目標。當然最終確定的規則,要盡最大努力保證執行。目前我們正在積極建設更完善的 CI 體系,將規範規則化。通過持續快速檢測,實時給使用者反饋,降低規範落地的成本。代碼是研發團隊的核心資產,需要我們共同守護。

附錄

上面介紹的規則細節,如果有不清楚的,可以在下面的鏈接中找到更詳細的解釋。

linter-rules[7]

閒魚核心 Liner 規則

附上閒魚的核心 Liner 規則,給大家一點參考。

命名

排序

格式化

評論

庫使用

NULL

字符串

集合

函數

成員變量

構造函數

異常處理

Mixin

類型

參數

質量

Core 集合融合部分

Recommended 集合融合部分

Flutter 集合融合部分

其他規則融合部分

References

[1] effective_dart: https://pub.dev/packages/effective_dart
[2] effective_dart: https://pub.dev/packages/effective_dart
[3] pedantic: https://pub.dev/packages/pedantic
[4] flutter_lints: https://pub.dev/packages/flutter_lints
[5] pub.dev: https://pub.dev/
[6] lints: https://pub.dev/packages/lints
[7] linter-rules: https://dart.dev/tools/linter-rules#lints

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Xsp4pdxqdyGovXQmXcKk7A