# 再谈移动端跨平台框架 Flutter 与 React Native

## 前言

这几年在大前端的开发领域，选择跨端方案的公司和部门越来越多，一方面是跨平台的前端框架越来越成熟，另一方面也是因原生开发者正逐年减少。所以，在当下掌握一门跨平台的技术栈还是很有必要的，无论从广度还是从深度都会有所帮助。

那我们应该选择哪种技术方案呢？如果这个问题放在几年前，答案可能会有很多。不过现在看来，市面上仅剩两种主流方案，就是经常听到的 React Native 和 Flutter。一个出自 Facebook，一个出自 Google。

这两个方案的优劣已有很多点评，基本上形成了两种阵营。但在我看来，它们其实没有明显的差距。如果有，早就被市场所淘汰了。现在看来所谓的劣势，很快就会被那帮天才工程师们，想出解决方案而弥补上了。这也许是竟争帮助了整个生态的完善。反而是 Apple 一直没有跟上，可能还是源于闭源生态，没有另外两家那么的急于变革。

反观 Google 的野心其实是很大的，想通过跨平台方案（无论是 Flutter 还是 Kotlin），从社区和开发者入手一统语言，甚至操作系统([Fuchsia](https://link.juejin.cn/?target=https%3A%2F%2Ffuchsia.dev%2F))，从而扩展更大的版图。Facebook 则想利用自己多年在前端领域积累的丰富经验，通过 React 切入所有平台。这可能成为了两套框架的设计初衷。

> Microsoft 到是另辟蹊径，在 IDE（VSCode）上花大力气，帮助大家建立更好的开发体验，统一了开发环境。

**SDK 版本**

Flutter: 2.5.x\
React Native: 0.64.x

## 1. 架构

### 1.1 设计理念

在端上的开发，有前辈总结了一个很精辟的观点：端上的开发无外乎三件事，“**数据获取**”，“**状态管理**”，“**页面渲染**”。而在跨端领域的竟争，我理解是“**虚拟机**”，“**渲染引擎**”，“**原生交互**”，“**开发环境**”的竟争。而在这几点上，无论是 Flutter 还是 React Native (以下简称 RN) 都有非常棒的解决方案。

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/e0dfbc16d2bc40c7a3ca457f1cbbc227~tplv-k3u1fbpfcp-watermark.awebp?)

首先从 Flutter 来看，在**虚拟机**上使用了 Dart VM，Dart 支持 JIT 与 AOT 两种编译模式，也就是我们所说的动态编译与静态编译。在开发阶段使用 JIT 编译，实现热更新预览，动态加载等，而在发布阶段使用 AOT 模式编译为机器码，保证启动速度和跨端信息的传递效率。在**渲染引擎**上，Flutter 使用了 [Skia](https://link.juejin.cn/?target=https%3A%2F%2Fskia.org%2F) 渲染引擎进行视图绘制，避开了不同平台上控件渲染差异。而且，少了这一层的交互，使得效率也得到提升。而在**原生交互**上，因为 Dart 本身跨平台的特性，底层 C++ 可以直接访问到原生的 API，加上信息使用机器码进行传递 (BinaryMessage)，所以与原生交互的效率非常高。

然后再说 RN ，在早期的架构上**虚拟机**使用的是 JSC (Javascript Core) 执行运算，这样它可以充分复用 JS 生态，吸引大量前端开发者参与。而且由于 JS 天生跨平台的特点，跨端移值 App 也顺理成章。在**渲染引擎**上 RN 没有直接使用 WebKit 或其它 Web 引擎，因为之前 Web 在构建复杂页面时带来的计算消耗，远比不上纯原生引擎的渲染。所以它直接复用了原生的渲染通道，这样就可以带来与原生近乎一致的体验。

不过说到这儿，你可能发现虽然早期的 RN 架构充分利用了现有生态，但毕竟不像 Flutter 那样从头到尾都自己来，那么的撤底。带来的问题就是，在 JSC 到原生渲染这一层，用了非常多的 Bridge，并通过 JSON 序列化在多个线程里来回传递信息，这样的消耗在简单的交互过程中可能不明显，而在大量的交互与渲染上会有明显的卡顿，这也成为广为诟病的一点。不过在新的架构中, RN 也做出了新的方案去解决这些痛点，下面会有介绍。

但我们知道 Flutter 也不是完美的，虽然什么事情都自己造自己来，但因为缺少成熟的生态，很多问题都需要官方或社区提供足够的轮子才能解决，否则开发者会在遇到特定问题时，只能自己想办法。另外，Dart 发布阶段用了静态编译，虽然效率得到了提升，但也缺少了在线动态更新的灵活性。

### 1.2 核心架构

#### 1.3.1 Flutter

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d001d2ca58564f90ba05f3595b95bfb5~tplv-k3u1fbpfcp-watermark.awebp?)

Flutter 的架构分为了三层，我们大多情况只与 Flutter Framework 层交互，更多平台无关的的底层能力已被封装好。这也使得 Flutter Framework 非常的轻，如果你需要更多的原生能力，通常使用各类 Flutter Plugin 比如 [Camera](https://link.juejin.cn/?target=https%3A%2F%2Fpub.dev%2Fpackages%2Fcamera)。

> 所以原生能力（轮子）依赖于官方和社区的产出速度

#### 1.3.2 React

**新旧架构对比**

**Old**

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/141e85ff124b4a87a7f256a80013f44f~tplv-k3u1fbpfcp-watermark.awebp?)

三个线程各自负责运算，渲染，Native 交互，中间的交互使用 Bridge 与 JSON 信息格式进行传递。

**New**

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/427e3fc8a1df4fbab42b818c82f014ad~tplv-k3u1fbpfcp-watermark.awebp?)

