這篇文章給大家介紹Flutter路由管理代碼這么長(zhǎng)怎么高效解決,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
站在用戶的角度思考問題,與客戶深入溝通,找到萬榮網(wǎng)站設(shè)計(jì)與萬榮網(wǎng)站推廣的解決方案,憑借多年的經(jīng)驗(yàn),讓設(shè)計(jì)與互聯(lián)網(wǎng)技術(shù)結(jié)合,創(chuàng)造個(gè)性化、用戶體驗(yàn)好的作品,建站類型包括:做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣、域名注冊(cè)、雅安服務(wù)器托管、企業(yè)郵箱。業(yè)務(wù)覆蓋萬榮地區(qū)。01 背景
在Flutter的業(yè)務(wù)開發(fā)過程中,F(xiàn)lutter側(cè)會(huì)逐漸豐富自己的路由管理。一個(gè)輕量的路由管理本質(zhì)上是頁面標(biāo)識(shí)(或頁面路徑)與頁面實(shí)例的映射。本文工程師將基于dart注解提供了一個(gè)輕量路由管理方案。
不論是在native與Flutter的混合工程,還是純Flutter開發(fā)的工程,當(dāng)我們實(shí)現(xiàn)一個(gè)輕量路由的時(shí)候一般會(huì)有以下幾種方法:
1. 較差的實(shí)現(xiàn),if-else的邏輯堆疊:
做映射時(shí)較差的實(shí)現(xiàn)是通過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)無法做到頁面的統(tǒng)一抽象,頁面的構(gòu)造器和構(gòu)造邏輯被開發(fā)者自定義。
3)映射配置無法與頁面聯(lián)動(dòng),把頁面級(jí)的配置進(jìn)行中心化的維護(hù),導(dǎo)致維護(hù)責(zé)任人缺失。
2. 一般的實(shí)現(xiàn),手動(dòng)維護(hù)的映射表:
稍微好一點(diǎn)的是將映射關(guān)系通過一個(gè)配置信息和一個(gè)工廠方法來表現(xiàn)。
class Router { Mapmypages = { '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è)這種做法仍然比較麻煩,首先是問題3仍然存在,其次是由于Flutter目前不支持反射,必須有一個(gè)類似工廠方法的方式來創(chuàng)建頁面實(shí)例。
為了解決以上的問題,我們需要一套能在頁面級(jí)使用、自動(dòng)維護(hù)映射的方案,注解就是一個(gè)值得嘗試的方向。我們的路由注解方案annotation_route應(yīng)運(yùn)而生,整個(gè)注解方案的運(yùn)行系統(tǒng)如圖所示:
讓我們從dart注解開始,了解這套系統(tǒng)的運(yùn)作。
02 dart注解
注解,實(shí)際上是代碼級(jí)的一段配置,它可以作用于編譯時(shí)或是運(yùn)行時(shí),由于目前Flutter不支持運(yùn)行時(shí)的反射功能,我們需要在編譯期就能獲取到注解的相關(guān)信息,通過這些信息來生成一個(gè)自動(dòng)維護(hù)的映射表。那我們要做的,就是在編譯時(shí)通過分析dart文件的語法結(jié)構(gòu),找到文件內(nèi)的注解塊和注解的相關(guān)內(nèi)容,對(duì)注解內(nèi)容進(jìn)行收集,最后生成我們想要的映射表,這套方案的構(gòu)想如圖示:
在調(diào)研中發(fā)現(xiàn),dart的部分內(nèi)置庫加速了這套方案的落地。
03 Source_gen
dart提供了build、analyser、source_gen這三個(gè)庫,其中source_gen利用build庫和analyser庫,給到了一層比較好的注解攔截的封裝。從注解功能的角度來看,這三個(gè)庫分別給到了如下的功能:
build庫:整套資源文件的處理
analyser庫:對(duì)dart文件生成完備的語法結(jié)構(gòu)
source_gen庫:提供注解元素的攔截
這里簡(jiǎn)要介紹下source_gen和它的上下游,先看看我們捋出來的它注解相關(guān)的類圖:
source_gen的源頭是build庫提供的Builder基類,該類的作用是讓使用者自定義正在處理的資源文件,它負(fù)責(zé)提供資源文件信息,同時(shí)提供生成新資源文件的方法。source_gen從build庫提供的Builder類中派生出了一個(gè)自己的builder,同時(shí)自定義了一套生成器Generator的抽象,派生出來的builder接受Generator類的集合,然后收集Generator的產(chǎn)出,最后生成一份文件,不同的派生builder對(duì)generator的處理各異。這樣source_gen就把一個(gè)文件的構(gòu)造過程交給了自己定義的多個(gè)Generator,同時(shí)提供了相對(duì)build庫而言比較友好的封裝。
在抽象的生成器Generator基礎(chǔ)上,source_gen提供了注解相關(guān)的生成器GeneratorForAnnotation,一個(gè)注解生成器實(shí)例會(huì)接受一個(gè)指定的注解類型,由于analyser提供了語法節(jié)點(diǎn)的抽象元素Element和其metadata字段,即注解的語法抽象元素ElementAnnotation,注解生成器即可通過檢查每個(gè)元素的metadata類型是否匹配聲明的注解類型,從而篩選出被注解的元素及元素所在上下文的信息,然后將這些信息包裝給使用者,我們就可以利用這些信息來完成路由注解。
04 annotation_route
在了解了source_gen之后,我們開始著手自己的注解解析方案annotation_route剛開始介入時(shí),我們遇到了幾個(gè)問題:
只需要生成一個(gè)文件:由于一個(gè)輸入文件對(duì)應(yīng)了一個(gè)生成文件后綴,我們需要避免多余的文件生成
需要知道在什么時(shí)候生成文件:我們需要在所有的備選文件掃描收集完成后再能進(jìn)行映射表的生成
source_gen對(duì)一個(gè)類只支持了一個(gè)注解,但存在多個(gè)url映射到一個(gè)頁面
在一番思索后我們有了如下產(chǎn)出
首先將注解分成兩類,一類用于注解頁面@ARoute,另一類用于注解使用者自己的router@ARouteRoot。routeBuilder擁有RouteGenerator實(shí)例,RouteGenerator實(shí)例,負(fù)責(zé)@ARoute注解;routeWriteBuilder擁有RouteWriterGenerator實(shí)例,負(fù)責(zé)@ARouteRoot注解。通過build庫支持的配置文件build.yaml,控制兩類builder的構(gòu)造順序,在routeBuilder執(zhí)行完成后去執(zhí)行routeWriteBuilder,這樣我們就能準(zhǔn)確的在所有頁面注解掃描完成后開始生成自己的配置文件。
在注解解析工程中,對(duì)于@ARoute注解的頁面,通過RouteGenerator將其配置信息交給擁有靜態(tài)存儲(chǔ)空間的Collector處理,同時(shí)將其輸出內(nèi)容設(shè)為null,即不會(huì)生成對(duì)應(yīng)的文件。在@ARoute注解的所有頁面掃描完成后,RouteWriteGenerator則會(huì)調(diào)用Writer,它從Collector中提取信息,并生成最后的配置文件。對(duì)于使用者,我們提供了一層友好的封裝,在使用annotation_route配置到工程后,我們的路由代碼發(fā)生了這樣的變化:
使用前:
import 'testa.dart' import 'testb.dart' import 'testc.dart' import 'testd.dart' import 'teste.dart' import 'testf.dart' class Router { Widget pageFromUrlAndQuery(String urlString, Mapquery) { 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(PageEOption(urlString, query)); } else if(urlString == 'myapp://testf') { return TestF(PageFOption(urlString, query)); } return DefaultWidget; } }
使用后:
import 'package:annotation_route/route.dart'; class MyPageOption { String url; Mapquery; MyPageOption(this.url, this.query); } class Router { ARouteInternal internal = ARouteInternalImpl(); Widget pageFromUrlAndQuery(String urlString, Map query) { ARouteResult routeResult = internal.findPage(ARouteOption(url: urlString, params: query), MyPageOption(urlString, query)); if(routeResult.state == ARouteResultState.FOUND) { return routeResult.widget; } return DefaultWidget; } }
關(guān)于Flutter路由管理代碼這么長(zhǎng)怎么高效解決就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。