# 多端融合方案

## SwfitUI

SwiftUI 自亮相以来，全网就在讨论其与 React、Flutter 之间的关系。

首先是与 Flutter 的对比，Flutter 的思路是从 0 开始，即语言、基础库、渲染引擎、排版引擎即框架本身全部由自己实现，其渲染引擎 Skia 只需要操作系统为其提供一个 GL Context 便可以完成所有图形渲染，这使得其跨平台性变得十分强大，到目前为止 Windows、Linux、macOS、Fuchsia 都已经得到了 Flutter 官方的支持。

这种做法我认为有利有弊，首先好处是所有平台下行为一致，不管是滚动视图、Material Design 控件还是模糊效果这些在其他平台没有的都得到了全平台的支持，开发者并不需要为这些去做平台间的适配，反观 React Native… 当然缺点也是存在的，Flutter 这种做法类似于游戏引擎，平台提供的 UI 特性它一概不用，因此 Flutter View 与原生视图的交互就没有那么容易了，同时新的 Dart 语言貌似也不是非常受社区和开发者喜爱。

SwiftUI 没有像 Flutter 那样从头再来，这个全新的框架依旧使用了 UIKit、AppKit 等作为基础。但它并不是一个 UIKit 的声明式封装

许多基础组件，像 Text、Button 等都并不是直接使用 UILabel、UIButton 而是一个名为 DisplayList.ViewUpdater.Platform.CGDrawingView 的 UIView 子类。它们使用了自定义绘制，但又集成于 UIKit 的环境中，因此我猜测 SwiftUI 只提供了组件的自定义渲染和布局引擎，它使用到的底层技术还是 Core Animation、Core Graphics、Core Text 等。使用自定义绘制去实现组件可以理解成为跨平台提供便利，毕竟一个按钮还要区分 UIButton、NSButton 来实现未免有些麻烦。但是部分复杂的控件还是采用了 UIKit 中已有的类，比如 UISwitch 等。由于未脱离 UIKit 体系，嵌入一个 UIView 非常容易，你不需要搞什么外部纹理（Flutter 需要），因为它们的上下文是同一个，坐标系也是同一个。

所以我认为 SwiftUI 更加类似 React Native，使用系统框架提供的组件，只不过绘制和布局可以自己来实现，这在 SwiftUI 之前也有相关的框架这样实践的，比如 Yoga、ComponentKit 等。


SwiftUI 是声明式的 UI 开发方式。关于声明式和命令式的介绍可以查看这篇[博文](https://github.com/FantasticLBP/knowledge-kit/blob/master/第七部分%20设计模式/7.1.md)。声明式开发框架 SwiftUI、React、Flutter、Vue 等都具备下面的特点。

- 使用各自的 DSL 来描述UI 该长什么样子（样式模版），而不是一句句代码描述来告诉系统该如何一步步构建 UI
- 声明所需要的数据部分
- 框架内部通过模版和数据部分，负责渲染绘制
- 数据发送变动
- 框架根据最新的数据和样式模版计算出最新的样式声明
- 最新的样式声明和之前的样式声明比较，计算出差值。系统重新绘制

```Swift
@State var name: String = "Tom"
var body: some View {
    Text("Hello \(name)")
}
```

在 SwiftUI 中， view 是由纯数据结构描述的。因此这些数据的创建和差分计算都不会带来太多的性能开销。

## React && React Native

先谈谈 React 吧。React 的优秀的地方在于：Virtual DOM、JSX、单向数据流等等。但是谈谈 React 这些框架为什么可以做 Web 也可以做跨端解决方案 RN。传统的 Web 开发是基于命令式编程的方式，监听事件、发起请求、操作 DOM、刷新页面。想想看，每次数据变动了都需要刷新 DOM，然后用户就可以在浏览器上看到了最新的数据，DOM 的操作是很耗费资源的。怎么理解这句话，其实 DOM 对象本身就是一个 JS 对象，所以 JS 对于 JS 对象的操作来说性能耗费基本不用考虑，微乎其微。但是 DOM 每次变动到真实 UI 的渲染是非常耗费性能的（触发浏览器的布局和绘制）。至于浏览器的布局重绘原理我会新开文章进行讨论和总结，可以先看看文章底部的参考资料。

在 React 中使用数据（state、props）+ 样式模版（JSX）的方式开发 UI。以下是 React 大致的工作原理

- 设置页面所需要的数据 State、props
- 创建页面的模版样式部分 JSX
- 根据数据和样式模版生成 Virtual DOM
- 页面首次渲染的时候先根据 Virtual DOM 生成真实的 UI
- 数据变动（setState）结合「批更新策略」
- 根据变动后的数据和模版样式生成新的 Virtual DOM
- 根据 Diff 算法计算变动的 Virtual DOM 部分
- 根据变动的 Virtual DOM 部分去绘制 UI

什么是 Virtual DOM? 

Virtual DOM 就是一个 JS 对象，用来描述真实的 DOM。看看下面的例子

```HTML
<ol id='ol-list'>
  <li class='item'>Item 1</li>
  <li class='item'>Item 2</li>
  <li class='item'>Item 3</li>
</ol>
```

转换为 Virtual DOM

```Javascript
var olElement = {
    tagName: 'ol',
    props: {
        id: 'ol-list'
    },
    children: [
        {tagName: 'li', props: {class: 'item'}, children: ['Item 1']},
        {tagName: 'li', props: {class: 'item'}, children: ['Item 2']},
        {tagName: 'li', props: {class: 'item'}, children: ['Item 3']}
    ]
}
```

所以 Virtual DOM 抽象出来后就很方便了，在 Web 端可以去渲染到真实的 DOM；在 Native 端可以去映射到 Native UI 组件上。所以 React 有了 Virtual DOM 便可以在 Web 端和 Native 端大展拳脚。



## 参考资料

- [浏览器的布局绘制与DOM操作](https://blog.csdn.net/sinat_32434539/article/details/77894009)
- [前端必读：浏览器内部工作原理](https://www.cnblogs.com/rainy-shurun/p/5603686.html)
- [深度理解 Virtual DOM](https://www.cnblogs.com/wubaiqing/p/6726429.html)


