App跨渠道结构历史悠久,从cordovareact nativeflutter,直到最近的uni-app x。江山代有才人出,每个都试图颠覆原生,但曩昔却一直未成功。

曩昔的问题到底在哪里?

咱们先捋一捋各种技能道路,剖析这些跨渠道开发结构和原生运用的不同详细在哪里。

逻辑层 烘托层 类型 代表作
webview webview 弱类型 5+App、cordova
js引擎 webview 弱类型 uni-app之app-vue 、小程序(dount)
js引擎 原生烘托 弱类型 react native、uni-app之app-nvue、weex
dart引擎 flutter烘托引擎 强类型 flutter
js引擎 flutter烘托引擎 弱类型 微信skyline、webF、ArkUI-x
kotlin 原生烘托 强类型 uni-app x
kotlin 原生烘托 强类型 原生运用

上面的表格,除了行尾的原生运用外,各个跨渠道结构按出现时刻排序,能够看到跨渠道结构是如何演进的。

上表中,uni-app x和原生运用是一样的,逻辑层和烘托层都是原生,都是强类型;而其他跨渠道结构或者在逻辑层、或者在烘托层与原生不共同。

webview不行现已是业界常识了,发动慢、烘托慢、内存占用高。这块本文不再胪陈。

但那些非web-view的结构到底哪里不如原生?

1. js逻辑+ 原生烘托

react nativeweex等抛弃webview,改由原生烘托的跨渠道计划,2014年就推出了。 如今手机硬件也越来越好了,为什么功用还达不到原生?

js+原生烘托的计划主要有2点缺陷:

  • JS引擎自身的功用问题
  • JS和原生之间的通讯推迟

1.1 js引擎慢,发动速度和运转速度都弱于原生

所以许多开发者即使运用这类计划,主页也仍是原生来写。

React Native的Hermes引擎和华为的arkUI,供给了js编译为字节码的计划,这是一种空间换时刻的计划,发动速度有了必定优化,但仍然比不过原生。

弱类型在编译期可优化的幅度有限,仍是需求一个运转时来跑,无法像强类型那样直接深化底层。

以数字运算为例,js的number运算的确比强类型的int慢,内存开支也更大。

1.2 js言语与原生之间通讯卡顿

每个言语有自己的内存空间,跨言语通讯都有折损,每次通讯几十到几百毫秒不等,视手机其时的状态。一旦频繁通讯,就会显着卡顿。

逻辑层的js,即要和原生烘托层通讯,还要和原生API通讯:

1.2.1 js与原生ui通讯

举个简略的场景例子,在js里监听翻滚,根据翻滚改变实时调整界面上某些元素的高度改变。这个问题能难倒一大批跨渠道开发结构。

假如悉数在webview里,js操作ui还好一些,所以uni-app的app-vue里的renderjs操作UI功用高,便是这个道理。同理还有微信小程序的wsx

尽管小程序和uni-app都是js,但实际上逻辑层在独立js引擎里,经过原生桥来操控web-view,通讯成本很高。

weex供给了bindingx技能,这是一种弱编程,烘托层预先定义了一些操作UI的方法,调用时悉数在烘托层运转,不会来回与逻辑层通讯。但这种预定义方法的习惯面有限,无法做到在js里高功用、自在的操作所有UI。

1.2.2 js操作原生api

操作系统和三方SDK的API都是原生的,js调用这些能力也需求跨言语通讯。比方js调用原生的Storage或IO,数据较多时遍历的功用十分差。

当然在js API的封装上能够做些优化,比方微信的storage供给了wx.batchGetStorageSync这种批量读取的API,已然遍历功用差,那爽性一次性从原生读出来再传给js。

这也只能是无奈的计划,假如在遍历时想用js做什么判断就完成不了了,而且一次性读出很大的数据后传给js这一下,也需求通讯时刻。

2. flutter计划

flutter在2018年发布,第一次统一了逻辑层和烘托层,而且运用了强类型。

它没有运用原生烘托,而是运用由dart驱动的烘托引擎,这样逻辑层的dart代码操作UI时,再也没有延时了!bindingx、wxs这种补丁计划再也不需求了。

