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

背景:

在flutter的业务开发过程中,flutter侧会逐渐丰富自己的路由管理。一个轻量的路由管理本质上是页面标识(或页面路径)与页面实例的映射。本文基于dart注解提供了一个轻量路由管理方案。 
不论是在native与flutter的混合工程,还是纯flutter开发的工程,当我们实现一个轻量路由的时候一般会有以下几种方法:

  1. 较差的实现,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)映射配置无法与页面联动,把页面级的配置进行中心化的维护,导致维护责任人缺失.

  1. 一般的实现,手动维护的映射表: 
    稍微好一点的是将映射关系通过一个配置信息和一个工厂方法来表现
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 
刚开始介入时,我们遇到了几个问题:

  1. 只需要生成一个文件:由于一个输入文件对应了一个生成文件后缀,我们需要避免多余的文件生成
  2. 需要知道在什么时候生成文件:我们需要在所有的备选文件扫描收集完成后再能进行映射表的生成
  3. 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业务场景越来越复杂,我们也会在注解的自由度上进行更深的探索。

 


原文链接
本文为云栖社区原创内容,未经允许不得转载。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/520238.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

正则表达式简单语法及常用正则表达式

基本符号&#xff1a; ^ 表示匹配字符串的开始位置 (例外 用在中括号中[ ] 时,可以理解为取反,表示不匹配括号中字符串) $ 表示匹配字符串的结束位置 * 表示匹配 零次到多次表示匹配 一次到多次 (至少有一次) ? 表示匹配零次或一次 . 表示匹配单个字符 | 表示为或者,…

用PyTorch创建一个图像分类器?So easy!(Part 1)

经过了几个月的学习和实践&#xff0c;我完成了优达学城网站上《Python Programming with Python Nanodegree》课程的学习&#xff0c;该课程的终极项目就是使用Pytorch为102种不同类型的花创建一个图像分类器。 在完成这个项目的过程中&#xff0c;我和其他学员一样&#xff…

正则表达式中的开头和结尾

^  表示匹配 以 后一个字符开头有两个作用1&#xff09;匹配以指定字符开头  ^[a-zA-Z_]\w  必须以小写字母、大写字母、下划线开头2&#xff09;用在[]内部用于取反  [^hz]    匹配不含有hz的字符$  表示匹配以 前一个字符结尾\d$  以数字结尾

阿里上市,四大洲8个国家的十位代表敲锣;全球首款支持5G双卡双待的芯片发布;撕裂者3990X:桌面史上第一次64核128线程……...

关注并标星星CSDN云计算速递、最新、绝对有料。这里有企业新动、这里有业界要闻&#xff0c;打起十二分精神&#xff0c;紧跟fashion你可以的&#xff01;每周两次&#xff0c;打卡即read更快、更全了解泛云圈精彩newsgo go govivo X30新细节曝光&#xff1a;搭载潜望式超远摄支…

支付宝客户端架构分析:自动化日志收集及分析

小蚂蚁说&#xff1a; 《支付宝客户端架构解析》系列将从支付宝客户端的架构设计方案入手&#xff0c;细分拆解客户端在“容器化框架设计”、“网络优化”、“性能启动优化”、“自动化日志收集”、“RPC 组件设计”、“移动应用监控、诊断、定位”等具体实现&#xff0c;带领…

java 快速生成有残午餐_写出优质Java代码的4个技巧

译者注&#xff1a;如果现在要求对你写的Java代码进行优化&#xff0c;那你会怎么做呢&#xff1f;作者在本文介绍了可以提高系统性能以及代码可读性的四种方法&#xff0c;如果你对此感兴趣&#xff0c;就让我们一起来看看吧。以下为译文。本文我们将介绍一些有助于解决常见问…

在Kubernetes上运行区块链服务(BaaS)

本文是在2018年11月15日由Linux基金会CNCF主办的KubeCon & CloudNativeCon China 2018大会的“Running Blockchain as a Service (BaaS) on Kubernetes”演讲内容基础上整理而成&#xff0c;从技术上介绍了阿里云如何将基于区块链Hyperledger Fabric的BaaS和容器集群技术Ku…

RabbitMQ 镜像集群之同步策略_专栏讲解

文章目录一、 基础知识汲取1.1. 镜像集群简述1.2. 策略参数说明1.3. 策略案例二、HA mode 同步方式2.1. 参数说明2.2. 案例2.3. 命令终端形式2.4. MQ管控台设置2.5. 队列同步2.5. 优缺点三、Ha async mode 同步方式&#xff08;推荐使用&#xff09;3.1. 参数说明3.2. 案例3.3.…

bug要改一天?

大家应该都听过下面的一个段子~女生&#xff1a;亲爱的&#xff0c;我来亲戚了男生&#xff1a;多喝热水......女生&#xff1a;我好像要感冒了&#xff0c;亲爱的~男生&#xff1a;多喝热水......女生&#xff1a;我现在好难受哦&#xff0c;快撑不下去了男生&#xff1a;多喝…