新的架构主要有两点改变

1. JS Bundle 不再依赖于 JSC(Javascript Core)。换句话说，它可以编译和应用在任何 JS 引擎 (V8等)。
2. 引入 [JSI](https://link.juejin.cn/?target=https%3A%2F%2Fblog.notesnook.com%2Fgetting-started-react-native-jsi%2F) 标准，基于 JSI 协议实现各自方法，使得 JS 可以直接引用 C++ 对象，反之亦然。与原生之间的交互不再用 Bridge 去做粘合。

> 渲染引擎仍是依赖原生的管道。猜测可能 FB 没有像 Google 那样，有这么多年的 Web 渲染引擎经验，轮子就不用再花时间再造了

**RN Bridge 上的变化**

**Old**

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/af3d3a89fb4747179dbe0e0f44566878~tplv-k3u1fbpfcp-watermark.awebp?)

可以看到 Bridge 非常的重

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/33efaffdcf7043b1ac1d57c5dff9c398~tplv-k3u1fbpfcp-watermark.awebp?)

将原先较重的 Bridge 分拆成两个模块，Farbric 处理 UI，TurboModules 处理与原生交互

> 两个模块均是遵循 JSI 协议的 C++ 模块

## 2. 核心流程

### 2.1 数据获取

#### 2.1.1 网络请求

| **Flutter**       | **React Native**                       |
| ----------------- | -------------------------------------- |
| http.dart 库C++ 实现 | 复用现有的 JS 库fetch, XMLHttpRequest, Axios |

Flutter

```dart
import 'package:http/http.dart' as http;

// 它返回一个 Flutter 的 Future 对象，类似 JS 的 Promise.
http.get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1'));
复制代码
```

RN

```javascript
fetch('https://reactnative.dev/movies.json');
复制代码
```

其它 JS 生态里的网络库都是适用的

#### 2.1.2 JSON 模型化

**Flutter**

官方提供了 [json\_serializable](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fdevelopment%2Fdata-and-backend%2Fjson) 库，让你可以先定义好模型与属性后，直接通过命令行生成对应的 JSON 转模型代码。

```dart
@JsonSerializable()
class User {
  User(this.name, this.email);

  String name;
  String email;

  /// A necessary factory constructor for creating a new User instance
  /// from a map. Pass the map to the generated `_$UserFromJson()` constructor.
  /// The constructor is named after the source class, in this case, User.
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  /// `toJson` is the convention for a class to declare support for serialization
  /// to JSON. The implementation simply calls the private, generated
  /// helper method `_$UserToJson`.
  Map<String, dynamic> toJson() => _$UserToJson(this);
}
复制代码
```