而且dart作为强类型,编译优化很好做,发动速度和运转速度都胜过js。

在这个开源项目下gitcode.net/dcloud/test…,供给了一个flutter编写的100个slider一起滑动的示例, 项目下有源码也有打包好apk,能够直接装置体会。

100个slider一起滑动,十分检测逻辑和UI的通讯。假如在webview内部,html和js写100个这样的slider,在新的手机上体现也还ok。但在小程序和react native这种逻辑和UI分离的形式下,100个slider是灾难。

下载装置apk后能够看到dart操作flutter的UI真的没有通讯折损,100个slider的拖动十分流通。

App跨渠道结构VS原生开发深度评测之2023版

flutter看起来很完美。但为什么也没有成为干流呢?许多大厂振奋的引进后为何又不再扩展运用范围呢?

2.1 dart与原生API的通讯

别忘了上面1.2.2说到的原生API通讯。flutter尽管在逻辑层和烘托层都是dart,但要调用原生API时,仍是要通讯。

操作系统和三方SDK的API是原生的,让dart调用需求做一层封装,又落到了跨言语通讯的坑里。

gitcode.net/dcloud/test…这是一个开源测验项目,来测验原生的claas数据与dart的通讯耗时。

项目里边有源码,咱们可自行编译;根目录有打包好的apk,也能够直接装置体会。

这个项目首先在kotlin中构建了包括不同数据量的class,传递到dart然后烘托在界面上,而且再写回到原生层。

有0.1k和1k两种数据量(点击界面上的1k数字可切换),有读和读并写2个按钮,各自循环1000次。

以下截图的测验环境是华为mate 30 5G,麒麟990。手机上所有进程杀掉。如下图:

  • 1k数据从原生读到dart并烘托

App跨渠道结构VS原生开发深度评测之2023版

  • 1k数据从原生读到dart并烘托再写回

App跨渠道结构VS原生开发深度评测之2023版

  • 0.1k数据从原生读到dart并烘托

App跨渠道结构VS原生开发深度评测之2023版

  • 0.1k数据从原生读到dart并烘托再写回

App跨渠道结构VS原生开发深度评测之2023版

通讯损耗十分显着。而且数据量从1k降低到0.1k时,通讯时刻并没有减少10倍,这是由于通讯耗时有一个基础线,数据再小也降不下去。

为什么会这样?由于dartkotlin不是一种编程言语,不能直接调用kotlinclass,只能先序列化成字符串,把字符串数据从原生传到dart,然后在dart层再从头结构。

当然也能够在原生层为dart封装API时供给wx.batchGetStorageSync这类批处理API,把数据一次读好再给dart,但这种又会遇到灵敏性问题。

而在uni-app x中,这种跨言语通讯是不存在的,不需求序列化,由于uni-app x运用的编程言语uts,在android上就编译为了kotlin,它能够直接调用kotlin的class而无需通讯和封装。示例如下,详细uni-app x的原理后续章节会专题介绍。

<template>
</template>
<script lang="uts">
import Build from 'android.os.Build';
export default {
	onLoad() {
		console.log(Build.MODEL); //uts能够直接导入并运用原生方针,不需求封装,没有跨言语通讯折损
	}
}
</script>

再共享一个知识:

许多人都知道iPhone上跨渠道结构的运用,体现比android好。但大多数人只知道是由于iPhone的硬件好。

其实还有一个重要原因,iOS的jscore是c写的,OS的API及烘托层也都是ObjectC,js调用原生时,某些类型能够做共享内存的优化。但杂乱方针也仍是无法直接丢一个指针曩昔共享运用内存。

而android,不管java仍是kotlin,他们和v8、dart通讯仍然需求跨言语通讯。

2.2 flutter烘托和原生烘托的并存问题

flutter的自烘托引擎,在技能上是不错的。但在生态兼容上有问题。

许多三方软件和SDK是原生的,原生烘托和flutter自烘托并存时,问题许多。

flutter开发者都知道的一个常见坑是输入法,由于输入法是典型的原生UI,它和flutter自绘UI并存时各种兼容问题,输入框被遮挡、窗体resize习惯,输入法有许多种,很难适配。

