近年来,前端领域的跨端技术越来越多了:React Native、Weex、Flutter、Electron、Kraken 等等。
那么多跨端方案,它们有没有通用的思路?我们能不能从这么多方案中找出本质的原理?
本文尝试探究以下问题:
- 什么是跨平台
- 有哪些方案是跨平台的
- 跨端和跨平台的区别是什么
- 前端领域有哪些跨端方案
- 跨平台、跨端的通用原理是什么
什么是跨平台
我们知道,CPU 有不同的架构和指令集,上层也有不同的操作系统,一个系统的可执行文件在另一个系统上就是不可执行的,比如 Windows 的 EXE 文件在 mac 上就不能直接执行。不同的系统就是不同的运行平台。可执行文件是不跨平台的。
不同平台提供的 API 不同,所以代码逻辑可能也不同,需要不同平台单独维护代码。这样就带来了几个问题:
- 多平台各自开发,怎么保证功能是一致的
- 多平台各自开发,那是不是得各自测试,开发和测试的人力都是多份的
所以出现了跨平台的一些技术,目标是一份代码跑在任意平台。
我们先来看一些各领域的跨平台方案:
浏览器
操作系统不同,浏览器上跑的网页的代码确实同一份。浏览器就是一种历史悠久的跨平台方案。
网页跨平台不意味着浏览器也是跨平台的,浏览器的可执行文件还是每个平台单独开发和编译的,但是他们支持的网页解析逻辑一样,这样上面跑的网页就是跨平台的。
浏览器提供了一个容器,屏蔽了底层差异,提供了统一的 API(DOM API),这样就可以实现同一份代码跑在不同平台的统一的容器里。这个容器叫做浏览器引擎,由 js 引擎、渲染引擎等构成。
Docker
Docker 是一种虚拟化技术,可以在操作系统之上加一个虚拟层,在这层之上划分一到多个容器,容器里再去跑系统、app,这样可以实现硬件和软件的分离,动态分配硬件资源给容器,并且方便 app 运行环境的整体迁移(保存成镜像)。
Docker 很明显也是一种跨平台技术,同一个镜像可以跑在任何操作系统的 Docker 上。只要不同操作系统实现同样的容器即可。
JVM
Java 是一门编译 + 解释的语言,Java 源码编译成字节码,然后字节码直接在 VM 上解释执行。
Java 为什么这么火呢?主要是因为跨平台。
C、C++ 这种语言写的代码需要编译成不同操作系统上的可执行文件来跑,而且每个平台的代码可能还不一样,需要写多份。
Java 因为提供了 JVM 容器,只要把源码编译成 JVM 能解释的字节码就行了,而且 jdk 提供了统一的 API,分别由不同操作系统的底层 API 来实现,这样对于 Java 代码来说,不同操作系统的代码是一致的。
JVM 也是通过容器的技术实现了一份代码跑在多个平台,而且 jre 提供了统一的 API,屏蔽掉了底层的差异。
Node、Deno
Node 和 Deno 也是跨平台的技术,通过提供一套一致的 API,让其上的 js 代码可以跨平台。这些 API 也是不同平台各自实现的。
Electron
Electron 内置了 Chromium,并为其注入了 Node 的 API 和一些 GUI 相关的 API,是基于两大跨平台技术综合而成的跨平台方案。基于这些方案的组合使得 Electron 支持用前端技术开发桌面端。
跨平台方案的优缺点
跨平台方案的优点很明显,就是一份代码跑在不同平台的同样的容器内,不用不同平台单独开发,节省成本。
但是跨平台方案也有缺点:
-
因为多了一层容器,所以性能相比直接调用系统 API 会有所下降
-
为了实现多平台的一致,需要提供一套统一的 API,这套 API 有两个难题:
-
API 怎么设计。要综合不同平台的能力,取一个合适的集合来实现。设计上有一定难度。Node、Deno、Java 都抽象了操作系统的能力,提供了各自的跨平台 API
-
部分 API 很难做到多平台的一致性
-
当容器没有提供的能力需要扩展的时候比较麻烦,比如 js 引擎的 Bridge、 JVM 的 Jni、Node 的 C++ Addon 等都是为这个容器扩展能力的方式
前端领域的跨端方案
跨平台指的是跨操作系统,而跨端是指客户端。
客户端的特点就是有界面、有逻辑,所以包含逻辑跨端和渲染跨端。主要的客户端有 Web、安卓、iOS、IOT设备等。
现在主流的跨端方案有React Native、Weex、Flutter、Kraken 以及各家自研的跨端引擎等。
React Native
跨端包括逻辑跨端和渲染跨端,rn 的逻辑跨端是基于 js 引擎,通过 bridge 注入一些设备能力的 API,而渲染跨端则是使用安卓、iOS 实现 React 的 Virtual DOM 的渲染。
其中 Native API 和组件(灰色画出的部分)并没有做到双端一致,而且有的时候扩展图中灰色部分需要原生配合,混杂 rn 代码和自己扩展的代码导致代码比较难管理。最著名的事件就是 Airbnb 从最大的 React Native 支持者到弃用 React Native。
Weex
Weex 也是类似的思路来实现跨端的,不过他对接的上层 UI 框架是 Vue,而且努力做到了双端的组件 和 API 的一致性(虽然后续维护跟不上了)。架构和上图类似。
Flutter
Flutter 是近些年流行的跨端方案,跨的端包括安卓、iOS、Web 等。它最大的特点是渲染不是基于操作系统的组件,而是直接基于绘图库(skia)来绘制的,这样做到了渲染的跨端。逻辑的跨端也不是基于 js 引擎,而是自研的 Dart VM 来跨端,通过 Dart 语言来写逻辑,
Kraken
跨端包括两部分,渲染跨端和逻辑跨端。有时候只需要渲染跨端、有时候只需要逻辑跨端,有的时候需要完整的跨端引擎,这 3 种情况都有各自的适用场景。
Kraken 就是一个跨端渲染引擎,基于 Flutter 的绘图能力实现了 CSS 的渲染,实现了渲染的跨端。
自研渲染引擎
跨端引擎很依赖底层实现的组件和 API,用开源方案也一样需要扩展这部分,所以有一定规模的团队都会选择自研。
自研跨端引擎会和 rn、Weex 不同:
-
渲染部分不需要实现 Virtual DOM 的渲染,而是直接对接 DOM API,上层应用基于这些 DOM API 实现跨端渲染。这样理论上可以对接任意前端框架。
-
逻辑部分也是基于 js 引擎,通过 Binding 直接注入一些 C++ 实现的 API,或者运行时通过 Bridge 来注入一些安卓、iOS 实现的 API。
自研跨端引擎的好处是组件和 API 可以自己扩展,更快地响应业务的需求。其中组件和 API 的双端一致性,以及统一的 API 的设计都是难点。
跨端的通用原理是什么
其实跨端和跨平台的思路类似,都是实现一个容器,给它提供统一的 API,这套 API 由不同的平台各自实现,保证一致的功能。
具体一些的话,跨端分为渲染和逻辑跨端,有的时候只需要单独的渲染跨端方案(比如 Karen)和逻辑跨端方案,有的时候需要完整的跨端引擎。
Weex、React Native 的渲染部分都是通过实现了 Virtual DOM 的渲染,用安卓、iOS各自的渲染方式实现,逻辑部分使用 js 引擎,通过 Bridge 注入一些安卓、iOS 的 API。
Flutter 则是直接使用 Skia 绘图库绘制,并且逻辑跨端使用 Dart VM。
但是不管具体实现怎样,思路都大同小异:跨端引擎需要实现一个渲染引擎、实现一个 VM,基于这套架构实现各种组件和 API,跨端容器上层对接一个 UI 框架,再上层的业务代码可以基于容器的 API 实现跨端的渲染和逻辑。
Web Container
这两天 Web Container 比较火,其实也是一种跨平台技术,它是在浏览器里面实现的容器,通过 WASM 实现了 Node 的 API,这样在这个容器里面可以跑 Node 代码。其实思路比较常见,但是是一个新场景。
浏览器容器之上又跑了个容器,容器套娃。
总结
我们聊了跨平台和跨端的区别,跨平台是指跨操作系统,而跨端则是指跨客户端。
跨平台技术聊了 Docker、浏览器、JVM、Node、Deno、Electron、Web Container 等,他们都是跨平台(操作系统)的方案,跨平台有优点也有缺点,缺点就在于 API 的设计比较难,Node、Deno、Java 等都有自己的一层 API 设计;API 一致性的保障也比较困难;其次就是扩展方式复杂一些(JVM 的 Jni、Node 的 C++ Addon 等)。
跨端方案聊了 React Native、Weex、Flutter、Kraken 等,有的是绑定了 React、Vue 等前端框架,直接从 Virtual DOM 渲染,有的是实现了 DOM API,可以对接任意前端框架。当然可以单独做渲染或逻辑跨端。渲染跨端或者用安卓、iOS 提供的方式,或者自己绘制,逻辑跨端或者用 js 引擎(可以对接前端框架)或者用 Dart VM。
希望这篇文章可以让你理解跨端和跨平台的容器的思路和优缺点,遇到一些新技术(比如 Web Container)也能快速的理解。
转自公众号:神光的编程秘籍