mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 04:17:17 +00:00
docs: clang 插件开发
This commit is contained in:
@@ -599,14 +599,115 @@ NSData *businessData = [NSData dataWithContentsOfFile:businessBundle options:NSD
|
||||
|
||||
|
||||
|
||||
## 学习方面
|
||||
## Fabric 新渲染器
|
||||
|
||||
学过 React.js 之后你再去学习 React Native 会很简单,一些核心的东西理解之后会很简单。比如 React 中的单向数据流、虚拟 Dom、diff 算法、数据变动的批量更新机制、diff 之后的 UI 渲染。
|
||||
React Native 开源之初的宏大愿景是:将现代 Web 技术引入移动端(Brighing modern web techniques to mobile)
|
||||
想来也是,Web 开发历史悠久,沉淀了很多优秀的 UI 开发实践和基础设置。随着 React Web 的出现,将现代的 Web 中积累的开发理念、语言、框架、规范也带入到移动端,
|
||||
但也有难点。如何打通 Web 和移动端
|
||||
|
||||
换言之,问题就是语言之间相互调用,也就是通信。本质上,JavaScript 如何与 OC/Java 通信。彼此可以互相调用方法、访问属性。
|
||||
|
||||
### 老架构渲染器
|
||||
|
||||
- 老渲染器的主要职责之一,就是将 JavaScript 侧声明的组件转换为 iOS/Android 侧的 Api 命令。
|
||||
```
|
||||
const App = () => <Text>Hello world</Text>
|
||||
AppRegistry.registerComponent(appName, () => App)
|
||||
```
|
||||
当声明一个包含 `<Text>Hello world</Text>` App 组件,并将该 App 组件传递给 `registerComponent` 方法后,通过渲染器,会将声明式的代码转换为原生指令。
|
||||
以上 Hello World 应用中会包括一个用于布局的 View 视图和显示文本的视图。在 iOS 端,会生成一个 UIView 用于布局,并会创建 NSAttributedString 用于显示文本。在 Objective-C 中调用相关以上创建视图的 API 后,操作系统就会将 Hello World 文字显示在屏幕上了。
|
||||
|
||||
- 老渲染器的另一个重要职责是实现 Flex 布局。
|
||||
开源的第一版 Flex 布局是直接用原生代码实现的,后来该功能独立了出来,作为一个 C++ 第三方库 Yoga 被 React Native 引入。
|
||||
假设想实现文字居中
|
||||
```
|
||||
<view style={{flex:1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Text>Hello world</Text>
|
||||
</view>
|
||||
```
|
||||
渲染器会将 style 属性设置,转换为包裹 Hello World 视图容器的 x/y 轴坐标使其实现屏幕居中。
|
||||
|
||||
- 老版渲染器还有一个职责就是尽可能的提升渲染性能。
|
||||
RN 在第一版的时候就设置为双线程异步消息通信架构,后来 RN 团队又为 Yoga 布局引擎,新增了一个线程,专门用于处理布局。
|
||||
相较于单线程同步调用的架构,多线程异步消息架构更能大幅减少卡顿。一方面,渲染任务被分解到3个线程中(JS 线程、布局线程、UI 线程),所以 UI 线程的任务量会变少,UI 线程卡顿的几率也会减少。另一方面,采用异步通信,JS 线程任务的执行不会阻塞 UI 线程。
|
||||
|
||||
|
||||
样式布局方面增加了 flexbox,这样子布局在移动端会非常方便,非常简单,
|
||||
### Fabric 新渲染器
|
||||
Fabric 新渲染器是基于老渲染器的重构升级,而重构升级过程中不变的是核心责任,是组件化 / 声明式、Flex 布局和多线程模型。升级的是开发者体验,以及性能提升带来的用户体验。
|
||||
Fabric 渲染器完成的主要任务还是将声明的组件转换为最终原生 Api 调用。转换过程涉及到3颗树:
|
||||
- Element Tree
|
||||
- Fiber Tree
|
||||
- Shadow Tree
|
||||
|
||||
#### Element Tree
|
||||
Element Tree 是 Javascript 侧,由 React 通过开发者书写的 JSX 创建而成,由若干个 Element 组成。
|
||||
一般而言,根结点 `<App />` 就是一个 Element,同时它也是 Element Tree。一个 Element 其实就是一个普通的 Object,该对象描述组件的实例或宿主视图实例。
|
||||
```
|
||||
const App = () => {
|
||||
return (
|
||||
<View style={{opacity: 0.99, flex:1, justifyContent: 'center', alignItems: 'center'}}>
|
||||
<Text style={{opacity: 0.88}}>Hello World</Text>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
// Element Tree
|
||||
<App/>
|
||||
```
|
||||
整个应用的根节点是 `<App />`,`<App />` 的子节点是 `<View />`,`<View />` 子节点是 `<Text />`,共同构成了一棵 Element Tree。
|
||||
Element Tree 的每个节点都是一个 Element,React Element 有2种类型:一种是通过函数或者自定义合成组件生成的、一种是宿主组件生成的。其中,宿主组件指的是框架通过 JS 引擎暴露给 JS 的原生组件(Native 组件)
|
||||
|
||||
`<App />` 根节点是自定义函数创建的,属于合成组件生成的节点,由 type、props、concurrentRoot 等属性组成的对象,type 属性是一个 function 函数,函数名 name 是 App。打印信息如下
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNative-ElementTreeDemo1.jpeg" style="zoom:40%">
|
||||
|
||||
`<Text />` 节点是由框架暴露组件生成的节点,信息如下
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNative-ElementTreeDemo2.webp" style="zoom:40%">
|
||||
|
||||
可知,一个 Element 也是一个普通的对象,该对象的 type 属性为 RCTText,style 属性值由设置透明属性 `opacity:0.9` 和设置居中布局的属性组成,子节点 children 属性值为 `Hello world`
|
||||
|
||||
从 Hello World 应用中的 <App> <Text> 节点的构成,我们可以看出,一个 Element 常见的属性包括 type 、props、concurrentRoot、style、children 等属性。
|
||||
- type:type 代表该 Element 的类型。如果 type 的值是 RCTText、RCTView 之类的字符串,那么该 Element 对应着一个宿主视图。如果 type 的值是函数或类,那么该 Element 是由合成组件生成的,并且没有对应的宿主视图。
|
||||
- props:Element 初始化传入的属性,其中又包括当前根节点 concurrentRoot、样式 style、子节点 children,或者例如 Text 组件的 ellipsizeMode 文本省略属性等等。
|
||||
|
||||
在 React 层, Element Tree 会被映射为 Fiber Tree。
|
||||
|
||||
#### Fiber Tree
|
||||
Fiber Tree 由若干个 FIber 节点组成。如果某个 Fiber 节点是通过用于描述宿主视图的 Element 生成的,那么该 Fiber 节点会对应一个同样的宿主视图。
|
||||
|
||||
Fiber 是 React16 之后引入的新能力,使得 React 每次可渲染的颗粒度变小了。由 React 16 之前的一次 render 所有节点,变成了一次 render 时可分批次对节点进行操作。因此,从渲染角度,我们还可以将 Fiber 节点当作每次 render 的最小渲染单位,让 Fabric 渲染器更快更智能。
|
||||
|
||||
在 React 内部,Fiber 节点是由 `function createFiberFromElement(element, mode, lanes)` 函数创建的。顾名思义,Fiber 节点是由 Element 节点生成的,换言之 Fiber Tree 可以看成是 Element Tree 的映射。
|
||||
|
||||
同样 Fiber Tree 分为2种,一种是由合成组件生成的 Element 所映射得到的 Fiber 节点,它没有对应的宿主组件实例;一种是由宿主组件生成的 Element 所映射得到的 Fiber 节点,它拥有对应的宿主组件实例。
|
||||
|
||||
以 Hello world 威力,打印 App 组件所创建的 Fiber 节点
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNativeFiberNodeDemo.webp" style="zoom:40%">
|
||||
|
||||
可以看到 App Fiber 也是一个 JS 对象,也拥有 type、child、props 等属性,这些属性在 Element 上也有。例如 App 组件创建的 Element、Fiber 的 Type 都是一个名为 App 的函数。Fiber 节点上的属性比 Element 多,比如 Fiber 节点拥有兄弟节点 sibling、父节点 return、状态节点 stateNode 等属性。
|
||||
|
||||
状态节点是一个特殊的属性,关联了渲染器在 C++ 层生成的 Shadow 节点。App Fiber 节点的 stateNode 为 null,代表的就是合成组件所对应的 Fiber 节点是没有关联的 Shadow 节点的,也就没有对应的宿主视图。
|
||||
|
||||
打印 Text 组件所创建的 Fiber 节点,如下:
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNativeFiberNodeDemo2.webp" style="zoom:40%">
|
||||
Text Fiber 节点和 App Fiber 节点属性都一样,比如:类型 type、子节点 child、兄弟节点 sibling、父节点 return、状态节点 stateNode 等。不同的是 Text Fiber 节点的 type 是 RCTText,App Fiber 的 type 是个函数。Text Fiber 的 stateNode 是有值的,node 属性值是一个 CallbackObject,而 App Fiber 的 node 是 null。该 CallbackObject 类型的值代表的是一个在 C++ 层的 shadow 节点。
|
||||
|
||||
|
||||
#### Shadow Tree
|
||||
Shadow Tree 是 C++ 层创建的树,由若干个 Shadow 节点组成,这些 Shadow 节点是在创建对应的拥有 stateNode 值的 Fiber 节点时,同步创建的。
|
||||
```
|
||||
<Text style={{opacity: 0.88}}>Hello world</Text>
|
||||
```
|
||||
Xcode 打印如下:
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNativeShadowNode.webp" style="zoom:40%">
|
||||
<Text>元素对应的 Shadow 节点是个 Native 对象,拥有属性 props、子节点 children、布局 layoutMetrics 等属性。
|
||||
|
||||
其中,Shadow 的 props 的透明度 opacity: 0.88 来自于 JSX 中的 style={{opacity: 0.88}} 的设置;子节点 children 的 text="Hello World" 来自于 JSX 标签括起来的内容 Hello World。而 x/y 轴坐标以及 width/height 视图大小是根据其自身 style 布局属性,以及父节点和其他节点 style 布局属性计算出来的。
|
||||
|
||||
Shadow Tree 不仅继承了由 JSX 所创建的 Element Tree 相关属性、父子节点关系,还新增了该视图如何在屏幕上进行布局的具体值。
|
||||
|
||||
最后 Fabric 渲染器的 C++ 层,通过 diff 算法对比更新前后的2颗 Shadow Tree,计算出更新视图的操作指令,完成最终的渲染。整个流程就是 Fabric 渲染器将 JSX 渲染成原生视图的完整流程。
|
||||
|
||||
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ReactNative-RenderPhase.png" style="zoom:40%">
|
||||
可以访问完整的[渲染、提交与挂载流程](https://reactnative.cn/architecture/render-pipeline)
|
||||
|
||||
## 经验小结
|
||||
|
||||
|
||||
Reference in New Issue
Block a user