混合烘托,还有信息流广告、map、图表、动画等许多三方sdk触及。这个时分内存占用高、烘托帧率下降、不同烘托方法字体不共同、暗黑主题不共同、国际化、无障碍、UI主动化测验,各种不共同。。。

这里没有供给开源示例,由于flutter官方是供认这个问题的,它供给了2种方法:混合集成形式和虚拟显现形式形式。

但在烘托速度、内存占用、版本兼容、键盘交互上都各自有各自的问题。详见flutter官网:docs.flutter.dev/platform-in…。这个是中文翻译:flutter.cn/docs/platfo…

在各大App中,微信的小程序主页是为数不多的运用flutter UI的界面,现已上线1年以上。

下面是微信8.0.44(此刻最新版),从微信的发现页面进入小程序主页。

App跨渠道结构VS原生开发深度评测之2023版

视频中手机切换暗黑主题后,这个UI却仍是白的,而且flutter的父容器原生view现已变黑了,它又在黑底上制作了一个白色界面,体会十分差。

这个小程序主页界面很简略,没有输入框,规避了混合烘托,点击搜索图标后又跳转到了黑色的原生烘托的界面里。

倘若这个界面再内嵌一个原生的信息流SDK,那会看到白色UI中的信息流广告是黑底的,更无法接受。

当然这不是说flutter没法做暗黑主题,重启微信后这个界面会变黑。这里仅仅阐明烘托引擎不共同带来的各种问题。

注:如何辨认一个界面是不是用flutter开发的?在手机设置的开发者选项里,有一个GPU出现形式剖析,flutter的UI不触发这个剖析。且无法检查布局边界。

flutter的混合烘托的问题,在所有运用原生烘托的跨渠道开发结构中都不存在,比方react native、weex、uni-app x。

总结下flutter:逻辑层和UI层交互没有通讯折损,但逻辑层dart和原生api有通讯成本,自绘UI和原生ui的混合烘托问题许多。

3. js+flutter烘托

flutter除了上述说到的原生通讯和混合烘托,还有3个问题:dart生态、热更新、以及比较难用的嵌套写法。

一些厂商把flutter的dart引擎换成了js引擎,来处理上述3个问题。比方微信skyline、webF、ArkUI-x。

其实这是让人困惑的行为。由于这又回到了react native和weex的老路了,仅仅把原生烘托换成了flutter烘托。

flutter最大的优势是dart操作UI不需求通讯,以及强类型,而改成js,操作UI再次需求通讯,又需求js运转时引擎。

为了处理js和flutter烘托层的通讯问题,微信的skyline又推出了补丁技能worklet动画,让这部分代码运转在UI层。(当然微信的通讯,除了跨言语,还有跨进程通讯,会更显着)

这个项目gitcode.net/dcloud/test…, 运用ArkUI-x做了100个slider,咱们能够看源码,下载apk体会,显着能看到由于逻辑层和UI层通讯导致的卡顿。

App跨渠道结构VS原生开发深度评测之2023版

上述视频中,留意看手指按下的那1个slider,和其他99个经过数据通讯指挥跟随一起举动的slider,无法同步,而且界面掉帧。

不过自烘托由于无法经过Android的开发者东西查看GPU出现形式,所以无法从条状图直观反映出掉帧。

留意ArkUI-x不支撑Android8.0以下的手机,不要找太老的手机测验。

许多人以为自烘托是王道,但其实自烘托是坑。由于flutter的UI还会带来混合烘托问题。

也便是说,js+flutter烘托,和js+原生烘托,这2个计划比较,都是js弱类型、都有逻辑层和烘托层的通讯问题、都有原生API通讯问题,而js+flutter还多了一个混合烘托问题。

可能有的同学会说,原生烘托很难在iOS、Android双端共同,自烘托没有这个问题。

但其实完全能够双端共同,假如你运用某个原生烘托结构遇到不共同问题,那仅仅这个结构厂商做的欠好罢了。

是的,很惋惜react native在跨端组件方面投入缺乏,官方连slider组件都没有,导致本次评测中未供给react native下slider-100的示例和视频。

