做网站公司联系方式页面,做网站成本,有个网站专做品牌 而且价格便宜,手绘教学网站背景#xff1a;
在flutter的业务开发过程中#xff0c;flutter侧会逐渐丰富自己的路由管理。一个轻量的路由管理本质上是页面标识#xff08;或页面路径#xff09;与页面实例的映射。本文基于dart注解提供了一个轻量路由管理方案。 不论是在native与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 {MapString, 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另一类用于注解使用者自己的routerARouteRoot。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, MapString, 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;MapString, dynamic query;MyPageOption(this.url, this.query);}class Router {ARouteInternal internal ARouteInternalImpl();Widget pageFromUrlAndQuery(String urlString, MapString, 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业务场景越来越复杂我们也会在注解的自由度上进行更深的探索。 原文链接 本文为云栖社区原创内容未经允许不得转载。