Flutter:跨端方案比较

原创:有趣知识点摸索型文章
创作不易,请珍惜,之后会持续更新,不断完善
个人比较喜欢做笔记和写总结,毕竟好记性不如烂笔头哈哈,这些文章记录了我的IOS成长历程,希望能与大家一起进步
温馨提示:由于简书不支持目录跳转,大家可通过command + F 输入目录标题后迅速寻找到你所需要的内容

目录

  • 一、ReactNative
  • 二、Weex
  • 三、为什么选择用Flutter
  • 四、Flutter开发语言Dart

一、ReactNative

从零入门ReactNative的建议
  • 需要有htmljscss等前端编程语言
  • 然后,先去理解React基本使用
  • 搭建ReactNative开发环境,选用iOS/Android先把项目运行起来。
  • 学习ReactNative基本开发流程。
  • 写页面必须先搞懂flex布局方式。
  • 学习ReactNative相关组件。
  • 深入学习ReactNative的原理等高级知识。
  • 阅读源码,真正的搞懂它。

1、相关命令

搭建环境

依赖 Node 环境

brew install node
node -v 查看node版本
sudo npm cache clean -f 清除缓存
sudo n stable 升级到稳定版本
sudo npm install -g n 按照node

安装 watchman。其是 facebook 的一个开源项目,它开源用来监视文件并且记录文件的改动情况,当文件变更它可以触发一些操作,例如执行一些命令等等。

brew install watchman

创建项目

npx react-native init xxx_name_xxx

通过代码启动工程或者直接使用 Xcode 启动工程编译调试

yarn iOS

react-native 相关命令

react-native init MyAppName --version 0.64.0
react-native --version 查看版本
npx react-native info 命令查看当前的版本

2、工程目录

  • 工程入口代码文件index.js,类似 iOS 中的 main.m 文件。
  • App组件定义App.js,当然也可以新建文件定义组件,类似 iOS 中的 AppDelegate.m 文件。
  • iOS 和 Android 两个文件夹是原生相关的工程。
  • package.json 工程的配置文件,定义脚本命令、包依赖、测试等相关
  • 相关依赖库 node_modules
  • App.json RN模块的配置。其中name配置字符串,必须和RCTRootView初始化initWithBridge:moduleName:initialProperties:用到的moduleName一样。
{ "name": "HelloFirstRNDemo",
"displayName": "HelloFirstRNDemo"}

3、开发基本流程

ReactNative 代码

1、在App.js中,导入 RN 相关的组件,其中包括系统组件与自定义组件的导入

import React from 'react';
import {
  SafeAreaView,
  ScrollView,
  StatusBar,
  Text,
  useColorScheme,
  View,
} from 'react-native';

2、在App.js中,自定义组件,即搭建页面。

class HelloWorld extends React.Component {
  render() {
    return (
      <SafeAreaView>
        <Text style={styles.title}> hello my world! </Text>
        <ScrollView>
          <Section> section 1</Section>
      </SafeAreaView>
    )
  }
}

3、在App.js中,定义样式,推荐统一定义样式,RN 使用布局方式为Flex 布局。

const styles = StyleSheet.create({
    title: {
        fontSize: 42,
    },
});

4、在index.js中,组件的注册。

import {AppRegistry} from 'react-native';
import {HelloWorld} from './App';
import {name as appName} from './app.json';

// 第一个参数:应用名称,第二个参数:组件名。
AppRegistry.registerComponent(appName, () => HelloWorld);
原生端(iOS为例)
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];

// 其中 self 为实现 RCTBridgeDelegate 的实例,必须实现方法如下:
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge {
      NSURL *url = nil;
    #if DEBUG
      url = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
    #else
      url = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
    #endif
      return url;
}

初始化ReactNative代码的加载容器类RCTRootView

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                   moduleName:@"FirstRNDemo"
                                            initialProperties:nil];

二、Weex

1、概述

阿里巴巴在Qcon大会上宣布跨平台移动开发工具 Weex 开放内测邀请。Weex 能够完美兼顾性能与动态性,让移动开发者通过简捷的前端语法写出 Native 级别的性能体验,并支持 iOS、安卓、YunOS 及 Web 等多端部署。

近一年来,ReactNativeWeex 这些跨平台技术对 Native 开发者来说,冲击是巨大的。Native 在开发App的时候存在一些弊端,比如客户端需要频繁更新,iOS更新时间还要受到审核的牵制;iOS、Android 和前端同时开发同一个需求,在人员成本上消耗大;Hybrid 的性能和 Native 相比又差了一点。ReactNativeWeex 的出现,就是为了解决这些痛点的。

