日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用)

發布時間:2024/8/23 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景:

在flutter的業務開發過程中,flutter側會逐漸豐富自己的路由管理。一個輕量的路由管理本質上是頁面標識(或頁面路徑)與頁面實例的映射。本文基于dart注解提供了一個輕量路由管理方案。?
不論是在native與flutter的混合工程,還是純flutter開發的工程,當我們實現一個輕量路由的時候一般會有以下幾種方法:

  • 較差的實現,if-else的邏輯堆疊:?
    做映射時較差的實現是通過if-else的邏輯判斷把url映射到對應的widget實例上,
  • class Router {Widget route(String url, Map params) {if(url == 'myapp://apage') {return PageA(url);} else if(url == 'myapp://bpage') {return PageB(url, params);}} }

    這樣做的弊端比較明顯:?
    1)每個映射的維護影響全局映射配置的穩定性,每次維護映射管理時需要腦補所有的邏輯分支.?
    2)無法做到頁面的統一抽象,頁面的構造器和構造邏輯被開發者自定義.?
    3)映射配置無法與頁面聯動,把頁面級的配置進行中心化的維護,導致維護責任人缺失.

  • 一般的實現,手動維護的映射表:?
    稍微好一點的是將映射關系通過一個配置信息和一個工廠方法來表現
  • class Router {Map<String, dynamic> mypages = <String, dynamic> {'myapp://apage': 'pagea','myapp://bpage': 'pageb'}Widget route(String url, Map params) {String pageId = mypages[url];return getPageFromPageId(pageId);}Widget getPageFromPageId(String pageId) {switch(pageId) {case 'pagea': return PageA();case 'pageb': return PageB();}return null;}

    在flutter側這種做法仍然比較麻煩,首先是問題3仍然存在,其次是由于flutter目前不支持反射,必須有一個類似工廠方法的方式來創建頁面實例。?
    為了解決以上的問題,我們需要一套能在頁面級使用、自動維護映射的方案,注解就是一個值得嘗試的方向。我們的路由注解方案annotation_route(github地址:https://github.com/alibaba-flutter/annotation_route)?應運而生,整個注解方案的運行系統如圖所示:?

    讓我們從dart注解開始,了解這套系統的運作。

    dart注解

    注解,實際上是代碼級的一段配置,它可以作用于編譯時或是運行時,由于目前flutter不支持運行時的反射功能,我們需要在編譯期就能獲取到注解的相關信息,通過這些信息來生成一個自動維護的映射表。那我們要做的,就是在編譯時通過分析dart文件的語法結構,找到文件內的注解塊和注解的相關內容,對注解內容進行收集,最后生成我們想要的映射表,這套方案的構想如圖示:?

    在調研中發現,dart的部分內置庫加速了這套方案的落地。

    source_gen

    dart提供了build、analyser、source_gen這三個庫,其中source_gen利用build庫和analyser庫,給到了一層比較好的注解攔截的封裝。從注解功能的角度來看,這三個庫分別給到了如下的功能:

    • build庫:整套資源文件的處理
    • analyser庫:對dart文件生成完備的語法結構
    • source_gen庫:提供注解元素的攔截?
      這里簡要介紹下source_gen和它的上下游,先看看我們捋出來的它注解相關的類圖:

    source_gen的源頭是build庫提供的Builder基類,該類的作用是讓使用者自定義正在處理的資源文件,它負責提供資源文件信息,同時提供生成新資源文件的方法。source_gen從build庫提供的Builder類中派生出了一個自己的builder,同時自定義了一套生成器Generator的抽象,派生出來的builder接受Generator類的集合,然后收集Generator的產出,最后生成一份文件,不同的派生builder對generator的處理各異。這樣source_gen就把一個文件的構造過程交給了自己定義的多個Generator,同時提供了相對build庫而言比較友好的封裝。?
    在抽象的生成器Generator基礎上,source_gen提供了注解相關的生成器GeneratorForAnnotation,一個注解生成器實例會接受一個指定的注解類型,由于analyser提供了語法節點的抽象元素Element和其metadata字段,即注解的語法抽象元素ElementAnnotation,注解生成器即可通過檢查每個元素的metadata類型是否匹配聲明的注解類型,從而篩選出被注解的元素及元素所在上下文的信息,然后將這些信息包裝給使用者,我們就可以利用這些信息來完成路由注解。

    annotation_route

    在了解了source_gen之后,我們開始著手自己的注解解析方案annotation_route?
    剛開始介入時,我們遇到了幾個問題:

  • 只需要生成一個文件:由于一個輸入文件對應了一個生成文件后綴,我們需要避免多余的文件生成
  • 需要知道在什么時候生成文件:我們需要在所有的備選文件掃描收集完成后再能進行映射表的生成
  • source_gen對一個類只支持了一個注解,但存在多個url映射到一個頁面?
    在一番思索后我們有了如下產出

  • 首先將注解分成兩類,一類用于注解頁面@ARoute,另一類用于注解使用者自己的router@ARouteRoot。routeBuilder擁有RouteGenerator實例,RouteGenerator實例,負責@ARoute注解;routeWriteBuilder擁有RouteWriterGenerator實例,負責@ARouteRoot注解。通過build庫支持的配置文件build.yaml,控制兩類builder的構造順序,在routeBuilder執行完成后去執行routeWriteBuilder,這樣我們就能準確的在所有頁面注解掃描完成后開始生成自己的配置文件。?
    在注解解析工程中,對于@ARoute注解的頁面,通過RouteGenerator將其配置信息交給擁有靜態存儲空間的Collector處理,同時將其輸出內容設為null,即不會生成對應的文件。在@ARoute注解的所有頁面掃描完成后,RouteWriteGenerator則會調用Writer,它從Collector中提取信息,并生成最后的配置文件。對于使用者,我們提供了一層友好的封裝,在使用annotation_route配置到工程后,我們的路由代碼發生了這樣的變化:?
    使用前:

    class Router {Widget pageFromUrlAndQuery(String urlString, Map<String, dynamic> query) {if(urlString == 'myapp://testa') {return TestA(urlString, query);} else if(urlString == 'myapp://testb') {String absoluteUrl = Util.join(urlString, query);return TestB(url: absoluteUrl);} else if(urlString == 'myapp://testc') {String absoluteUrl = Util.join(urlString, query);return TestC(config: absoluteUrl);} else if(urlString == 'myapp://testd') {return TestD(PageDOption(urlString, query));} else if(urlString == 'myapp://teste') {return TestE(PageDOption(urlString, query));} else if(urlString == 'myapp://testf') {return TestF(PageDOption(urlString, query));} else if(urlString == 'myapp://testg') {return TestG(PageDOption(urlString, query));} else if(urlString == 'myapp://testh') {return TestH(PageDOption(urlString, query));} else if(urlString == 'myapp://testi') {return TestI(PageDOption(urlString, query));}return DefaultWidget;}}

    使用后:

    import 'package:annotation_route/route.dart';class MyPageOption {String url;Map<String, dynamic> query;MyPageOption(this.url, this.query);}class Router {ARouteInternal internal = ARouteInternalImpl();Widget pageFromUrlAndQuery(String urlString, Map<String, dynamic> query) {ARouteResult routeResult = internal.findPage(ARouteOption(url: urlString, params: query), MyPageOption(urlString, query));if(routeResult.state == ARouteResultState.FOUND) {return routeResult.widget;}return DefaultWidget;}}

    目前該方案已在閑魚app內穩定運行,我們提供了基礎的路由參數,隨著flutter業務場景越來越復雜,我們也會在注解的自由度上進行更深的探索。

    ?


    原文鏈接
    本文為云棲社區原創內容,未經允許不得轉載。

    總結

    以上是生活随笔為你收集整理的Flutter路由管理代码这么长长长长长,阿里工程师怎么高效解决?(实用)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。