End

跨平台 动态化 蒋宏伟 27-跨端的机遇:小程序、Flutter 和 RN 原理对比

本文地址


目录

跨端的机遇:小程序、Flutter 和 RN 原理对比

React Native 新架构实战课,蒋宏伟,58 同城前端架构师,58RN 负责人,2022-07-01

作者简介

蒋宏伟:58 同城前端架构师、58RN、Hybrid 框架负责人,GMTC、51CTO 讲师,在跨端框架、性能异常监控领域有丰厚的经验。

作为当前最热门的跨端框架之一,近年来 React Native 备受开发者追捧。它好上手,开发成本低,对前端和客户端都相对友好,而且它也是当下唯一能支持复杂业务场景下热更新的跨端框架

限定语:当下、唯一、复杂业务、热更新、跨端。虽然结论太多儿戏,但看在限定词比较多的份上,也勉强能接受。

学习偏底层的纯技术类知识,有什么意义?

  • 如果只学那些日常会用到的、别人也会知识点,我的职业竞争力在哪里?
  • 掌握那些大家都会的应用层面的知识只是及格,要想更进一步,我们就要有更高的技术追求。
  • 学习底层框架的意义不在于“内卷”,也不在于为了面试去背八股文,为的是解决别人解决不了的问题,为的是创新
  • 人无我有,人有我优,才是你的核心竞争力。

小程序

从技术上说,小程序是在一个应用程序中再嵌入另一个应用程序,也就是说微信本身就是一个应用程序,而小程序就是微信应用程序之上的程序,这是国内的独有现象,国外是没有的。

从商业角度讲,这进一步把微信应用,打造成了一个操作系统平台之上的平台,或者 AppStore 之上的 AppStore。相对于自己开发原生应用而言,由于微信等超级 App 在国内的普及程度非常高,开发者不仅可以使用这些超级 App 提供的技术能力和触达用户的渠道,还能轻易地实现跨端,节约成本。

小程序虽好,但它是一个闭源软件,而不是一个开源软件,我们没办法直接抄作业,只能从官方的文章中借鉴和分析。

以微信小程序为例,在 基于小程序技术栈的微信客户端跨平台实践 这篇文章中,微信技术团队分享了微信小程序在各个技术方向的跨端探索,包括基于 WebView 的、基于 RN-like(类RN方案) 的、基于 Flutter 的,这三种跨端思路。

第一种是基于 WebView 实现跨端的思路。

但在基于这种思路开发时,微信团队遇到了 WebView 渲染和原生渲染体验不一致的问题。

他们举了两个例子。一个是两个技术方案渲染出来的字体不一致:在 Android 平台上,WebView 没办法和 Android 系统的字体保持一致,体验上有“较为明显的割裂感”。

第二个例子是,在图片和视频混排的场景下,Android 中低端机会出现掉帧的现象。

既然纯 WebView 会存在字体不一致和掉帧的问题,那我们能不能把原生字体、原生组件引进来,把 WebView 中有问题的字体和组件替换掉呢?

这就是第二种思路,使用部分原生组件代替部分 WebView 组件的方案。

因为这套思路,受到的是 React Native 这类框架的启发,因此他们内部也叫做 RN-like 方案。

我们简单说一下这个方案的原理。首先微信小程序提供了自己的一套开发框架和语言,包括用于描述页面基础组件的 WXML(WeiXin Markup Language),以及用于描述页面样式的 WXSS(WeiXin Style Sheets)。

当 WXML/WXSS 发生改变,也就是 UI 页面发生改变的时候,小程序前端公共库(WXA Framework)会进行一些内部运算,以操作指令的形式将变化结果提交给 C++,具体的布局计算、CSS 样式更新和 DOM 结构变化都是由 C++ 这一层实现的。C++ 计算完成后,再决定是调用 WebView 渲染组件,还是调用原生视图渲染组件。

此外,在微信推出的同层渲染方案中,就是用原生组件把 video、map、camera、textarea、input 等 WebView 标签替代了。

除了以上两种方案之外,微信还尝试了第三种思路。

第三种思路就是,用 Flutter 完全代替 WebView。

Flutter 版的小程序的原理大致是,前面部分和 RN-like 方案很像,用小程序前端公共库(WXA)计算 WXML/WXSS 的变化,并将这些变化描述成指令传给 C++ 层。和 RN-like 方案不同的是,C++ 层完成布局计算后,调用的是 Dart2C++ 接口,并将渲染命令传给 Flutter,由 Flutter 进行绘制。

微信技术团队也给出了三种技术路线的性能对比:

疑问:
问题一:为什么测试环境不保持一致?
问题二:为什么没有 Flutter 和 RN-like 方案直接对比的结果?
问题三,来自灵魂的拷问:是不是作者水平有限,才得出这种的结论?

