我们知道React Native之所以能在移动设备上运行起来,是因为React Native和原生设备之间有一种交互,以iOS为例,React Native 能够运行起来,全靠 objective-c 和 JavaScript 的交互。对于页面的渲染来说,React的渲染都是以组件为单位,那么这个React组件到底是怎么用原生组件渲染的呢?
React组件的产生过程与生命周期
ReactElement
用于描述虚拟节点,它们可以通过 React.createElement 方法或 jsx 写法被创建。
ReactElement分为 DOM Element 和 Component Elements 两类,前者是dom组件后者是自定义组件如
class MyText extends React.Component {
render() {
...
}
}
初始化过程
上一张图
1.通过render()方法,将element的虚拟根节点渲染到container中
2.接下来根据element的类型分成三种情况,String还是ReactElement中的DOM Element或Component Elements ,依次实例化ReactDOMTextComponent , ReactDOMComponent , ReactCompositeComponent 类
3.ReactDOMTextComponent , ReactDOMComponent , ReactCompositeComponent 用于管理 ReactElement ,负责将不同的 ReactElement 转化成DOM,并更新DOM
生命周期
当组件按上文方式初始化后,就有了生命周期
生命周期中的每个阶段都有相应的回调函数,用于控制和操作组件。
初始化阶段
图中最上面的框
getDefaultProps和 getInitialState
用于初始化组件的属性和状态
componentWillMount
在第一次绘制 render() 之前触发,在整个生命周期中只被触发一次。
componentDidMount
在第一次绘制 render()之后触发,在整个生命周期中只被触发一次。这个函数之后,就进入了稳定运行状态,等待事件触发。
运行阶段
componentWillReceiveProps
组件收到新的属性(props)时触发,输入参数 nextProps 是即将被设置的属性,旧的属性还是可以通过 this.props 来获取。常在此处通过调用 this.setState() 来更新的组件状态。
shouldComponentUpdate
组件接收到新的属性和状态改变的话,都会触发,输入参数 nextProps 和上面的 componentWillReceiveProps 函数一样, nextState 表示组件即将更新的状态值。这个函数的返回值决定是否需要更新组件,如果 true 表示需要更新,继续走后面的更新流程。否者,则不更新。
componentWillUpdate
更新渲染前调用,输入参数与 shouldComponentUpdate 一样,但不要在此更新状态
componentDidUpdate
更新渲染后调用,
卸载阶段
componentWillUnmount
组件要被从界面上移除的时候,就会触发。一般在次进行一些清理计时器之类的结尾工作
怎样用原生组件渲染的
既然React Native能在移动设备上运行,那么上述的react组件是怎么用原生组件渲染的呢?
react-native应用的启动过程
1.Native的初始化
以ios为例,包括
读取 JavaScript 源码
初始化模块信息
初始化 JavaScript 代码的执行器,即 RCTJSCExecutor 对象
生成模块列表并写入 JavaScript 端
执行 JavaScript 源码
这五个阶段,这里不是本文的描述点,所以略过。这个地方和react-native的通讯机制打算在之后深入探讨学习。
2.Native端执行初始化React上下文
调用JS端AppRegistry.runApplication(key,params),key为模块/组件名称.
3.JS端找到注册的对应启动组件,执行renderApplication渲染整个应用
renderApplication会执行如下代码
ReactNative.render(
,
rootTag
);
其中AppContainer是一个JS组件,使用View包裹了根组件,开发时工具Inspector、YellowBox都是在这个组件中加载,RootComponent是传入的根组件。
4.找到js端注册的组件
js通过AppRegistry注册组件
AppRegistry.registerComponent('MyApp', rootComponent);
5.Native端渲染组件
以ios为例
self.rctRootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"MyApp"
initialProperties:nil
launchOptions:nil];
位于 AppDelegate 文件中,用户能看到的一切内容都来源于这个 RootView ,所有的初始化工作也都在这个方法内完成。
原生组件渲染
应用启动的最后阶段就是JS端开始渲染根组件,那么其它react组件怎么用原生组件渲染的呢?我们先看render()方法做了什么
render() {
return (
)
}
将jsx翻译一下就是
render() {
return (
React.createElement(Image, { source: Styles.picUrl })
)
}
回想一下上文中ReactElement的描述
ReactElement.createElement = function (type, config, children){ ... }
在react-native下ReactNative的UI组件通过requireNativeComponent -> createReactNativeComponentClass -> ReactNativeBaseComponent下mountComponent的调用关系,最终在mountComponent中调用UIManager组件创建View
UIManager.createView(tag, this.viewConfig.uiViewClassName, nativeTopRootTag, updatePayload);
UIManager是一个NativeModule,JS端可访问。可通过createView,
updateView方法来创建和更新View。
之后原生组件的实现方法,首先是Image组件JS端代码,需要requireNativeComponent加载原生组件以ios为例
const RCTImageView = requireNativeComponent('RCTImageView', Image);
返回
这就创建出原生组件了,之后的事情就是js端的逻辑对原生组件进行控制,这就是react-native的通讯机制的问题了。