Weex 从出生那天起,仿佛就是和 ReactNative 是一对。ReactNative 宣称 “Learn once, write anywhere”,而 Weex 宣称“Write Once, Run Everywhere”。Weex 从出生那天起,就被给予了一统三端的厚望。ReactNative 可以支持 iOS、Android,而 Weex 可以支持iOS、Android、HTML5。一统三端就解决了前言里面说的第二个痛点,同时开发浪费人员成本的问题。

Native 移动开发者只需要在本地导入 Weex 的 SDK,就可以通过 HTML/CSS/JavaScript 网页的这套编程语言来开发 Native 级别的 Weex 界面。这意味着可以直接用现有 Web 开发的编辑器和 IDE 的代码补全、提示、检查等功能。从而也给前端人员开发 Native 端,较低的开发成本和学习成本。

Weex 是一种轻量级、可扩展、高性能框架。集成也很方便,可以直接在 HTML5 页面嵌入,也可嵌在原生UI中。由于和 ReactNative 一样,都会调用 Native 端的原生控件,所以在性能上比 Hybrid 高出一个层次。这就解决了前言里面所说的第三个痛点,性能问题。

Weex 非常轻量,体积小巧,语法简单,方便接入和上手。ReactNative 官方只允许将 ReactNative 基础js 库和业务JS一起打成一个 JS bundle,没有提供分包的功能,所以如果想节约流量就必须制作分包打包工具。而 Weex 默认打的 JS bundle 只包含业务 JS 代码,体积小很多,基础 JS 库包含在 Weex SDK 中,这一点 Weex 与 Facebook 的 React Native 相比,Weex 更加轻量,体积小巧。把 Weex 生成的 JS bundle 轻松部署到服务器端,然后 Push 到客户端,或者客户端请求新的资源即可完成发布。如此快速的迭代就解决了前言里面说的第一个痛点,发布无法控制时间。

WeexNative 组件和 API 都可以横向扩展,业务方可去中心化横向灵活化定制组件和功能模块。并且还可以直接复用 Web 前端的工程化管理和监控性能等工具。


2、工作原理

上图是官方给的一张原理图,Weex是如何把JS打包成JS Bundle的原理本篇文章暂时不涉及。本篇文章会详细分析Weex是如何在Native端工作的。笔者把Native端的原理再次细分,如下图:

Weex可以通过自己设计的DSL,书写.we文件或者.vue文件来开发界面,整个页面书写分成了3段,templatestylescript,借鉴了成熟的 MVVM 的思想。

Weex在性能方面,为了尽可能的提升客户端的性能,DSLTransformer全部都放在了服务器端实现,Weex会在服务器端将XML + CSS + JavaScript 代码全部都转换成JS Bundle。服务器将JS Bundle部署到Server上和CDN上。

WeexReact Native不同的是,WeexJS Framework内置在SDK里面,用来解析从服务器上下载的JS Bundle,这样也减少了每个JS Bundle的体积,不再有React Native需要分包的问题。客户端请求完JS Bundle以后,传给JS FrameworkJS Framework解析完成以后会输出Json格式的Virtual DOM,客户端Native只需要专心负责 Virtual DOM 的解析和布局、UI 渲染。然而这一套解析,布局,渲染的逻辑SDK基本实现了。

最后Weex支持三端一致,服务器上的一份JS Bundle,通过解析,实现iOS/Android/HTML5 三端的一致性。


三、为什么选择用Flutter

目前跨端主流的框架FlutterHybirdRNWeex。其中除了Flutter, 其他三个都属于Web型,即HTML5 + CSS + JavaScript实现。

React-Native、Weex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用WebView,也不使用操作系统的原生控件。 相反,Flutter 使用自己的高性能渲染引擎来绘制widget。Flutter 使用C、C ++、Dart 和 Skia(2D渲染引擎)构建。在 iOS上,Flutter 引擎的C/C ++ 代码使用 LLVM 编译,任何 Dart 代码都是 AOT 编译为本地代码的,Flutter 应用程序使用本机指令集运行(不涉及解释器)。而在 Android 下,Flutter 引擎的 C/C ++ 代码是用 Android 的NDK 编译的,任何 Dart 代码都是 AOT 编译成本地代码的,Flutter 应用程序依然使用本机指令集运行(不涉及解释器)。因此,Flutter 能达到原生应用一样的性能。

Web 型方案的优点:

  • 开发迅速
  • 复用前端生态
  • 支持热更

Web 型方案的缺点:

  • 一些复杂的交互无法带来很好的体验,比如手势、视频播放。
  • 在不打离线包的前提下,进入页面会有白屏。
  • iOS 与Android 双端渲染效果存在差异。
跨端大生态
  • JSCore 虚拟机(虚拟 DOM)
    • Hybird
    • React Native
    • Weex
  • Dart 虚拟机(自绘引擎)

1、Flutter 原理

