2019最新Flutter 面試知識點集錦(一)


系統完整的學習是必須需要的,這里只能幫你總結一些知識點,更多的還請查閱 Dart/Flutter 官網

本篇主要是知識點總結,如有疑問可點擊各文章鏈接了解詳情,或者查閱我專欄

Dart 部分

其實學習過 JavaScript 或者 Java/Kotlin 的人,在學習 Dart 上幾乎是沒什么難度的,Dart 綜合了動態語言和靜態語言的特性, 這里主要提供一些不一樣,或者有意思的概念。

  • 1、Dart 屬於是強類型語言 ,但可以用 var 來聲明變量,Dart 會自推導出數據類型,var 實際上是編譯期的“語法糖”。dynamic 表示動態類型, 被編譯后,實際是一個 object 類型,在編譯期間不進行任何的類型檢查,而是在運行期進行類型檢查。
  • 2、Dart 中 if 等語句只支持 bool 類型,switch 支持 String 類型。
  • 3、Dart 中數組和 List 是一樣的。
  • 4、Dart 中,Runes 代表符號文字 , 是 UTF-32 編碼的字符串, 用於如 Runes input = new Runes(’\u{1f596} \u{1f44d}’);
  • 5、Dart 支持閉包。
  • 6、Dart 中 number 類型分為 int 和 double ,沒有 float 類型。
  • 7、Dart 中 級聯操作符 可以方便配置邏輯,如下代碼:
event
  ..id = 1
  ..type = ""
  ..actor = "";
  • 1
  • 2
  • 3
  • 4
  • 8、賦值操作符
    比較有意思的賦值操作符有:
AA ?? "999"  ///表示如果 AA 為空,返回999
AA ??= "999" ///表示如果 AA 為空,給 AA 設置成 999
AA ~/999 ///AA 對於 999 整除
  • 1
  • 2
  • 3
  • 9、可選方法參數
    Dart 方法可以設置 參數默認值 和 指定名稱

