关于移动端跨平台框架的了解与思考

之前移动端是Web、Android和iOS三分天下,后来微信小程序加入战场,再后来又出了快应用支付宝小程序头条小程序等平台。前端跨端貌似成为了越来越主流的开发方案。

在之前的项目和学习中,也或多或少使用过一些跨端框架,这里整理一下我对于目前几种跨端开发方案的了解及思考。

<!--more-->

参考

附相关框架的开发文档

1. 多个平台

1.1. 原生客户端

Android和iOS原生开发,使用相应平台支持的开发工具和语言,并直接调用系统提供的SDK API,

  • 可访问平台的全部功能、如定位、摄像头等
  • 性能流畅,用户体验好

1.2. 微信小程序

微信小程序从开始公测依赖,陆陆续续参与了多个项目的开发,其中也体验了小程序原生、wepy、mpvue等不同的开发体验。

小程序大概是新增几个平台里面流量最大的平台,开发需求也最多。

1.3. 支付宝小程序

支付宝小程序可以理解为运行在支付宝里面的小程序,跟微信小程序类似

1.4. 快应用

快应用是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;

快应用是国内10家主流安卓手机厂商联合推出的应用平台,知乎上面有一个关于快应用的讨论。

由于我平常使用Android手机较少,因此没有体验过快应用。有评论提到快应用的缺点是:目前的使用范围还只是安卓端,且只包含国内手机,

2. 跨端框架

下面是目前我使用过的一些跨端框架,有的是跨Android移动端,有的是跨Web小程序端,有的框架“野心勃勃”一套代码输出多端

2.1. weex

关于weex的工作原理,可以参考这里,其大致流程为

  • 开始时采用vue语法,编写页面和组件
  • 通过Transformer将其转打包成JS bundle文件,然后部署在服务器上
  • 客户端内置了Weex JS Framework,用于提供vue、define、callNative等方法,在初始化时即被客户端的JS引擎调用
  • 客户端请求并接收JS bundle,并使用客户端的JS引擎,执行相关JS代码,生成VNode虚拟DOM树,然后调用callNative,渲染Native视图

在Weex实例运行期间,Native渲染引擎将接收各种各样不同模块的API调用。这些调用创建或更新组件外观,运行如toast的Native API.当用户交互或模块回调时,callJS()会由JS Framework调用. 这样的循环往复将从Weex实例初始化到销毁一直持续。

就业务范围而言,weex会把单个vue文件打包成单个JS bundle(默认拆包),因此weex更适合开发单页面集成的业务,也比较容易在现有项目中接入weex。

之前在豆腐工作的时候,客户端的同事已经开始采用weex进行业务开发了。后面我也尝试进行了一些weex开发,对于weex开发还是比较熟悉的。weex支持Android、iOS和web三端开发(尽管还是要进行一些兼容适配)。

此外,貌似weex社区的活跃度一直饱受诟病~

2.2. React Native

RN使用Javascript语言,类似于HTML的JSX,以及CSS来开发移动应用。在React中虚拟DOM最终会映射为浏览器DOM树,而RN中虚拟DOM会通过 JavaScriptCore 映射为原生控件树。

RN将虚拟DOM分为了两步

  • 布局消息传递; 将虚拟DOM布局信息传递给原生;
  • 原生根据布局信息通过对应的原生控件渲染控件树

由于React Native是原生控件渲染,所以性能会比混合应用中H5好很多。尽管与React类似,但是RN并不支持打包成web引用,因此其主要作用是跨Android、iOS两端。

2.3. Chameleon

Chameleon是我收到奇舞周刊的一篇推送了解到的,大致看了一下文档,支持多个平台的跨端,可以输出RN、weex、web、微信小程序等多个平台的代码。

比较有趣的是其使用的多态协议,多态协议包括接口多态和组件多态

  • 接口多态指的是编写不同平台的接口,打包时会自动根据平台编译对应的接口实现
  • 组件多态指的是

接口多态

因为端的不同而进行不同接口的调用或者不同业务逻辑处理的场景,比如在小程序中可以直接唤起分享控件,而在web中一般是弹出点击右上角分享的蒙层

如果需要跨多端运行同一套代码,那么只能根据不同环境去调用各自的接口,这样会在业务代码中新增很多平台判断,增加代码复杂度。

Chameleon在项目文件中利用标签将各端代码进行物理隔离,利用cml-type属性指定平台,不同type的实现可以调用对应平台的任意方法和接口

<!-- 统一的接口定义 -->
<script cml-type="interface"></script>
<!-- 不同平台的接口实现 -->
<script cml-type="web"></script>
<script cml-type="weex"></script>
<script cml-type="wx"></script>

组件多态

