React Native 分析(一)基本原理概述

React 的理念
React 的主要思想是通过构建可复用组件来构建用户界面。所谓组件,其实就是有限状态机(FSM),通过状态渲染对应的界面,且每个组件都有自己的生命周期,它规定了组件的状态和方法需要在哪个阶段改变和执行。
简单的来说,如下图:


image.png

比较装逼的来说,有限状态机,表示有限个状态以及在这些状态之间的转移和动作等行为的模型。一般通过状态、事件、转换和动作来描述有限状态机。下图 是描述组合锁状态机的模型图,包括 5 个状态、5 个状态自转换、6 个状态间转换和 1 个复位 RESET 转换到状态 s1。状态机能够记住目前所处的状态,可以根据当前的状态做出相应的决策,并且可以在进入不同的状态时做不同的操作。状态机将复杂的关系简单化,利用这种自然而直观的方式可以让代码更容易理解。

image.png

React 正是利用这一概念,通过管理状态来实现对组件的管理。例如,某个组件有显示和隐藏两个状态,通常会设计两个方法 show() 和 hide() 来实现切换,而 React 只需要设置状态 setState({ showed: true/false }) 即可实现。同时,React 还引入了组件的生命周期这个概念。通过它,就可以实现组件的状态机控制,从而达到“生命周期→状态→组件”的和谐画面。

虽然组件、状态机、生命周期这三者都不是 React 独创的,但 Web Components 标准与其中的自定义组件的生命周期的概念相似。就目前而言,React 是将这几种概念结合得相对清晰、流畅的 View 实现。

UI = ƒ(count) =
 div(
 span('Count ' + count),
 button('Add +1')
 ) 

在 React 中,数据是自顶向下单向流动的,即从父组件到子组件。这条原则让组件之间的关系变得简单且可预测。
state 与 props 是 React 组件中最重要的概念。如果顶层组件初始化 props,那么 React 会向下遍历整棵组件树,重新尝试渲染所有相关的子组件。而 state 只关心每个组件自己内部的状态,这些状态只能在组件内改变。把组件看成一个函数,那么它接受了 props 作为参数,内部由 state 作为函数的内部参数,返回一个 Virtual DOM 的实现。

在使用 React 之前,常见的 MVC 框架也非常容易实现交互界面的状态管理,比如 Backbone。它们将 View 中与界面交互的状态解耦,一般将状态放在 Model 中管理。但在 React 没有结合 Flux 或 Redux 框架前,它自身也同样可以管理组件的内部状态。在 React 中,把这类状态统一称为 state。

当组件内部使用库内置的 setState 方法时,最大的表现行为就是该组件会尝试重新渲染。这很好理解,因为我们改变了内部状态,组件需要更新了。

render(){   
    return (
        <div>
            <span>
                Count:<b>{this.state.count}</b>
            </span>
            <button onClick={() => ???}>
                Add +1
            </button>
        </div>
    )
}

所以??? 就是 this.setState({count:this.state.count + 1})

数据更新过程
因为View的展示和View的事件响应分属于不同的端,展示部分的描述在JS端,响应事件的监听和描述都在Native端,通过Native转发给JS端。Native开发里,什么时候会执行代码?只在有事件触发的时候,这个事件可以是启动事件,触摸事件,timer事件,系统事件,回调事件。而在React Native里,这些事件发生时OC都会调用JS相应的模块方法去处理,处理完这些事件后再执行JS想让OC执行的方法,而没有事件发生的时候,是不会执行任何代码的,这跟Native开发里事件响应机制是一致的。 会在下一节具体说明

就拿一个简单的点击按钮,更新 text 计数来说,点击以后, Native会分发如下事件:

[_bridge enqueueJSCall:@"EventEmitter.receiveTouches" args:@[
  @"end",
  @{@"x": @42, @"y": @106}]];

经过 bridge转换后,就变成

call('EventEmitter', 'receiveTouches', [{x: 42, y: 106}])

如果组件自身的 state 更新了,那么会依次执行 shouldComponentUpdate、componentWillUpdate、render 和 componentDidUpdate。

class App extends Component {
  componentWillReceiveProps(nextProps) {
    // this.setState({})
  }

  shouldComponentUpdate(nextProps, nextState) {
    // return true;
  }

  componentWillUpdate(nextProps, nextState) {
    // ...
  }

  componentDidUpdate(prevProps, prevState) {
    // ...
  }
}

shouldComponentUpdate 是一个特别的方法,它接收需要更新的 props 和 state,让开发者增加必要的条件判断,让其在需要时更新,不需要时不更新。因此,当方法返回 false 的时候,组件不再向下执行生命周期方法。
shouldComponentUpdate 的本质是用来进行正确的组件渲染。怎么理解呢?我们需要先从初始化组件的过程开始说起,假设有如图所示的组件关系,它呈三级的树状结构,其中空心圆表示已经渲染的节点。

image.png

当父节点 props 改变的时候,在理想情况下,只需渲染在一条链路上有相关 props 改变的节点即可

image.png

而默认情况下,React 会渲染所有的节点,因为 shouldComponentUpdate 默认返回 true。正确的组件渲染从另一个意义上说,也是性能优化的手段之一。

回到 RN 来说,RN框架会根据传递进来的信息,计算出应该哪个节点响应事件,并把该组件的 ID 作为参数传入。如果shouldComponentUpdate返回 true需要渲染,则让 Native 进行更新渲染。

var UIManager = require('NativeModules').UIManager;
UIManager.update(18, {text: '43'});

通过MessageQueue发送相应的数据给 Native 处理

NativeModules.UIManager = {
    ...
    update: function(viewID, attributes) {
        MessageQueue.push(
            ['UIManager', 'update', [viewID, attributes]]
        );
    }
    ...
};

转换成的 Native 代码,markAsDirty标记此控件需要更新,等待 VSync 事件更新

[UIManager updateView:18 props:@{@"text": @"43"}]
 addUIBlock:^() {
   UILabel *label = viewRegistry[18];
   label.text = @"43";
   [label markAsDirty];
}

简要的流程图

image.png

说了这么多,其实最重要的概念,还是

UI = ƒ(data)

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容