运行脚本命令即可

```
flutter pub run build_runner build
复制代码
```

**React Native**

官方没有提供了 JSON Model 转化库，需要自己找轮子。

### 2.2 状态管理

**Flutter**

正如 Flutter 将所有控件都定义为了 Widget 一样，它也分成了两种 Widget，一种是 Stateful, 另一种是 Stateless。

**Stateless**

Stateless 是无状态的，不能通过 state 状态去更新控件

```dart
class MyScaffold extends StatelessWidget {
  const MyScaffold({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ...
  }
}
复制代码
```

**Stateful**

Stateful 是有状态的，可以通过 state 变化去更新控件，但写法和 JS 有些不大一样，需要习惯

```dart
class FavoriteWidget extends StatefulWidget {
  const FavoriteWidget({Key? key}) : super(key: key);

  // 覆盖这个 createState 方法，实现状态管理
  @override
  _FavoriteWidgetState createState() => _FavoriteWidgetState();
}

// 状态设置通常写在私有方法这里
class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  
  // ···
  @override
  Widget build(BuildContext context) {
    ...页面构建
  }
  
  void _toggleFavorite() {
    // 用 setState 去进行状态的变更，以触发 Widget 的重新渲染
    setState(() {
      ...
        _isFavorited = false;
      ...
	  });
	}
}
复制代码
```

对于需要向上传递信息时，使用 InheritedWidget, Provider, FlutterHook 方式。

**React Native**

复用了 React 里的 [State](https://link.juejin.cn/?target=https%3A%2F%2Freactjs.org%2Fdocs%2Fstate-and-lifecycle.html) 模式，同时也支持现在流行的 Hook 方式使用 state，和 React 方式近乎类似。

```javascript
// React Native Counter Example using Hooks!

import React, { useState } from 'react';
import { View, Text, Button, StyleSheet } from 'react-native';

const App = () => {
  const [count, setCount] = useState(0);

  return (
    <View style={styles.container}>
      <Text>You clicked {count} times</Text>
      <Button
        onPress={() => setCount(count + 1)}
        title="Click me!"
      />
    </View>
  );
};

// React Native Styles
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  }
});
复制代码
```

### 2.3 页面渲染

#### 2.3.1 共性

在渲染方式上，理论基本一样，先是构建一颗平台无关性的虚拟树 (Virtual Dom Tree)，然后通过各自不同的实现自已渲染或交给原生进行渲染。

> 虚拟树的好处可以实现 UI 节点的局部更新，而不会全量刷新，具有平台无关性

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/020e6a9812df4b0992475d57a8c85315~tplv-k3u1fbpfcp-watermark.awebp?)

* 两个框架都是 UI 响应式框架（React Framework）

  `UI = f(state)`\
  UI 仅依赖于它的父类与自身的状态

  Flutter 在设计之初，也借鉴了很多 React 的设计思想。
* 所有组件都可被组合成一颗虚拟树虚拟树 (VDom)，在真正渲染前各个框架会把它们转化为各自的渲染对象 (RenderObj / VDom)。

#### 2.3.2 差异

**2.3.2.1 布局**

**Flutter**

在 Flutter 中，UI 组件称为 Widget，Flutter 将所有可能的控件都封装为 Widget ，而 RN 没有将所有控件封装，而是将样式与 Component 分离，进行自由组合。所以你不会在 RN 里看到长长的嵌套。

Flutter Widget 嵌套组合:

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/c6f5899571e7438190f0784faac48d33~tplv-k3u1fbpfcp-watermark.awebp?)

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/d715f0926be04ab2a618e76f687996bf~tplv-k3u1fbpfcp-watermark.awebp?)

虽然看起来组合 UI 很合理，但对于处理复杂的 UI 场景，就拙荆见肘了，比如[富文本](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fget-started%2Fflutter-for%2Fweb-devs%23making-inline-formatting-changes)。

