Flutter 食髓知味

Zwe1發表於2020-01-08

移動開發演變

  1. 原生開發

   早期針對各平臺(作業系統差異),使用相應平臺支援的開發語言,開發應用。就 ios 和 android 兩大陣營來講,需要兩套程式碼來實現一款應用,生產效率相對較低。

  1. 混合開發(Hybrid)

   H5 技術的出現,為 web 執行在原生應用中提供了更好的支援。webview + jsBridge 讓 web 開發者也能夠開發原生應用,並且一套程式碼適配兩端。

  1. 原生渲染

   前端框架群雄逐鹿,React 社群推出了 React-Native ,通過 JS 控制原生控制元件,同樣做到了適配兩端,但效能依然是瓶頸,JS 語言成為了限制所在。

  1. 自制 UI 引擎

   吸收其他框架所長,自制 UI 引擎。Flutter 使用 dart 語言開發,靜態型別,編譯型語言,AOT 讓執行時速度更快。既不使用 webview 也不使用作業系統原生控制元件, 自制 UI 引擎,可以保證在 android 和 ios 的一致性。

Flutter UI

繪圖原理

   一切前端編碼都是 GUI 程式設計,即針對於作業系統硬體繪圖能力的應用。

     UI (使用者介面)
        /\
        ||
        GPU
        /\
        ||
        CPU
複製程式碼

   使用者介面由大量的顯像單元組成,每個畫素點的顏色構成了整個檢視介面。我們通過編碼來控制畫素點色號,從而控制檢視成像。編碼發出系統指令,CPU 首先根據指令計算內容資料,再交由 GPU 生成每個畫素點的色彩資料,最終輸出給顯像單元呈色。

   flutter 在原生應用平臺之上構建了一套完整的 UI 系統,呼叫 OpenGL 庫來間接呼叫作業系統 API。從而實現跨平臺操作,並且保證了原生的渲染效能。

    Flutter
      ||
      \/
    OpenGL
      ||
      \/
    OS API
複製程式碼

Flutter UI 系統

   有 web 開發經驗,使用過諸如 React, vue 等框架的同學,理解 Flutter 系統會更加容易些。吸收現代前端系統架構,採用元件化及宣告式,Flutter 也是這一思想的踐行者。

    Widget                  Component
      ||                       ||
      \/                       \/
    Element                VirtualDOM
      ||                       ||
      \/                       \/
  RenderObject                dom
複製程式碼

   同樣以元件為 UI 構成的最小單元,將檢視單元抽象成相應的資料結構單元,在互動導致的狀態變化時使用優化後的對比演算法來獲取最小修改集合,通過 batchUpdate 來更新檢視, 整體思想與 web 前端框架如出一轍。

   其中 Widget (同比元件) 分為 StatelessWidget 和 StatefulWidget 兩類,可類比無狀態元件和有狀態元件。

Dart 與 Flutter

  1. Dart 單執行緒模型

使用 Event Loop 事件排程模型,Dart 與 JS 同樣選用單執行緒迴圈執行任務。

  1. final 和 const

為什麼 dart 要支援兩種不可變數宣告方式,final 和 const 到底有什麼不一樣的?

var count = 10;
final Num = count;  // final 只能賦值一次
const Num1 = 10; // const賦值必須是編譯時常量
複製程式碼

const 特殊之處在於,它所宣告的變數值必須是執行時常量。

  1. 定義必傳引數
const Scrollbar({Key key, @required Widget child})
複製程式碼

通過 @required 修飾的命名引數為必傳引數,呼叫時不傳會導致編譯報錯。

  1. 初始化列表

dart 支援了在建立例項時,初始化例項物件成員的能力,被稱為 初始化列表

import 'dart:math';

class Point {
  final int x;
  final int y;
  final num distance;

  // 在 : 後進行初始化列表操作
  Person(x, y):
    x = x,
    y = y,
    distance = sqrt(x * x + y * y);
}

void main() {
  final i = new Point(2, 3);
  print(i.distance)
}
複製程式碼
  1. Widget build

widget 需要實現 build,作為 UI 輸出函式。

Widget build(BuildContext context);
複製程式碼

context 為當前結點的上下文,指代元件例項物件。

  1. 建立 cacheable 物件

如果你想建立快取物件,可以考慮使用工廠建構函式。

class Logger {
  final String name;
  bool mute = false;
  static final Map<String, Logger> _cache = {};

  factory Logger(String name) {
    // 工廠建構函式中不能使用 this
    if (_cache.contains(name)) {
      // 返回快取物件
      return _cache[name]
    } else {
      // 通過命名建構函式建立 Logger 例項
      final Logger log = Logger.internal(name);
      _cache[name] = log;
      return log;
    }
  }

  Logger.internal(this.name);

  void log (String msg) {
    if (!mute) print(msg);
  }
}

var logger = Logger('UI');
logger.log('clicked');
複製程式碼

使用體驗

優點

  1. 型別系統帶來的穩定性

   出身 web 開發,深切感受了 JS 語言編寫應用的不穩定性,其根本原因是 JS 為弱型別語言,無 AOT 機制,即便引入了 Typescript 還是無法徹底解決這一問題,因為很多前端開發人員並沒有意識到面向介面程式設計的利好。
   在 Flutter 中並不存在問題,Flutter 使用的 dart 語言自帶型別系統,支援 JIT 的同時也支援 AOT,型別為必選項,並且如果型別錯誤是無法編譯通過的。
   於是在我們型別編寫無誤的情況下,編譯階段可以幫助我們檢驗編碼中的錯誤, 杜絕取值隱患,鮮有可能再出現類似如下錯誤。

Flutter 食髓知味
  1. 優質的使用者使用體驗

   在 Flutter 之前,也有 RN ,Weex 等跨平臺移動端框架,但由於其設計是在原生系統之上又構建了一個統一排程層,因此在效能表現上有所下降,尤其在動畫渲染中需要時刻同步原生系統,不足之處更為明顯。
   Flutter 使用了自制 UI 引擎,在底層相容了平臺差異,取代了 Android 與 Ios 原生渲染引擎,暴露給開發者的一致性的 API,這一點既保證了跨平臺相容,也解決了效能問題。

缺點

  1. widget 巢狀地獄

   Flutter 在語法層面也並非盡善盡美,通過組合來構建 UI 的常見模式廣為接受,但其語法設定顯得十分累贅。

Flutter 食髓知味

   簡單的樣式描述,就需要許多 widget 進行組合,導致一小塊的 UI 結構可能就需要盡百行的程式碼來繪製,這對於應用的可讀,後續維護,業務擴充套件都是極其不利的。
   或許可以參考形如 React 中 JSX 語法,支援如 DartX,通過 XML 和 style 的組合,來簡化結構,增強可讀性。這一點在社群也有諸多共鳴

相關文章