相比 React NativeWeexFlutter 实现跨平台采用了更为彻底的方案。它既没有采用WebView 也没有采用 JavaScript,而是自己实现了一台UI框架,然后直接系统更底层渲染系统上画UI。所以它采用的开发语言不是JS,而是DartDart是面向对象的、类定义的、单继承的语言)。它的语法类似C语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和(sound type syste)。 据称Dart语言可以编译成原生代码,直接跟原生通信。

同时,Flutter将UI组件和渲染器从平台移动到应用程序中,这使得它们可以自定义和可扩展。Flutter唯一要求系统提供的是canvas,以便定制的UI组件可以出现在设备的屏幕上,以及访问事件(触摸,定时器等)和服务(位置、相机等)。这是Flutter可以做到跨平台而且高效的关键。另外Flutter学习了RN的UI编程方式,引入了状态机,更新UI时只更新最小改变区域。

系统的UI框架可以取代,但是系统提供的一些服务是无法取代的。Flutter在跟系统service通信方式,采用的是一种类似插件式的方式,或者有点像远程过程调用RPC方式,这种方式据说也要比RN的桥接方式高效。


2、Flutter 和 React Native 底层框架对比

React-NativeWeex 核心是通过 Javascript 开发,执行时需要 Javascript 解释器,UI 是通过原生控件渲染。Flutter 与用于构建移动应用程序的其它大多数框架不同,因为 Flutter 既不使用 WebView,也不使用操作系统的原生控件。 相反,Flutter 使用自己的高性能渲染引擎来绘制 widgetFlutter 使用 C、C ++、DartSkia(2D渲染引擎)构建。

Skia 是一个 2D的绘图引擎库,ChromeAndroid 均采用 Skia 作为绘图引擎。Android 自带了 Skia,所以 Flutter Android SDK要比 iOS SDK小很多。

ReactNative 中,引入了虚拟 DOM 来减少DOM的回流和重绘,系统将虚拟 DOM 与真正的 DOM 进行比较,生成一组最小的更改,然后执行这些更改,以更新真正的 DOM。最后,平台重新绘制真实的 DOM 到画布中。

React Native 是移动开发的一大进步,并且是 Flutter 的灵感来源,但 Flutter 更进一步。 在 Flutter 中,UI 组件和渲染器已经从平台中集成到用户的应用程序中。没有系统 UI 组件可以操作,所以原来虚拟控件树的地方现在是真实的控件树,Flutter 渲染 UI 控件树并将其绘制到平台画布上。

如果说非要比较 FlutterReact Native 的优势,可以参考下面几点:

UI 一致性

Flutter 因为是自己做的渲染,因此在iOS和Android的效果基本完全一致。 React Native存在将RN控件转换为对应平台原生控件的过程,存在一定的差异。

动态化技术

Flutter使用的Dart语言,支持AOTJIT两种模式,在Dev时候,通过JIT可以实现热重载,开发者可以即时的看到代码修改的效果。而在Release Build的时候,通过AOT事先编译,来最大化的优化性能。因此目前Flutter不支持代码的热更新。

ReactNative 的代码通过加载 JSBundle.js 执行,JSBundle.js 可以保存在本地,也可以通过远程加载。目前有很多RN的热更新方案供选择。

App体积

Flutter iOS空项目 30M左右,Android空项目 7M左右。 (iOS需要额外集成SkiaReact Native iOS空项目 3M左右,Android 20M左右。(Android会加入OKHttp导致体积增大)

Flutter 部分的底层功能在 Android 系统上已经有实现,因此 Android 上适配要好(RNAndroid 上有可能遇到兼容性问题)。

Flutter的优势

运行效率上,FlutterReactNative都可以达到理论上的60帧的刷新率,来实现「Native般的流畅体验」,Flutter是全Native在执行,基于底层代码(Android 上为 C++ with NDK,iOS 上为 C++ with LLVM),而ReactNativeNative控件 + JavaScript代码,实际性能上,Flutter应该优于ReactNative,据官方文档,Flutter可以在支持的设备上达到120FPS,而ReactNative的文档上,只提到了可以达到60FPS。

兼容性上,Flutter 提供的 widget 都是基于 skia来实现和精心定制的,与具体平台没关,所以能保持很高的跨 osos version 的兼容性。 Flutter 从更基础的层去抹平平台差异,站在了更宽广、更可控的一个基础平台上去演变和发展。 Flutter 官方提供了大部分 Material Design 控件的实现(甚至比 Android Design Support 实现的更多)。


四、Flutter开发语言Dart

1、为什么要使用Dart语言

学习Flutter就不得不提到Dart,那FlutterDart有什么关系?确实有关系,早期的Flutter团队评估了十多种语言,并选择了Dart,因为它符合他们构建用户界面的方式。

Dart能成为Flutter不可或缺的一部分,根本原因还是因为其具有以下特性:

  • DartAOTAhead Of Time)编译的,编译成快速、可预测的本地代码,使Flutter几乎都可以使用Dart编写。这不仅使Flutter变得更快,而且几乎所有的东西(包括所有的小部件)都可以定制
  • Dart也可以JITJust In Time)编译,开发周期异常快,工作流颠覆常规(包括Flutter流行的亚秒级有状态热重载)
  • Dart可以更轻松地创建以60fps运行的流畅动画和转场。Dart可以在没有锁的情况下进行对象分配和垃圾回收。就像JavaScript一样,Dart避免了抢占式调度和共享内存(因而也不需要锁)。由于Flutter应用程序被编译为本地代码,因此它们不需要在领域之间建立缓慢的桥梁(例如,JavaScript到本地代码)。它的启动速度也快得多
  • Dart使Flutter不需要单独的声明式布局语言,如JSXXML,或单独的可视化界面构建器,因为Dart的声明式编程布局易于阅读和可视化。所有的布局使用一种语言,聚集在一处,Flutter很容易提供高级工具,使布局更简单
  • 开发人员发现Dart特别容易学习,因为它具有静态和动态语言用户都熟悉的特性

