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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

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

背景:

在flutter的業(yè)務(wù)開(kāi)發(fā)過(guò)程中,flutter側(cè)會(huì)逐漸豐富自己的路由管理。一個(gè)輕量的路由管理本質(zhì)上是頁(yè)面標(biāo)識(shí)(或頁(yè)面路徑)與頁(yè)面實(shí)例的映射。本文基于dart注解提供了一個(gè)輕量路由管理方案。?
不論是在native與flutter的混合工程,還是純flutter開(kāi)發(fā)的工程,當(dāng)我們實(shí)現(xiàn)一個(gè)輕量路由的時(shí)候一般會(huì)有以下幾種方法:

  • 較差的實(shí)現(xiàn),if-else的邏輯堆疊:?
    做映射時(shí)較差的實(shí)現(xiàn)是通過(guò)if-else的邏輯判斷把url映射到對(duì)應(yīng)的widget實(shí)例上,
  • 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)每個(gè)映射的維護(hù)影響全局映射配置的穩(wěn)定性,每次維護(hù)映射管理時(shí)需要腦補(bǔ)所有的邏輯分支.?
    2)無(wú)法做到頁(yè)面的統(tǒng)一抽象,頁(yè)面的構(gòu)造器和構(gòu)造邏輯被開(kāi)發(fā)者自定義.?
    3)映射配置無(wú)法與頁(yè)面聯(lián)動(dòng),把頁(yè)面級(jí)的配置進(jìn)行中心化的維護(hù),導(dǎo)致維護(hù)責(zé)任人缺失.

  • 一般的實(shí)現(xiàn),手動(dòng)維護(hù)的映射表:?
    稍微好一點(diǎn)的是將映射關(guān)系通過(guò)一個(gè)配置信息和一個(gè)工廠方法來(lái)表現(xiàn)
  • 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側(cè)這種做法仍然比較麻煩,首先是問(wèn)題3仍然存在,其次是由于flutter目前不支持反射,必須有一個(gè)類(lèi)似工廠方法的方式來(lái)創(chuàng)建頁(yè)面實(shí)例。?
    為了解決以上的問(wèn)題,我們需要一套能在頁(yè)面級(jí)使用、自動(dòng)維護(hù)映射的方案,注解就是一個(gè)值得嘗試的方向。我們的路由注解方案annotation_route(github地址:https://github.com/alibaba-flutter/annotation_route)?應(yīng)運(yùn)而生,整個(gè)注解方案的運(yùn)行系統(tǒng)如圖所示:?

    讓我們從dart注解開(kāi)始,了解這套系統(tǒng)的運(yùn)作。

    dart注解

    注解,實(shí)際上是代碼級(jí)的一段配置,它可以作用于編譯時(shí)或是運(yùn)行時(shí),由于目前flutter不支持運(yùn)行時(shí)的反射功能,我們需要在編譯期就能獲取到注解的相關(guān)信息,通過(guò)這些信息來(lái)生成一個(gè)自動(dòng)維護(hù)的映射表。那我們要做的,就是在編譯時(shí)通過(guò)分析dart文件的語(yǔ)法結(jié)構(gòu),找到文件內(nèi)的注解塊和注解的相關(guān)內(nèi)容,對(duì)注解內(nèi)容進(jìn)行收集,最后生成我們想要的映射表,這套方案的構(gòu)想如圖示:?

    在調(diào)研中發(fā)現(xiàn),dart的部分內(nèi)置庫(kù)加速了這套方案的落地。

    source_gen

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

    • build庫(kù):整套資源文件的處理
    • analyser庫(kù):對(duì)dart文件生成完備的語(yǔ)法結(jié)構(gòu)
    • source_gen庫(kù):提供注解元素的攔截?
      這里簡(jiǎn)要介紹下source_gen和它的上下游,先看看我們捋出來(lái)的它注解相關(guān)的類(lèi)圖:

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

    annotation_route

    在了解了source_gen之后,我們開(kāi)始著手自己的注解解析方案annotation_route?
    剛開(kāi)始介入時(shí),我們遇到了幾個(gè)問(wèn)題:

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

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

    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;}}

    目前該方案已在閑魚(yú)app內(nèi)穩(wěn)定運(yùn)行,我們提供了基礎(chǔ)的路由參數(shù),隨著flutter業(yè)務(wù)場(chǎng)景越來(lái)越復(fù)雜,我們也會(huì)在注解的自由度上進(jìn)行更深的探索。

    ?


    原文鏈接
    本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

    總結(jié)

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

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。