4. uni-app x

2022年,uts言语发布。2023年,uni-app x发布。

uts言语是根据typescript修正而来的强类型言语,编译到不同渠道时有不同的输出:

  • 编译到web,输出js
  • 编译到Android,输出kotlin
  • 编译到iOS,输出swift

而uni-app x,是根据uts言语从头开发了一遍uni-app的组件、API以及vue结构。

如下这段示例,前端的同学都很熟悉,但它在编译为Android App时,变成了一个纯的kotlin app,里边没有js引擎、没有flutter、没有webview,从逻辑层到UI层都是原生的。

<template>
	<view class="content">
		<button @click="buttonClick">{{title}}</button>
	</view>
</template>
<script> //这里只能写uts
	export default {
		data() {
			return {
				title: "Hello world"
			}
		},
		onLoad() {
			console.log('onLoad')
		},
		methods: {
			buttonClick: function () {
				uni.showModal({
					"showCancel": false,
					"content": "点了按钮"
				})
			}
		}
	}
</script>
<style>
	.content {
		width: 750rpx;
		background-color: white;
	}
</style>

这听起来有点天方夜谭,许多人不信。DCloud不得不重复告知咱们,能够运用如下方法验证:

  • 在编译uni-app x项目时,在项目的unpackage目录下看看编译后生成的kt文件

  • 解压打包后的apk,看看里边有没有js引擎或flutter引擎

  • 手机端检查布局边界,看看烘托是不是原生的(flutter和webview都无法检查布局边界)

可是开发者也不要误解之前的uni-app代码能够无缝搬迁。

  • 之前的js要改成uts。uts是强类型言语,上面的示例恰好类型都能够主动推导,不能推导的时分,需求用:as声明和转化类型。

  • uni-app x支撑css,可是css的子集,不影响开发者排版出所需的界面,但并非web的css全都兼容。

了解了uni-app x的基本原理,咱们来看下uni-app x下的100个slider作用怎么样。

项目gitcode.net/dcloud/test…下有源码工程和编译好的apk。

如下视频,翻开了GPU出现形式,能够看到没有一条竖线打破那条赤色的掉帧安全横线,也便是没有一帧掉帧。

App跨渠道结构VS原生开发深度评测之2023版

uni-app x在app端,不管逻辑层、烘托层,都是kotlin,没有通讯问题、没有混合烘托问题。不是达到了原生的功用,而是它本身便是原生运用,它和原生运用的功用没不同。

这也是其他跨渠道开发结构做不到的。

uni-app x是一次斗胆的技能打破,共享下DCloud挑选这条技能道路的思路:

DCloud做了许多年跨渠道开发,uni-app在web和小程序渠道取得了很大的成功,不管规模巨细的开发者都在运用;但在app渠道,大开发者只运用uni小程序sdk,中小开发者的app会全体运用。

究其原因,uni-app在web和小程序上,没有功用问题,直接编译为了js或wxml,uni-app仅仅换了一种跨渠道的写法,不存在用uni-app开发比原生js或原生wxml功用差的说法。

但曩昔根据小程序架构的app端,功用的确不及原生开发。

那么App渠道,为什么不能像web和小程序那样,直接编译为App渠道的原生言语呢?

uni-app x,方针不是改进跨渠道结构的功用,而是给原生运用供给一个跨渠道的写法。

这个思路的转化使得uni-app x超越了其他跨渠道开发结构。

在web端编译为js,在小程序端编译为wxml等,在app端编译为kotlin。每个渠道都仅仅帮开发者换种共同的写法罢了,运转的代码都是该渠道原生的代码。

然而在2年前,这条道路有2个巨大的危险:

  1. 从来没有人走经过

  2. 即使能走通,工作量巨大

没有人确认这个产品能够做出来,DCloud内部争议也许多。

还好,阅历了无数的困难和挑战,这个产品总算问世了。

换个写法写原生运用,还带来另一个好处。

相同事务功用的app,运用vue的写法,比手写纯原生快多了。也便是uni-app x对开发功率的提升不仅仅由于跨渠道,单渠道它的开发功率也更高。