在 RN 中，UI 组件称为 Component，布局沿用了 Component (类似 Web UI 元素) + Style (类似 CSS) 进行布局，没有像 Flutter Widget 一样先封装好各种"样式+组件"，而是把选择权交给你自己进行组合。

```javascript
import {View, Text, StyleSheet} from ‘react-native';

class HelloThere extends React.Component {
  render() {
    return (
      <View style={styles.box}>
        <Text>Hello World!</Text>
      </View>
    );
  }
}

var styles = StyleSheet.create({
  box: {
    borderColor: 'red',
    backgroundColor: '#fff',
    borderWidth: 1,
    padding: 10,
    width: 100,
    height: 100
  }
});
复制代码
```

**2.3.2.2 绘制**

**Flutter**

正如上面提到的架构所示，Flutter 不需要和原生渲染引擎打交道，直接通过 Skia （2D 渲染引擎）进行绘制 (GPU)，所以它的渲染管道非常简洁高效。

**React Native**

RN 是在通过 Yoga （布局引擎）计算好后位置后，通过不同平台的渲染管道进行渲染，所以这里在 Layout 计算与投递结果的过程中多了 Bridge 环节，效率可想而知。\\

> Flutter UI 所见即所得，在所有平台上表现一致。RN 依赖平台的原生控件样式，表现更趋于原生。

#### 2.3.3 渲染过程

**Flutter**

如前所说，Flutter 在更新完 UI Tree 后直接通过 GPU 渲染

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a986dfcc33ba44e7abc1bc153be69089~tplv-k3u1fbpfcp-watermark.awebp?)

**React Native**

和 React Render 很类似，先是更新 VDom ，然后再更新真正的组件，只是 RN 是 Native 组件

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f535b29855fa48de84824c20c4d5da0b~tplv-k3u1fbpfcp-watermark.awebp?)

### 2.4 原生交互

#### 2.4.1 混合开发 (Embed)

**Flutter**

**Flutter 内嵌入 Native 页面**