你可以看到,相比原始的 WebView 方案,使用 RN-like 和 Flutter 渲染方案后,内存都有所下降,FPS 也都有所提升。

但微信团队综合对比下来,认为目前 RN-like 方案,也就是使用 WebView 加上部分原生组件渲染页面的方案,有极大的灵活性和前端兼容性,并不会用 Flutter 方案将其代替。

Flutter

即便只开发 React Native,你也应该对竞品框架有所了解,特别是在深入底层原理学习的时候,如果你能把各个框架之间原理对比着学习,会更容易看到它们的相同之处和不同之处,也更容易从宏观层面对它们进行抽象理解

回到 Flutter 框架本身。谷歌对 Flutter 的投入力度非常大,因为它不仅是一个跨端框架,还是开发 Fuchsia 操作系统的 UI 界面的工具。

在移动互联网爆发后,Chrome 不仅要支持桌面应用,还要支持移动应用。Flutter 的创始人 Eric Seidel 对 Chrome 进行了拆分,去掉桌面应用的历史包袱后,发现一些核心指标比原来快上了 20 倍,于是就有了后来 Flutter 的故事。

Flutter 现在的定位是一个跨平台 UI 开发工具(UI tookit),但当你剖析它的原理时,你会发现 Flutter 和浏览器有很多相似之处,你可以相互对照着进行理解:

疑问:
这种简版的模型,可以用在任何 UI 平台上,包括 Android/iOS/Windows,怎么可以得出 "Flutter 和浏览器的架构非常相似" 这种结论呢?

我们不考虑架构细节,只从最基本的原理上观察,你会发现 Flutter 和浏览器的架构非常相似。

首先,Flutter 和浏览器都是跨平台的,而且它们主要用的渲染引擎都是 Skia

其次,在语言层面上,Flutter 业务层的语言采用的是 Dart,浏览器用的是 JavaScript。Dart 语言和 JavaScript 非常类似,因为 Dart 诞生之初的目标就是想替代 JavaScript。而在框架底层,二者都是用 C++。

第三,在和原生应用通讯上,Flutter 提供了两套通信机制,消息通道(EventChannel )和接口通道(MethodChannel )。

  • 消息通道的原理类似于 Hybrid 应用或 React Native 老架构的“bridge”,跨语言通讯的消息都需要进行序列化和反序列化
  • 接口通道的原理类似 JavaScript 直接调用 C++,利用的是引擎提供的脚本语言和 C++ 之间调用能力。

举个例子,如果 Flutter 要调用原生能力,比如微信支付能力,实际上它还是通过 Dart 调用消息通道和接口通道,再间接地调用 Java/OC,由 Java/OC 再调用微信提供的原生 SDK。在这种混合应用场景中,Flutter 也没办法避免通信带来的性能折损。

当然,Flutter 和浏览器也有很多不同之处:

  • Flutter 没有历史包袱。它不需要支持那些不常用功能和布局能力;
  • Flutter 用的是 Dart,浏览器用的是 HTML/CSS/JavaScript;
  • Flutter 为了保证 UI 组件(Widgets)的灵活性,使用的是 Dart 来实现的,而浏览器标签提供的 UI 组件能力是由 C++ 实现的,等等。

我认为,Flutter 的优势在于它的纯 Flutter 应用的渲染路径很短。

纯 Flutter 的渲染只涉及独立渲染进程,从业务语言 Dart 的调用,到 C++ 层的渲染和布局,再到调用使用 C++ 写的 Skia 渲染引擎,由 Skia 将计算出来的向量图,栅格化为手机屏幕能够显示位图,也就是我们看到的页面。

而 Flutter 的主要槽点是 Dart。

虽然 Dart 语法和 JavaScript 语法很像,但终归是两门语言,增加了很多学习成本和开发成本。Dart 发明之初是为下一代 Web 应用准备的,也有各种 JavaScript 和 Dart 的对比文章在吹捧 Dart,甚至把 Dart 称为 “JavaScript 杀手”。但最终,开发者还是用脚投票选择了 JavaScript 而不是 Dart,导致 Chrome 团队内置 Dart 虚拟机的计划腹死胎中。

其次,由于苹果政策的限制,Dart 语言是不能热更新的。虽然业内也有很多 Flutter 热更新的动态化框架,但这些都是以牺牲 Flutter 性能为基础的。苹果政策规定只有使用 JavaScriptCore 作为引擎的应用才能动态更新,因此 Flutter 的热更新要先执行 JavaScript,再调用 Java/OC,然后才开始执行 Dart,这样 Flutter 的调用栈就变长、变复杂了。

React Native