其实google自己也知道原生开发写法太杂乱,关于换种更高效的写法来写原生运用,他们的做法是推出了compose UI。

不过惋惜的是这个计划引进了功用问题。咱们专门测验运用compose UI做100个slider滑动的例子,流通度也掉帧。

源码见:gitcode.net/dcloud/test…, 项目下有打包后的apk能够直接装置体会。

翻开GPU出现形式,能够看到compose ui的100个slider拖动时,大多数竖线都打破那条赤色的掉帧安全横线,也便是掉帧严重。

App跨渠道结构VS原生开发深度评测之2023版

已然现已把不同开发结构的slider-100运用打包出来了,咱们趁便也比较了不同结构下的包体积巨细、内存占用:

包体积(单位:M) 内存占用(单位:Kb)
flutter 18 141324.8
ArtUI-x 45.7 133091.2
uni-app x 8.5 105451.2
compose ui 4.4 98575.2

包体积数据阐明:

  • 包括3个CPU架构:arm64、arm32、x86_64。

  • flutter的代码都是编译为so文件,支撑的cpu类型和包体积是等比联系,1个cpu最小需求6M体积,事务代码越多,cpu翻倍起来越多。

  • ArtUI-x的事务代码尽管写在js里,但除了引用了flutter外还引用了js引擎,这些so库体积都不小且按cpu分类型翻倍。

  • uni-app x里主事务都在kotlin里,kotlin和Android x的兼容库占据了不少体积。部分如图片引用了so库,1个cpu最小需求7M体积。但由于so库小,增加了2个cpu类型只增加了不到1M。

  • compose ui没有运用so库,体积裁剪也更完全。

  • uni-app x的常用模块并没有裁剪出去,比方slider100的例子其实没有用到图片,但图片运用的fesco的so库仍是被打进去了。实际事务中不可能不必图片,所以实际事务中uni-app x并不会比compose ui体积大多少。

内存占用数据阐明:

  • 在页面中操作slider数次后中止,获取运用内存运用信息VmRSS: 进程当前占用物理内存的巨细
  • 表格中的内存数据是运转5次获取的值取平均值
  • 自烘托会占据更多内存,假如还触及混合烘托那内存占用更高

5. 后记

跨言语通讯、弱类型、混合烘托、包体积、内存占用,这些都是曩昔跨渠道结构不如原生的当地。

这些问题在uni-app x都不存在,它仅仅换了一种写法的原生运用。

各种结构 类型 逻辑层与UI通讯折损 逻辑层与OS API通讯折损 混合烘托
react native、nvue、weex
flutter
微信skyline、webF、ArkUI-x
uni-app x
原生运用

当然,作为一个客观的剖析,这里需求强调uni-app x刚刚问世,还有许多不成熟的当地。比方前文diss微信的暗黑形式,其实截止到目前uni-app x还不支撑暗黑形式。乃至iOS版现在只能开发uts插件,还不能做完好iOS运用。

需求墙里都是uni-app x该做还未做的。也欢迎咱们投票。

另外,原生Android中一个界面不能有太多元素,否则功用会拉胯。flutter的自烘托和compose ui处理了这个问题。而原生中处理这个问题需求引进自绘机制来降低元素数量,这个在uni-app x里对应的是draw自绘API。

uni-app x这个技能道路是工业真实需求的东西,随着产品的迭代完善,它能真实帮助开发者即提升开发功率又不牺牲功用。

让跨渠道开发不如原生,成为历史。

欢迎体会uni-app x的示例运用,感触它的发动速度,烘托流通度。

源码在:gitcode.net/dcloud/hell…; 或者扫描下方二维码下载打包后的apk文件:

App跨渠道结构VS原生开发深度评测之2023版

这个示例里有几个例子十分检测通讯功用,除了也内置了slider-100外,另一个是“模版-scroll-view自定义翻滚吸顶”,在翻滚时实时修正元素top值一直为一个固定值,一点都不抖动。

咱们不游说您运用任何开发技能,但您应该知道它们的原理和不同。

欢迎指正和讨论。