Fluttter 提供了 [AndroidView](https://link.juejin.cn/?target=https%3A%2F%2Fapi.flutter.dev%2Fflutter%2Fwidgets%2FAndroidView-class.html) 与 [UiKitView](https://link.juejin.cn/?target=https%3A%2F%2Fapi.flutter.dev%2Fflutter%2Fwidgets%2FUiKitView-class.html) 来支持原生页面的嵌入，不过这类 Widget 在使用中还要注意布局，事件的回调等诸多问题，从官方的文档来看其实不太推荐这类场景。虽然架构上没有限制，但目前桌面端的 Widget 还不支持。

```dart
if (defaultTargetPlatform == TargetPlatform.android) {
  return AndroidView(
    viewType: 'plugins.flutter.io/google_maps',
    onPlatformViewCreated: onPlatformViewCreated,
    gestureRecognizers: gestureRecognizers,
    creationParams: creationParams,
    creationParamsCodec: const StandardMessageCodec(),
  );
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
  return UiKitView(
    viewType: 'plugins.flutter.io/google_maps',
    onPlatformViewCreated: onPlatformViewCreated,
    gestureRecognizers: gestureRecognizers,
    creationParams: creationParams,
    creationParamsCodec: const StandardMessageCodec(),
  );
}
return Text(
    '$defaultTargetPlatform is not yet supported by the maps plugin');
复制代码
```

**Native 嵌入 Fluttter**

如 Flutter Demo 所示一样，它可以被嵌入任何 Activity 或 ViewController 中。

官方建议最好是在应用初始化时将 Flutter 环境加载好，或者在向用户展示 Flutter 页面前加载好。因为 Flutter 初始化要做很多事情，如 加载 Flutter 库，初始化 Dart VM, 创建 Dart Isolate(内存与线程管理)，UI 初始化等。预热的时间消耗大概是在 300ms 左右（参考[官方数据](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fdevelopment%2Fadd-to-app%2Fperformance%23memory-and-latency)）

**React Native**

React Native 与 Native 原生的控件互嵌相对比较容易。

**Native 内嵌入 RN 页面**

iOS

RCTRootView 我们可以认为是 RN 的一个容器，可以像处理普通 View 一样进行添加。但要注意 RN 里的 layout 要设置为 flex 布局，以便按容器的 size 去适配。

```swift
- (void)viewDidLoad {
    ...
    RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
                                                       moduleName:appName
                                                initialProperties:props];
    rootView.frame = CGRectMake(0, 0, self.view.width, 200);
    [self.view addSubview:rootView];
    ...
}
复制代码
```

**RN 内嵌入 Native 页面**

iOS

继承 RCTViewManager 。然后和事件通信一样，通过 RCT\_EXPORT\_MODULE 暴露 Native 对应的类，然后实现 view 方法，返回 native 的 view 实例。

```swift
// RNTMapManager.m
#import <MapKit/MapKit.h>

#import <React/RCTViewManager.h>

@interface RNTMapManager : RCTViewManager
@end

@implementation RNTMapManager

RCT_EXPORT_MODULE(RNTMap)

- (UIView *)view
{
  return [[MKMapView alloc] init];
}

@end
复制代码
```

然后在 RN 里直接插入该 View 到对应的 UI 组件下。

```javascript
import { requireNativeComponent } from 'react-native';

// requireNativeComponent automatically resolves 'RNTMap' to 'RNTMapManager'
module.exports = requireNativeComponent('RNTMap');

// MyApp.js

import MapView from './MapView.js';

...

render() {
  return <MapView style={{ flex: 1 }} />;
}
复制代码
```

[Android 的嵌入方式类似](https://link.juejin.cn/?target=https%3A%2F%2Freactnative.dev%2Fdocs%2Fnative-components-android)

#### 2.4.2 事件通信

**Flutter**

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/df741cc7f80a4426b21e5b5964e68cfb~tplv-k3u1fbpfcp-watermark.awebp?)

> Native <-> Shell (iOS /Android) <-> MethodChannel (Flutter Framework) <-> Dart Code

Message 会被不同的平台间进行类型转换，比如 `Map -> HashMap/Dictionary`

Dart

```dart
const channel = MethodChannel('foo');
final String greeting = await channel.invokeMethod('bar', 'world');
print(greeting);
复制代码
```

iOS

```swift
let channel = FlutterMethodChannel(name: "foo", binaryMessenger: flutterView)
channel.setMethodCallHandler {
  (call: FlutterMethodCall, result: FlutterResult) -> Void in
  switch (call.method) {
    case "bar": result("Hello, (call.arguments as! String)")
    default: result(FlutterMethodNotImplemented)
  }
}
复制代码
```

Android

```
val channel = MethodChannel(flutterView, "foo")
channel.setMethodCallHandler { call, result ->
  when (call.method) {
    "bar" -> result.success("Hello, ${call.arguments}")
    else -> result.notImplemented()
  }
}
复制代码
```

**React Native**

**Native**

在 Native 侧只需实现对应的协议，即可将类或方法暴露给 RN

> React 通常将要它们称为 Module

iOS

```swift
//  RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
  
// 在对应的 Native Class 声明上加上 RCTBridgeModule 协议
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end
  
  
// RCTCalendarModule.m
#import "RCTCalendarModule.h"

@implementation RCTCalendarModule

// 将这个类暴露给 RN 掉用。如果不指定名称，默认以类的名字命名
RCT_EXPORT_MODULE();

// 暴露一个方法给 RN
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}

@end
复制代码
```

Android

参照以下方式引入 RN 各个库，并继承 ReactContextBaseJavaModule ，加上 @ReactMethod 标识以暴露方法

```java
package com.your-app-name; // replace com.your-app-name with your app’s name
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;

public class CalendarModule extends ReactContextBaseJavaModule {
   CalendarModule(ReactApplicationContext context) {
       super(context);
   }
}

// 暴露方法给 RN
@ReactMethod
public void createCalendarEvent(String name, String location) {
}
复制代码
```

**RN**

这个时候就可以通过下面方法访问到 Native 的类

```javascript
const { CalendarModule } = ReactNative.NativeModules;
复制代码
```

## 3. 开发环境

### 3.1 编码

**Flutter**

[声明式的语言结构](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fresources%2Ffaq%23what-programming-paradigm-does-flutters-framework-use)

每个 Widget 可以继续嵌套 Widget ，有点类似俄罗斯套娃。

SwiftUI 也是声明式的，写法很类似

```swift
var container = Container( // grey box
  child: Center(
    child: Container( // red box
      child: Text(
        "Lorem ipsum",
        style: bold24Roboto,
      ),
      decoration: BoxDecoration(
        color: Colors.red[400],
      ),
      padding: EdgeInsets.all(16),
      width: 240, //max-width is 240
    ),
  ),
  width: 320, 
  height: 240,
  color: Colors.grey[300],
);
复制代码
```

**React Native**

RN 可以支持函数式编程 Hook 与 Class 方式编写。样式与组件代码分离，不会有长长嵌套出现。

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f3f6633a15864cd9b48fbed6b7621bec~tplv-k3u1fbpfcp-watermark.awebp?)