2、编译与执行

历史上,计算机语言分为两组:静态语言(例如C,其中变量类型是在编译时静态指定的)和动态语言(例如 SmalltalkJavaScript,其中变量的类型可以在运行时改变)。静态语言通常编译成目标机器的本地机器代码(或汇编代码)程序,该程序在运行时直接由硬件执行。动态语言由解释器执行,不产生机器语言代码。

当然,事情后来变得复杂得多。虚拟机(VM)的概念开始流行,它其实只是一个高级的解释器,用软件模拟硬件设备。虚拟机使语言移植到新的硬件平台更容易。因此,VM的输入语言常常是中间语言。例如,一种编程语言(如Java)被编译成中间语言(字节码),然后在VMJVM)中执行。

另外,现在有即时(JIT)编译器。JIT编译器在程序执行期间运行,即时编译代码。原先在程序创建期间(运行时之前)执行的编译器现在称为AOT编译器。

一般来说,只有静态语言才适合AOT编译为本地机器代码,因为机器语言通常需要知道数据的类型,而动态语言中的类型事先并不确定。因此,动态语言通常被解释或JIT编译。

在开发过程中AOT编译,开发周期(从更改程序到能够执行程序以查看更改结果的时间)总是很慢。但是AOT编译产生的程序可以更可预测地执行,并且运行时不需要停下来分析和编译。AOT编译的程序也更快地开始执行(因为它们已经被编译)。

相反,JIT编译提供了更快的开发周期,但可能导致执行速度较慢或时快时慢。特别是,JIT编译器启动较慢,因为当程序开始运行时,JIT编译器必须在代码执行之前进行分析和编译。研究表明,如果开始执行需要超过几秒钟,许多人将放弃应用。


3、Dart的编译与执行

在创造Dart之前,Dart团队成员在高级编译器和虚拟机上做了开创性的工作,包括动态语言(如JavaScript的V8引擎和SmalltalkStrongtalk)以及静态语言(如用于JavaHotspot编译器)。他们利用这些经验使Dart在编译和执行方面非常灵活。

Dart是同时非常适合AOT编译和JIT编译的少数语言之一(也许是唯一的“主流”语言)。支持这两种编译方式为Dart和(特别是)Flutter提供了显著的优势。

JIT编译在开发过程中使用,编译器速度特别快。然后,当一个应用程序准备发布时,它被AOT编译。因此,借助先进的工具和编译器,Dart具有两全其美的优势:极快的开发周期、快速的执行速度和极短启动时间。

Dart在编译和执行方面的灵活性并不止于此。例如,Dart可以编译成JavaScript,所以浏览器可以执行。这允许在移动应用和网络应用之间重复使用代码。开发人员报告他们的移动和网络应用程序之间的代码重用率高达70%。通过将Dart编译为本地代码,或者编译为JavaScript并将其与node.js一起使用,Dart也可以在服务器上使用。

最后,Dart还提供了一个独立的虚拟机(本质上就像解释器一样),虚拟机使用Dart语言本身作为其中间语言。

Dart可以进行高效的AOT编译或JIT编译、解释或转译成其他语言。Dart编译和执行不仅非常灵活,而且速度特别快。

即使编译后的代码也可能需要一个接口来与平台代码进行交互,并且这也可以称为桥,但它通常比动态语言所需的桥快几个数量级。另外,由于Dart允许将小部件等内容移至应用程序中,因此减少了桥接的需求。


4、布局

声明式布局语言:

Center(child:
  Column(children: [
    Text('Hello, World!'),
    Icon(Icons.star, color: Colors.green),
  ])
)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容