考虑以下场景

  • 当某个功能组件需要调用各端的原生组件,各端原生组件的属性不一致,或者一端有原生组件,其他端需要组合实现等
  • 产品在需求上导致某一个组件在各端的结构表现不同。

这种情况下,如果在模板中使用v-ifv-else-if通过条件判断渲染对应平台的组件,则存在与上面接口混用同样的问题,最理想的做法是在打包时根据选择的平台,直接替换为该平台需要用到的组件。

Chameleon提供了下面的目录接口,用于定义多态组件,其中.interface为组件接口,为多端提供统一的组件接口,剩下为对应的平台的组件实现。

├── components
│   ├── c-list
│   │   ├── c-list.interface
│   │   ├── c-list.web.cml
│   │   ├── c-list.weex.cml
│   │   ├── c-list.wx.cml
│   │   └── ...

然后在页面上只需要使用c-list多态组件即可

<c-list data="{{ list }}"></c-list>

我觉得多态协议是Chameleon最大的特色之一,该功能大大提升了项目的可扩展性。

2.4. mpvue

mpvue是美团推出的一个使用 Vue.js 开发小程序的前端框架。框架基于 Vue.js 核心,修改了 Vue.jsruntimecompiler 实现,使其可以运行在小程序环境中,从而为小程序开发引入了整套 Vue.js 开发体验。

由于与Vue的语法基本相同,因此只需要修改小部分平台差异代码和更新下 webpack 的建构配置就可以直接在web和小程序端运行,从而实现了跨移动端和小程序端。官方还提到

在未来最理想的状态是,可以一套代码可以直接跑在多端:WEB、小程序(微信和支付宝)、Native(借助weex)。

3. 其他跨端技术

3.1. Flutter

Flutter是Google发布的一个用于创建跨平台、高性能移动应用的框架。Flutter没有使用原生控件,而是实现了一个自绘引擎,使用自身的布局、绘制系统。

之前使用flutter开发了两个demo项目,开发体验极好,也是我接下来的学习重点。

3.2. PWA

PWA全称Progressive Web App,即渐进式WEB应用。具备下面特性

  • 可以将app的快捷方式放置到桌面上,全屏运行,与原生app无异,网页只能运行在各种浏览器中
  • 能够在各种网络环境下使用,包括网络差和断网条件下,不会显示undefind,网页需要在联网环境下才能打开
  • 推送消息的能力,网页没有相关的权限

4. 关于跨端的思考

回顾GUI发展历史,可以看见移动端开发也正在经历类似的时期:PC桌面应用最初是需要支持windows、macos和linux等操作系统,甚至需要兼容不同windows系统,后来软件服务从cliet/server转向了browser/server,此外还出现了electron等桌面跨平台框架,其本质也是利用Chromium进行UI绘制。

可见基于浏览器的应用有着天然的跨平台优势,在移动端也是如此,无论是iOS还是Android,均可以运行同一套web应用,web应用的缺点在于无法使用系统的全部功能,论用户体验和功能实现是比不上原生开发的,因此才会不断出现各种跨平台框架和技术。

本质上,跨端开发是为了增加代码复用,减少开发者对多个平台差异适配的工作量,降低开发成本,提高业务专注的同时,提供比web更好的体验。一般来讲,在业务中跨平台有2种场景

  • 整个项目一套代码实现:第一种业务层需求在各端环境高度类似,原本需要针对不同端重复开发、重复测试,如首页官网、列表详情页等
  • 仅组件一套代码运行:第二种是各端页面需求不一致,却有共同的组件需要重复开发、重复测试,各个端用各自原本框架开发,使用一套代码开发公用组件,然后各个端可以使用公用组件实现业务

从产品的层面,各个端有自己的差异性,不同的业务需求是不一样的,有的业务需要各端的UI和展示保持一致,有的业务又希望各端肩负不同的逻辑。

因此我认为跨端技术应该是由业务来驱动的,如果一味的坚持多端统一,可能会得不偿失。此外,现在的跨端技术并不是十分稳定和成熟,项目初期的技术选型上还是要保持慎重比较好(被weex坑的一把辛酸泪)。

flutter的目标是跨平台移动UI框架,除了目前的组件嵌套语法写起来有些蛋疼之外,相关的widget理念和dart语言都是非常优秀的,接下来应该会继续尝试学习flutter。

此外,上面提到的Chameleon框架,实现的多态接口和多态组件,可以很方便的处理各端的差异,我觉得这个设计非常有趣,有机会希望尝试一下这个框架。

那么,关于跨端的思考,我的结论是:跨端最好的样子应该是,编写同一套代码,各端的开发和调试体验一致,通过多态解决各端的业务和平台差异

如何在代码中打日志理解Generator函数与async函数