怎樣寫好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規則的時候需要關注規則對應的成熟度。目前主要的成熟度等級包括:

  1. Stable - 穩定
  2. Experimental - 實驗性規則,可能會不太穩定
  3. Deprecated - 不再推薦使用的規則,建議廢棄

常見的Linter規則集

  1. effective_dart

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

  2. pedantic

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

  3. flutter_lints

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

  4. lints

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

閒魚規範的取捨

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

表達簡潔

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

  1. 去除冗餘new關鍵字【unnecessary_new】

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

  1. 高效null處理【prefer_conditional_assignment, prefer_null_aware_operators】

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

減少歧義

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

  1. 顯式變量類型【always_specify_types】

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

  1. 變量/函數狀態堅持最小化表達 【annotate_overrides,prefer_const_declarations,prefer_final_fields】

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

  1. 最小化變量狀態表達能精準刻畫變量狀態
    最小化變量狀態是指用最嚴格的屬性描述變量。例如一個變量從之前的多處賦值,改爲一處賦值。那麼變量其實自帶了final的隱形屬性。最小化狀態表達要求必須加上final關鍵字,而不是推斷是final。因爲一旦加上final關鍵字,後續在賦值就會報錯。這能減少不必要的理解成本,最大限度避免變量狀態的隱形改變。
  2. 提升代碼性能
    增加的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]
  2. 註釋中的變量引用,符合引用的約束[comment_references]

規則權威

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

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

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

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

寫在最後

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

附錄

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

linter-rules

閒魚核心Liner規則

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

命名

排序

格式化

評論

庫使用

NULL

字符串

集合

函數

成員變量

構造函數

異常處理

Mixin

類型

參數

質量

Core集合融合部分

Recommended 集合融合部分

Flutter 集合融合部分

其他規則融合部分

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