使用flutter重构斗鱼APP - dy_flutter
前言
话不多说,直接上APP效果截图:源码地址:https://github.com/yukilzw/dy_flutter
主要涵盖功能:
- 滑动状态导航、轮播图
- 移动端px兼容
- 封装HTTP、IO缓存操作
- 页面路由传值
- bloc全局状态管理
- 礼物横幅动画队列
- 弹幕消息滚动
- 接入静态视频流
- 九宫格抽奖游戏
- 照片选择
- webView容器
- …
初识
flutter - 轻松、快速地构建漂亮的移动应用。
-
Flutter是Google使用Dart语言开发的移动应用开发框架,使用一套Dart代码就能构建高性能、高保真的iOS和Android应用程序,并且在排版、图标、滚动、点击等方面实现零差异。
-
作为一个全新的移动端开发选择,谷歌希望日后为新系统fuchasia所使用(谷歌希望fuchasia能替代安卓系统,成为未来物联网多端集成的系统),因此在此之前同样支持在安卓、IOS上运行flutter,引导开发者技术转型。
-
flutter和前端的关系:flutter作为一种新的APP开发模式,和前端本质上没有任何关系(这一点和RN有本质区别),无论前端工程师还是安卓IOS工程师,面临的学习成本都是相似的。
详解
Flutter框架简介
[外链图片转存失败(img-eAicPH8A-1568527893103)(https://ceph-dev-pub.dz11.com/fed-doc/1563345316987.png)]
- 跨平台应用的框架,没有使用WebView或者系统平台自带的控件,使用自身的高性能渲染引擎(Skia)自绘,
- 界面开发语言使用dart,底层渲染引擎使用C, C++
- 组合大于继承,控件本身通常由许多小型、单用途的控件(Widget)组成,结合起来产生强大的效果,类的层次结构是扁平的,以最大化可能的组合数量
构建管道
[外链图片转存失败(img-crK06E83-1568527893104)(https://ceph-dev-pub.dz11.com/fed-doc/1563346011910.jpg)]
- 整合视图所有组件类的状态
- 通过继承自flutter基类的build方法,构建widget树,在任何状态改变的,重新触发build
- 根据传入每个widget的参数来计算样式,合并至渲染树
- 利用skia引擎在画布上进行重绘
- 关于flutter渲染数据流相关的更详细分析,可查阅文章
flutter 原理
Flutter与React-native对比
架构设计差异
RN:
[外链图片转存失败(img-SASPCsyd-1568527893104)(https://ceph-dev-pub.dz11.com/fed-doc/1563349953381.png)]
Flutter:
[外链图片转存失败(img-bPCit7gl-1568527893104)(https://ceph-dev-pub.dz11.com/fed-doc/1563350052318.png)]
- RN利用Bridge桥接的方式调用原生进行渲染,JSCore负责解析JS代码逻辑,映射到原生对应控件,由于安卓、IOS原生的差异性较大,RN底层需要对多端进行映射实现,并且难免出现不兼容情况。
- Flutter利用skia引擎绘制渲染,直接与GPU交互,脱离系统层面,因此绘制出的UI可以完全一致,甚至可以用在web端(Flutter-web)。
用前端比较熟悉的WEB开发作比方,RN好比是构建DOM用浏览器内核解析渲染,而Flutter则类似canvas画布,在H5开发中,canvas绘制的图像由于是依赖GPU的像素点操作,不会有UI兼容性问题。
综上所述,flutter的性能的确做到完完全全等于原生,而RN在一些复杂业务场景下,会出现卡顿白屏。因此尽管RN诞生了快4年了,也没有复杂的APP使用纯RN开发,都是将RN当做热更新组件集成到现有原生APP。而Flutter不同,它就是一种APP开发方式,官方甚至没有给出集成到现有业务的方案(尽管此前美团通过修改Flutter打包逻辑做到了这一点,但是有点南辕北辙的意思)。
打包体积差异
[外链图片转存失败(img-upircTxY-1568527893104)(https://ceph-dev-pub.dz11.com/fed-doc/1563348991175.png)]
Q:从上表数据可以看出,在IOS下RN的体积要小的多,在安卓下Flutter体积要小的多,这是什么原因呢?
上面我们提到过,flutter需要skia引擎,这在安卓系统里是自带的(skia也是谷歌开发的东西),但是IOS里没这个玩意,所以打包的时候,需要吧C++编译后的代码也打到包里,无形中就增加了10M多。同理安卓系统JS引擎是V8,没有JSCore,所以RN打包安卓的时候,也要把JSCore打进去。但是不管是哪种方式,随着包的业务体积变大,引擎所占用的比例也就变得越来越小了。
- 关于Flutter与RN的详细对比可以查阅:
全网最全 Flutter 与 React Native 深入对比分析
对比总结
- Flutter性能会更好无线接近原生的体验,Dart是AOT编译的,编译成快速、可预测的本地代码
- RN采用JS语言开发,基于React,对前端工程师更友好。Dart语言受众小,有学习成本
- Flutter自己实现了一套UI框架,丢弃了原生的UI框架。而RN还是可以自己利用原生框架,两个各有好处。Flutter的兼容性高,RN可以利用原生已有的优秀UI
- RN的布局更像css,而Flutter的布局更像native布局,但是去掉xml通过代码直接写需要适应下
- Flutter的第三方库还很少,RN发展的早,社区环境逐步变得完善,Flutter github还有5000+个issues要解决
实践
最让大家关心的是如何利用flutter来开发一款APP,不幸的是,这对于前端工程师来说也许是一件不太友好的事,因为Flutter本身就不是设计给前端用的。
接下来会通过归纳学习路线,指引大家一步步的开发出自己的flutter应用,这个过程需要十足的耐心
Dart
第一步就是要学习Dart,这是一门2011年才由谷歌推出的新的编程语言,它是强类型、纯面向对象的。如果你对Typescript足够熟练,会降低Dart学习成本(作为前端开发者,建议先熟练掌握Typescript,这是必须的)。
推荐文档:
Dart 语法描述(中) - 来源于Dart中文网,顺着目录把所有语法看一遍
Dart SDK - Dart SDK 使用 API,也就是Dart虚拟机提供的底层类,比如网络、IO操作、异步等,类似于nodeJS中的net、fs、os等自带模块。
Dart PUB - 扩展的第三方模块,说白了,就是我们JS中的npm包,这是叫做pub,可以将自己封装的模块发到这个网址上,并且也有版本管理,和npm简直一模一样
中文版的内容有些删减,但是也覆盖了日常开发中所有的场景,如需要浏览全部API请查最新官方英文文档 Dart
flutter
搭建开发环境
如果搭建过RN环境,想必大家第一次都是各种报红,Dart在这方面更友好,只要要找官方指引走,基本上一次就能跑起来Hello Word
总结一下顺序就是:
- 配置国内镜像到环境变量(有v*n当我没说…)
- 安装Flutter SDK(下载双击exe傻瓜操作)
- 将安装路径下
flutter\bin
文件夹配置到环境变量 - 安装 Android Studio并打开SDK Manage安装android SDK和android sdk build-tools
[外链图片转存失败(img-3NKcB6ei-1568527893105)(https://ceph-dev-pub.dz11.com/fed-doc/1563353803808.png)]
如果之前使用过RN,可能已经安卓过SDK,但是版本并不一定适用,可以在cmd执行flutter doctor
来检测当前环境是否搭建成功,如果有错误,根据提示安装对应版本SDK即可。
[外链图片转存失败(img-GAHSxYkx-1568527893105)(https://ceph-dev-pub.dz11.com/fed-doc/1563354019502.png)]
Android Studio的报错我们不管,我们用Vscode开发,我们只要关注第二项没红叉,这里警告是因为我的SDK版本是之前RN使用的,不完全符合,但这个版本不会出错没有红叉
- 安装VScode flutter插件
- 在一个目录下命令行执行
flutter create myapp
然后进入目录cd myapp
- 将你的手机插电脑,或者启一个虚拟安卓机,执行
flutter devices
检查是否有已连接设备 - 执行
flutter run
看到一个Flutter APP界面出现在你的应用上,真是激动不已啊~
[外链图片转存失败(img-vpcRgIRT-1568527893105)(https://ceph-dev-pub.dz11.com/fed-doc/1563354405160.png)]
应用的热更新 & 热重载
熟悉前端的同学都知道,在web开发时我们更新一段JS代码,页面不会刷新,会局部重载,而在业务结构改动的时候,热重载不会生效,必须进行刷新页面进行热更新,这得益于webpack-dev-server里webSocket的消息实现,在Flutter也有类似的功能,
当我们应用在cli里启动后,输入小写r触发热重载,输入大写R触发热更新。
[外链图片转存失败(img-6cz79BVi-1568527893106)(https://ceph-dev-pub.dz11.com/fed-doc/1563354769695.png)]
用VSCODE安装Flutter插件直接在根目录main.dart下按F5启动。
[外链图片转存失败(img-WZWILBcz-1568527893106)(https://ceph-dev-pub.dz11.com/fed-doc/1563450951193.png)]
[外链图片转存失败(img-TRVV87kg-1568527893106)(https://ceph-dev-pub.dz11.com/fed-doc/1563449347577.png)]
如果使用vscode内部插件启动flutter,需要点击编辑器上方的刷新按钮,根据个人喜欢选择启动方式
项目开发
在一切就绪后,我们开始进行编码,flutter的布局方式和WEB有着很大区别,没有层级样式表CSS,一切由Dart代码布局,通过为Widget注入参数来实现,但是由于Dart是强类型语言,远不如CSS灵活,每一种Widget都只允许注入指定参数,否则会编译报错,这就需要开发者对每一种Widget的用法都熟于心,这也增加了flutter的门槛。
布局由于是嵌套关系,但是flutter并没有HTML、XML、JSX这样的模板语法,纯粹由Dart类来构建视图结构,所以就出现了一个非常恐怖的问题 地狱嵌套,试想一下,如果大家用React的时候,不用JSX,而全部用Raact.createElement
嵌套children
去实现dom树结构,然后将样式通过props
中的style
来设置,那这个代码有多可怕,但是flutter的写法就是这样的。
从flutter 中文网来一步步学习flutter开发吧~
提供一个官方最全的虽有flutter API查阅文档f;utter SDK
调试
如果想要像chrome浏览器那样进行UI、断点调试,可以在VSCODE里安装官方工具Dart DevTools,这玩意用起来类似于chrome f12和react devtools,使用方法是在Vscode里F5启动flutter项目后,ctrl + p 输入:
[外链图片转存失败(img-b5WssLZ8-1568527893107)(https://ceph-dev-pub.dz11.com/fed-doc/1563451010955.png)]
然后chrome会弹出调试界面:包含tree结构检查,元素审查,渲染性能,CPU,GPU,内存情况等等
[外链图片转存失败(img-l4aNbHA1-1568527893107)(https://ceph-dev-pub.dz11.com/fed-doc/1563451114381.png)]
路由
APP的路由不存在url,因此路由都是动态的,不可能说我打开APP指定一个URL去唤起对应的某个页面,
在这个fluttergithub官方demo上有路由的实现方法,可以借鉴,但是没有给出传递参数的办法,我个人想办法用传参继承的方式做到了这一点,可参考dy_flutter
状态管理
在大型APP项目的开发中,如果仅仅使用StatefulWidget
进行setState管理实例属性,无法满足要求,需要一种全局的状态管理手段,Flutter的状态管理方案的选择非常多样化,可以酌情选择:
-
直接使用flutter的
InheritedWidget
进行module状态管理 InheritedWidget数据管理,这种方式属于未封装的原生API,操作起来可维护性不强不做推荐 -
使用Redux,什么玩意?Redux?是的,国外的一个大佬用Dart语言基于
InheritedWidget
API实现了一套flutter_redux库,不用多说了,就是anction+reducer+dispatch玩法,这是最适合前端的方案~~Flutter接入Redux -
ScopedModel 也是根据
InheritedWidget
继承实现的封装,API和redux不一样,不作赘述。 -
Bloc(Bloc文档【英】),BLoc是一种利用reactive programming方式构建应用的方法,这是一个由流构成的完全异步的世界。更深层次的来讲,Bloc底层是基于RxDart的订阅观察者模式,RxDart是Rx设计的Dart语言实现,同类知名的还有RxJava,Rxjs,前者用在安卓应用状态管理,后者用在Angular前端框架状态管理。
视频流
目前使用官方插件video_player,但是只能支持播放静态视频,没有太好的直播流方案,还在研究
插件化
由于flutter 是自己绘制的UI层,不像RN或者Native那样可以直接调用系统功能(WebView、摄像头、陀螺仪)等,所以在真实业务场景下需要使用原生代码来实现插件给flutter调用。
总结
-
本文对flutter的原理、搭建、学习路线等资料进行了一定的归纳,实际开发中会遇到各种各样的坑,有的是因为对flutter API不熟,有的甚至是因为flutter本身存在的一些问题。
-
目前flutter仍处于发展探索阶段,离正式商业化使用还有距离,毕竟不可能将现有APP整个用flutter重构,现有APP接入flutter又是本末倒置,RN仍是跨双端开发更稳定的方案,使用于大多数场景,且JS + React的模式也更适合前端工程师。