和小程序、Flutter 一样,React Native 也是移动浪潮下的产物,不同的是它走的技术路线是 React + Native 的组合路线,这让 Web 开发者上手非常快。

先来看看 React Native 和 Flutter 的相似之处和不同之处:

疑问:
正如上图中小括号中备注的一样,Flutter 仅仅是在渲染原生 View 时才是这种渲染流程,而正常情况时,Flutter 是不会混合原生 View 开发的,其也完全不是这种渲染流程。

二者的相似之处有三点。

首先,“Flutter 模型架构的灵感来自 Facebook 的 React 框架”,它们都是组件化的、(伪)声明式的。

其次,两个跨端框架对原生开发都有很高的依赖,只要稍微复杂点,都离不开原生的支持,比如电商交易离不开微信支付,在线直播离不开声网等。

第三点,也是最关键的一点,在混合应用中,由于二者都高度依赖原生开发,因此它们常常需要使用原生应用或操作系统本身提供的组件或 API。咱们不看具体的细节,只从宏观角度看,你会发现无论是 React Native 还是 Flutter,它们使用原生组件或 API 的调用路径是一样的。

上图的左边是 React Native 的渲染路径图,从图中你可以看出来,React Native 最上层的业务语言是 JavaScript,下一层是 C++。C++ 的核心功能主要是布局计算,并且起到了黏合剂的作用,连接了 JavaScript(JS VM) 和 Java(JNI)/OC。通过 Java 和 OC 对操作系统提供的原生组件进行增删改查,最后由操作系统调用渲染引擎将原生组件渲染出来。

上图的右边是 Flutter 使用原生组件时的渲染路径图,你可以看到,Flutter 最上层的业务语言是 Dart,下一层也是 C++。C++ 的核心功能也是布局计算和黏合剂,由于是原生组件所以不会使用 Skia 渲染引擎,而是继续调用 Java/OC,最后也是由系统调用渲染引擎才能渲染原生组件。

二者的不同之处主要介绍两点。

我们再看一次上面这张图。第一个不同点,React Native 用的是 JavaScript,Flutter 用的是 Dart。

第二点,二者的渲染引擎不一样。React Native 用的是操作系统提供渲染引擎,渲染引擎只有一个。但 Flutter 用了两个渲染引擎,分别是 Skia 渲染引擎和操作系统提供渲染引擎,那两个渲染引擎应该怎么合作,渲染出同一个页面呢?

还记得微信小程序中的 RN-like 方案吗?我们前面说微信小程序中同时使用了 WebView 和 Native 视图,并且将 WebView 和 Native 视图渲染到了同一个层级,而 WebView 常用的渲染引擎就是 Skia。

你可以看下下面这张图:

要将 Flutter 的 Skia 渲染引擎和系统渲染引擎绘制在同一个页面,和小程序将 WebView 和 Native 视图渲染到同一个页面的原理是相似的。Flutter 官方给出的原话是:

Hybrid composition appends the native android.view.View to the view hierarchy.
混合集成模式会将原生的 android.view.View 附加到视图层次结构中。

用大白话说就是,在 Flutter 同时使用两个渲染引擎时,会将系统引擎渲染出来的原生视图放到 Skia 渲染出来的视图层级之中。而且 Flutter 官方也承认,使用混合集成模式渲染原生视图 时,会有线程竞争、显存会被复制两次、浪费显存和绘图性能等问题。

由于 React Native 只使用系统渲染引擎,就不存在混合集成模式这种说法,所有的视图都是原生视图,这就简单很多了。

总结

在移动浪潮之下,诞生了小程序、Flutter、React Native 等非常多的优秀的跨端框架,是它们给我们大前端开发者带来了便利,也是它们给用户带来了更好体验。

小程序的 WXML 和 WXSS 类似于裁剪的 HTML/CSS,同时它的底层架构又参考了很多 PWA 和 React Native 的思想。更有意思的是,小程序不仅是一个跨端框架,还是一个取得巨大成功的商业平台,把用户和我们这些开发者连接得更紧密了。

Flutter 的创始人本身就是从 Chrome 团队出来的,你也可以从 Flutter 的架构思想上看到它与 Chrome 的很多相似之处,Flutter 不仅是成功的跨端框架,还是为 Fuchsia OS 铺平道路的技术创新。

而 React Native 是一个纯粹的技术框架,它采取了更加社区化的运作方式,微软、Expo、Callstack 都参与了 React Native 的共建。更关键的是,它把 Web 很多规范、编程思想带到了移动端,并且在技术上掀起了一波又一波的移动跨端的探索。

不懂技术原理,只懂如何使用,是没办法抓住技术带来的机遇的。

2022-7-25

posted @ 2022-07-25 00:54  白乾涛  阅读(1577)  评论(1编辑  收藏  举报