### 3.2 调试

在 UI 调试上，两者都有对应的工具。效果上来看，RN 更加像 JS 的调试工具一样，上手比较快。

**react-devtools**

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b44a8141fbce4758bc39e53bd691b060~tplv-k3u1fbpfcp-watermark.awebp?)

**Flutter Widget Inspector**

![image.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/ee21c1bb1b494e8db8aece24dd6a7991~tplv-k3u1fbpfcp-watermark.awebp?)

但两个方案都有共同的一个问题，就是当需要 Native 与 RN/Flutter 联调时，比如在两侧都要打断点时，好像没有推荐的做法。

## 4. 维护成本

### 4.1 环境依赖

**Flutter**

* Flutter SDK
* XCode
* Android toolchain

**React Native**

* React Native SDK
* XCode
* Android toolchain
* Node

### 4.2 工程化

**Flutter**

可使用线上代码管理，进行一站式代码提交，打包 Flutter 项目，不过目前还没有国内平台支持。

* [Codemagic](https://link.juejin.cn/?target=https%3A%2F%2Fblog.codemagic.io%2Fgetting-started-with-codemagic%2F)
* [Bitrise](https://link.juejin.cn/?target=https%3A%2F%2Fdevcenter.bitrise.io%2Fgetting-started%2Fgetting-started-with-flutter-apps%2F)
* [Appcircle](https://link.juejin.cn/?target=https%3A%2F%2Fappcircle.io%2Fblog%2Fguide-to-automated-mobile-ci-cd-for-flutter-projects-with-appcircle%2F)

或者通过 [fastlane](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fdeployment%2Fcd) 手动设置 pipeline.

**React Native**

官方没有提供最佳实践，不过因为 JS 在线打包很多平台都已支持，所以只要配置对应的 Native 工程环境即可。

### 4.3 产物

**Flutter**

通过 flutter 可以用[命令行工具](https://link.juejin.cn/?target=https%3A%2F%2Fflutter.dev%2Fdocs%2Fdevelopment%2Fadd-to-app%2Fios%2Fproject-setup%23option-b---embed-frameworks-in-xcode)手动生成最终产物

iOS 生成的是两个 framework

```
flutter build ios-framework
复制代码
```

* App.framework (你的 Dart 代码产物) \~ 100 KB （模板空项目）
* Flutter.framework （依赖的 Flutter 库）\~ 100mb

Android 可以生成 aar 或 apk

```
flutter build apk
复制代码
```

* libapp.so (你的 Dart 代码产物) \~ 3.4 MB （模板空项目）
* libflutter.so (flutter 工程产物) \~ 9 MB

**React Native**

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/b850efc615f74ce98863b55ebc0f51df~tplv-k3u1fbpfcp-watermark.awebp?)

**Metro**

RN 通过 Metro（专为 React Native 设计）打包工具将所有 RN 代码打包成对应的 js.bundle 产物，双端产物大小差不多。

你也可以自己通过命令行生成离线包:

```
react-native bundle --entry-file index.js --bundle-output ./bundle/ios.bundle --platform ios --dev false
复制代码
```

注意：`--dev false` 生成的非 dev 包和 dev 包，大小还是差很多的。

官方提供的一个初始化工程，生成的 bundle 大概是在 750 KB 左右

## 5. 性能

#### 5.1 渲染性能

在大多数浏览器和手机设备上都是 60HZ 刷新频率，也就我们只能在每帧 16ms 的时间内处理完所有事情，包括渲染才能保证显示的平滑。

**React Native**

在渲染效率上，官方其实也提到了，我们的大部分业务逻辑和事件处理都是在 JS 线程上的，因为架构的原因，在 JS 线程处理完数据之后，要扔给 UI 线程进行 Native 原生控件渲染，如果这个时间等于 200ms 就会丢掉 12 帧。而出现卡顿。如果任何情况下超过 100ms 就会被用户所感知。这种情况通常发生在新进一个页面时，要计算所有控件和布局进行渲染。

**Flutter**

其实 Flutter 因为少了原生控件的转化，少了一步桥接上的时间消耗。但要注意的问题仍一样，业务逻辑的处理耗时，和 UI Tree 层级。

```json
"configurations": [
  {
    "name": "Flutter",
    "request": "launch",
    "type": "dart",
    "flutterMode": "profile"
  }
]
复制代码
```

可通过 VS Code 安装 Dart Extension, 然后点击状态栏下方 status bar ，打开 DevTools 查看时时性能。

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/879be7f32c2c4ac3ba0639d71a679ec7~tplv-k3u1fbpfcp-watermark.awebp?)

####

**DevTools**

![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/bb748a1ea45f4ac182dbea69f01cb094~tplv-k3u1fbpfcp-watermark.awebp?)

## 6. 综合比较

|                    | **Flutter**                                                                            | **React-Native**                                                                                  | **备注**                        |
| ------------------ | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------- |
| 背后团队               | Google                                                                                 | Facebook                                                                                          |                               |
| 发布日期               | 2017.5                                                                                 | 2015.3                                                                                            | React-Native 是一个更成熟的框架        |
| 编程语言               | Dart                                                                                   | Javascript                                                                                        |                               |
| 学习曲线               | 低                                                                                      | 低                                                                                                 | 如果你已经了解 JS，将会更快上手 RN.         |
| 热加载                | 是                                                                                      | 是                                                                                                 |                               |
| 热更新                | 否                                                                                      | 是                                                                                                 | RN 可下发 JS 实现。 Flutter 产物已为二进制 |
| 开源                 | 是                                                                                      | 是                                                                                                 |                               |
| 文档完整性              | 是                                                                                      | 是                                                                                                 |                               |
| 编程架构               | State Manager                                                                          | Flux                                                                                              | 都基于状态管理                       |
| 自动化集成发布            | 官方文档                                                                                   | 无可用的官方文档                                                                                          |                               |
| 插件数量               | \~20k                                                                                  | \~30k                                                                                             | 如果算上 React 的话插件就有 200k 左右     |
| 仓库地址               | [Flutter](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fflutter%2Fflutter) | [React Native](https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Ffacebook%2Freact-native) |                               |
| Github Stars/Forks | 132k/19k                                                                               | 99k/21k                                                                                           |                               |
| 产物                 | \~10MB (Android) \~100MB (iOS)                                                         | \~ 70M (Android) \~ 40M (iOS)                                                                     | 模板空工程，多架构产物                   |

**什么时候选择跨平台框架**

* 当你没有太多 UI 动效和复杂的交互界面时
* 如果你已有原生项目，想在部分模块提升开发效率时
* 当你新建一个项目，想快速试错时

**什么时候推荐使用 RN?**

* 已有项目，有较多场景想混合开发时
* 已有前端页面，想尽快移植时
* 有大量前端开发者，Native 人员不足时
* 有真正跨多端场景时, iOS/Native/Web/Desktop

**什么时候推荐使用 Flutter?**

* 全新项目，无太多混合开发的场景
* 现存项目，没有太多 Native 与 Flutter 页面互相嵌套的情况
* 在移动设备上对于渲染性能及 UI 一致性有较高要求时