新手也能看懂,消息队列其实很简单

本文内容思维导图&#xff1a; 消息队列其实很简单 “RabbitMQ&#xff1f;”“Kafka&#xff1f;”“RocketMQ&#xff1f;”...在日常学习与开发过程中&#xff0c;我们常常听到消息队列这个关键词。我也在我的多篇文章中提到了这个概念。可能你是熟练使用消息队列的老手&am…

java注册是什么意思_springcloud注册中心是什么意思?有什么作用?

可能我们都知道现如今java分布式微服务的流行程度&#xff0c;那么你了解过springcloud的有关内容吗?例如&#xff0c;springcloud中的注册中心是什么?用在何处?注册中心其实就是服务发现的核心。它主要保存了各个可用服务实例的网络地址(指IP Address和Port)。服务注册中心…

如何去设计前端框架能力?星巴克消息开放项目从0到1,从点到面的思考

本文由淘宝前端工程师罗嗣分享&#xff0c;主要讲述了作者在星巴克消息开放项目中的总结和思考&#xff0c;希望对大家有帮助&#xff0c;让业务分享更加有价值。 从满足星巴克项目需求单点出发&#xff0c;发散到从点到面的思考。从而总结了自己思考的基本流程&#xff08;方法…

如何在springboot中使用PageHelper分页插件

文章目录1. pom依赖2. yml3. 实体类4. mapper映射文件4. mapper接口5. service接口6. 实现类7. controller8. 浏览器验证使用思路&#xff1a; 1.引入myabtis和pagehelper依赖 2.yml中配置mybatis扫描和实体类 3. 这2行代码 pageNum&#xff1a;当前第几页 pageNum&#xff1a;…

6.6折票仅剩3天 | BDTC 2019全日程公布,哪些是你感兴趣的话题?

2019年12月5-7日&#xff0c;由中国计算机学会主办&#xff0c;CCF 大数据专家委员会承办&#xff0c;CSDN、中科天玑数据科技股份有限公司协办的中国大数据技术大会&#xff08;BDTC 2019&#xff09;将于北京长城饭店隆重举行。届时&#xff0c;超过百位顶尖技术专家将齐聚于…

云栖专辑| 阿里毕玄:程序员的成长路线

2018年12月20日&#xff0c;云栖社区3周岁生日。阿里巴巴常说“晴天修屋顶”&#xff0c;所以我们特别策划了这个专辑——分享给开发者们20个阿里故事&#xff0c;50本书籍。第一位是林昊&#xff08;毕玄&#xff09;。 在这篇《程序员的成长路线》里&#xff0c;阿里基础设施…

java线程条件变量_使用条件变量(多线程笔记)

条件变量属性&#xff1a;使用条件变量可以以原子方式阻塞线程&#xff0c;知道某个特定条件为真为止。条件变量始终与互斥锁一起使用。使用条件变量&#xff0c;线程可以以原子方式阻塞&#xff0c;知道满足某个条件为止。对掉件的测试时在互斥锁的保护下进行的。如果条件为假…

用PyTorch创建一个图像分类器?So easy!(Part 2)

在第一部分中&#xff0c;我们知道了为什么以及如何加载预先训练好的神经网络&#xff0c;我们可以用自己的分类器代替已有神经网络的分类器。那么&#xff0c;在这篇文章中&#xff0c;我们将学习如何训练分类器。 训练分类器 首先&#xff0c;我们需要为分类器提供待分类的…

涨姿势,一个通信项目从开始到结束,原来还包括这些工作

戳蓝字“CSDN云计算”关注我们哦&#xff01;作者 | 小枣君责编 | 阿秃本月12日&#xff0c;中国移动31个省的通信工程设计与可行性研究集采正式启动。这次集采规模庞大&#xff0c;涵盖了无线网(5G、FDD、NB等)、核心网、承载网、支撑网等专业方向&#xff0c;预估基本规模超4…

spring项目链接RabbitMQ集群

文章目录1.pom2. spring-rabbitmq-producer.xml3. spring-rabbitmq-consumer.xml4. rabbitmq.properties1.pom <!--spring整合rabbitmq--><dependency><groupId>com.rabbitmq</groupId><artifactId>amqp-client</artifactId><version&g…

听说支付宝有一个“疯起来连自己都打”的项目

小蚂蚁说&#xff1a; 自古红蓝出CP&#xff0c;在蚂蚁金服就有这样两支“相爱相杀”的队伍——红军和蓝军。蓝军是进攻方&#xff0c;主要职责是挖掘系统的弱点并发起“真实”的攻击&#xff0c;俗称“找茬”&#xff1b;红军则是防守方&#xff0c;其防控体系建设中的实时核…