比如: getDetail(Sting userName, reposName, {branch = “master”}){} 方法,這里 branch 不設置的話,默認是 “master” 。參數類型 可以指定或者不指定。調用效果: getRepositoryDetailDao(“aaa", “bbbb”, branch: “dev”); 。

  • 10、作用域
    Dart 沒有關鍵詞 public 、private 等修飾符,_ 下橫向直接代表 private ,但是有 @protected 注解 。

  • 11、構造方法
    Dart 中的多構造方法,可以通過命名方法實現。

默認構造方法只能有一個,而通過 Model.empty() 方法可以創建一個空參數的類,其實方法名稱隨你喜歡,而變量初始化值時,只需要通過 this.name 在構造方法中指定即可:

class ModelA {
  String name;
  String tag;

  //默認構造方法,賦值給name和tag
  ModelA(this.name, this.tag);

  //返回一個空的ModelA
  ModelA.empty();

  //返回一個設置了name的ModelA
  ModelA.forName(this.name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 12、getter setter 重寫
    Dart 中所有的基礎類型、類等都繼承 Object ,默認值是 NULL, 自帶 getter 和 setter ,而如果是 final 或者 const 的話,那么它只有一個 getter 方法,Object 都支持 getter、setter 重寫:
  @override
  Size get preferredSize {
    return Size.fromHeight(kTabHeight + indicatorWeight);
  }
  • 1
  • 2
  • 3
  • 4
  • 13、Assert(斷言)
    assert 只在檢查模式有效,在開發過程中,assert(unicorn == null); 只有條件為真才正常,否則直接拋出異常,一般用在開發過程中,某些地方不應該出現什么狀態的判斷。
  • 14、重寫運算符,如下所示重載 operator 后對類進行 +/- 操作。
class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

  ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 2);

  assert(v + w == Vector(4, 5));
  assert(v - w == Vector(0, 1));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

支持重載的操作符 :
在這里插入圖片描述

  • 類、接口、繼承
    Dart 中沒有接口,類都可以作為接口,把某個類當做接口實現時,只需要使用 implements ,然后復寫父類方法即可。

Dart 中支持 mixins ,按照出現順序應該為extends 、 mixins 、implements 。

Zone

Dart 中可通過 Zone 表示指定代碼執行的環境,類似一個沙盒概念,在 Flutter 中 C++ 運行 Dart 也是在 _runMainZoned 內執行 runZoned 方法啟動,而我們也可以通過 Zone ,在運行環境內捕獲全局異常等信息:

  runZoned(() {
    runApp(FlutterReduxApp());
  }, onError: (Object obj, StackTrace stack) {
    print(obj);
    print(stack);
  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

同時你可以給 runZoned 注冊方法,在需要時執行回調,如下代碼所示,這樣的在一個 Zone 內任何地方,只要能獲取 onData 這個 ZoneUnaryCallback,就都可以調用到 handleData

///最終需要處理的地方
handleData(result) {
  print("VVVVVVVVVVVVVVVVVVVVVVVVVVV");
  print(result);
}

///返回得到一個 ZoneUnaryCallback 
var onData = Zone.current.registerUnaryCallback<dynamic, int>(handleData);

///執行 ZoneUnaryCallback 返回數據
Zone.current.runUnary(onData, 2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

異步邏輯可以通過 scheduleMicrotask 可以插入異步執行方法:

Zone.current.scheduleMicrotask((){
  //todo something
});
  • 1
  • 2
  • 3

Future

Future 簡單了說就是對 Zone 的封裝使用。

比如 Future.microtask 中主要是執行了 Zone 的 scheduleMicrotask ,而 result._complete 最后調用的是 _zone.runUnary 等等。

  factory Future.microtask(FutureOr<T> computation()) {
    _Future<T> result = new _Future<T>();
    scheduleMicrotask(() {
      try {
        result._complete(computation());
      } catch (e, s) {
        _completeWithErrorCallback(result, e, s);
      }
    });
    return result;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Dart 中可通過 async/await 或者 Future 定義異步操作,而事實上 async/await 也只是語法糖,最終還是通過編譯器轉為 Future。

Stream

Stream 也是有對Zone 的另外一種封裝使用。

Dart 中另外一種異步操作, async* / yield 或者 Stream 可定義 Stream 異步, async* / yield 也只是語法糖,最終還是通過編譯器轉為 Stream。
Stream 還支持同步操作。

1)、Stream 中主要有 Stream 、 StreamController 、StreamSink 和 StreamSubscription 四個關鍵對象,大致可以總結為:

  • StreamController :如類名描述,用於整個 Stream 過程的控制,提供各類接口用於創建各種事件流。
  • StreamSink :一般作為事件的入口,提供如 add , addStream 等。
  • Stream :事件源本身,一般可用於監聽事件或者對事件進行轉換,如 listen 、where 。
  • StreamSubscription :事件訂閱后的對象,表面上用於管理訂閱過等各類操作,如 cacenl 、pause ,同時在內部也是事件的中轉關鍵。

2)、一般通過 StreamController 創建 Stream;通過 StreamSink 添加事件;通過 Stream 監聽事件;通過 StreamSubscription 管理訂閱。

3)、Stream 中支持各種變化,比如map 、expand 、where 、take 等操作,同時支持轉換為 Future 。

Flutter 部分

Flutter 和 React Native 不同主要在於 Flutter UI是直接通過 skia 渲染的 ,而 React Native 是將 js 中的控件轉化為原生控件,通過原生去渲染的

  • Flutter 中存在 Widget 、 Element 、RenderObject 、Layer 四棵樹,其中 Widget 與 Element 是多對一的關系 ,
  • Element 中持有Widget 和 RenderObject , 而 Element 與 RenderObject 是一一對應的關系 ,
  • 當 RenderObject 的 isRepaintBoundary 為 true 時,那么個區域形成一個 Layer,所以不是每個 RenderObject 都具有 Layer 的,因為這受 isRepaintBoundary 的影響。
  • Flutter 中 Widget 不可變,每次保持在一幀,如果發生改變是通過 State 實現跨幀狀態保存,而真實完成布局和繪制數組的是 RenderObject , Element 充當兩者的橋梁, State 就是保存在 Element 中。
  • Flutter 中的 BuildContext 只是接口,而 Element 實現了它。
  • Flutter 中 setState 其實是調用了 markNeedsBuild ,該方法內部標記此Element 為 Dirty ,然后在下一幀 WidgetsBinding.drawFrame 才會被繪制,這可以看出 setState 並不是立即生效的。
  • Flutter 中 RenderObject 在 attch/layout 之后會通過 markNeedsPaint(); 使得頁面重繪,流程大概如下:
    在這里插入圖片描述
    通過isRepaintBoundary 往上確定了更新區域,通過 requestVisualUpdate 方法觸發更新往下繪制。
  • 正常情況 RenderObject 的布局相關方法調用順序是 : layout -> performResize -> performLayout -> markNeedsPaint , 但是用戶一般不會直接調用 layout,而是通過 markNeedsLayout ,具體流程如下:
    在這里插入圖片描述
  • Flutter 中一般 json 數據從 String 轉為 Object 的過程中都需要先經過 Map 類型。
  • Flutter 中 InheritedWidget 一般用於狀態共享,如Theme 、Localizations 、 MediaQuery 等,都是通過它實現共享狀態,這樣我們可以通過 context 去獲取共享的狀態,比如 ThemeData theme = Theme.of(context);

在 Element 的 inheritFromWidgetOfExactType 方法實現里,有一個 Map<Type, InheritedElement> _inheritedWidgets 的對象。
_inheritedWidgets 一般情況下是空的,只有當父控件是 InheritedWidget 或者本身是 InheritedWidgets 時才會有被初始化,而當父控件是 InheritedWidget 時,這個 Map 會被一級一級往下傳遞與合並 。
所以當我們通過 context 調用 inheritFromWidgetOfExactType 時,就可以往上查找到父控件的 Widget 。

  • Flutter 中默認主要通過 runtimeType 和 key 判斷更新:
static bool canUpdate(Widget oldWidget, Widget newWidget) {
    return oldWidget.runtimeType == newWidget.runtimeType
        && oldWidget.key == newWidget.key;
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5

Flutter 中的生命周期

  • initState() 表示當前 State 將和一個 BuildContext 產生關聯,但是此時BuildContext 沒有完全裝載完成,如果你需要在該方法中獲取 BuildContext ,可以 new Future.delayed(const Duration(seconds: 0, (){//context}); 一下。
  • didChangeDependencies() 在 initState() 之后調用,當 State 對象的依賴關系發生變化時,該方法被調用,初始化時也會調用。
  • deactivate() 當 State 被暫時從視圖樹中移除時,會調用這個方法,同時頁面切換時,也會調用
  • dispose() Widget 銷毀了,在調用這個方法之前,總會先調用 deactivate()。
  • didUpdateWidge 當 widget 狀態發生變化時,會調用。
    在這里插入圖片描述
  • 通過 StreamBuilder 和 FutureBuilder 我們可以快速使用 Stream 和 Future 快速構建我們的異步控件
  • Flutter 中 runApp 啟動入口其實是一個 WidgetsFlutterBinding ,它主要是通過 BindingBase 的子類 GestureBinding 、ServicesBinding 、 SchedulerBinding 、PaintingBinding 、SemanticsBinding 、 RendererBinding 、WidgetsBinding 等,通過 mixins 的組合而成的
  • Flutter 中的 Dart 的線程是以事件循環和消息隊列的形式存在,包含兩個任務隊列,一個是 microtask 內部隊列,一個是 event 外部隊列,而 microtask 的優先級又高於 event 。

因為 microtask 的優先級又高於 event, 同時會阻塞event 隊列,所以如果 microtask 太多就可能會對觸摸、繪制等外部事件造成阻塞卡頓哦。

    • Flutter 中存在四大線程,分別為 UI Runner、GPU Runner、IO Runner, Platform Runner (原生主線程) ,同時在 Flutter 中可以通過 isolate 或者 compute 執行真正的跨線程異步操作。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM