浅记录React Native原理

275 阅读4分钟

React Native 是一个由 Facebook 于 2015 年 9 月发布的一款开源的 JavaScript 框架,它可以让开发者使用 JavaScript 和 React 来开发跨平台的移动应用。

跨端技术

  • 一次编写,多端运行
  • 节省人力,加快迭代速度,支撑业务高速发展

WEB开发是最常见的跨端技术

  • 优点

    • 无需发版(开发完上线就可以看到效果)
    • 节省人力(不需要为多个端投入不同的人力)
    • 迭代速度快(前端写html和css开发比客户端开发快很多)
  • 缺点

    • 体验及性能比原生应用差
    • 优化难度大(通常涉及到浏览器内核的修改,并且能得到的提升非常有限)

web开发与原生开发对比

  • web: 采用动态语言开发,生成网页视图
  • 原生:采用静态语言开发,生成原生视图

能不能结合WEB开发和原生开发的优点,用动态语言,生成原生视图?🧐

核心原理

🤩 如何通过JS生成原生视图?

Virtual DOM

  • 描述用户界面的js对象
  • 具备平台无关性

基于Virtual DOM和渲染器的分离,React在Render层将具体平台对应的渲染逻辑封装起来,来处理和平台相关的视图渲染。

截屏2022-06-20 下午11.51.47.png 将不同的基础库放在Render里面!!我们只需要关心怎么写virtual dom就可以了~

浏览器端如何创建视图

  1. 如何表示一个React元素

两种方法将react 元素传递给React DOM的render方法, 渲染到浏览器指定的html元素里面

//case 1
class App extends React.Component {
}
ReactDOM.render(<App />, document.getElementById("container"))
//case 2
ReactDOM.render(<h1>Hello world</h1>, document.getElementById("container"))

react中用一个对象表示元素

var element = {
    //This tag allow us to uniquely identify this as a React Element
    $typeof: REACT_ELEMENT_TYPE,
    
    //built-in properties that belong on the element
    type: type, //这个元素的类型
    key: key, //这个元素的标识
    ref: ref, //这个元素的引用
    props: props, //元素的一些属性 (里面children表示这个元素的子元素)
    
    //Record the component responsible for creating this element
    _owner: owner
}

转化后的样子如下

//Case 1
<h1><p>Hello world</p></h1>
{
    type: 'h1',
    props:{
        children: {
            type: 'p',
            props: ['Hello world']
        }
    }
}

//Case 2
<App/>
{
    type: App,//Component
}
  1. React Element 归类

  • 原子类型(对应case1)

    • type为字符串
    • 粒度最小,结构上不可再分解
    • 渲染:由平台底层提供支持
    • 浏览器中为基础的html标签,Native端为基础UI组件
  • 组合类型(对应case2)

    • type为函数构造器(Virtual DOM生成器),提供自定义元素UI和行为的能力
    • 渲染先使用类型构造器创建一个实例并运行render方法得到一个新的元素,再进行渲染
  1. 渲染器工作

对于不同的类型,渲染器底层会提供不同的类

  • instantiateComponent: 根据传入对象的type创建不同的渲染器实例,比如,如果是一个文本,对应浏览器环境中的ReactDOMTextComponent
  1. 示例

渲染以下的react元素

class App extends React.component {
    render() {
        return (   
            <div>
                <h1>Welcome Page</h1>
                <Welcome name="Yaphet" />
            </div>
        )
    }
}

class Welcome extends React.Component {
    render(){
        return (
            <h2>{this.props.name}</h2>
        )
    }
}

ReactDOM.render(<App/>, document.getElementById("container"))

  1. 把给ReactDOM的render方法
  1. 将对应的渲染器实例化出来(ReactCompositeComponent)得到App节点(最上方)
  1. App节点根据元素的type(组合类型type为函数构造器)创建实例并运行render方法,得到了里面的第一个元素
  1. 拿到div之后创建相应的渲染器(ReactDOMComponent)并渲染这个原子类型的元素成为一个真实的dom节点
  1. 拿到里面的元素之后,分别实例化渲染器,再渲染出DOM元素

。。。。。。

依次按树🌲形向下。。。。。。

😍总结:在浏览器端,通过DOM API 完成UI的创建

浏览器端,js可以通过调用DOM API完成UI的创建,那js在Native端如何完成UI的创建呢?🧐

Native端如何创建视图

JavaScript 引擎

  1. 创建JavaScript上下文
JSContext *context = [[JSContext alloc] init]
  1. 在上下文中执行代码
[context evaluateScript:@"var triple = function(value) { return value + 3 }"];
  1. Native调用JavaScript
JSValue *tripleFn = context[@"triple"]

JavaScript把函数或变量暴露在全局,Native端就可以取到,同理,JS要用Native的东西,Native也必须把这个函数和变量暴露在全局

React Native实现JS Native互相调用

但在reactive native中 ,为了避免模块越来越多,会污染全局上下文

React Native只暴露了一个对象_fbBatchedBridge, 里面有一个方法callFunctionReturnFlushedQueue, 当Native调用js模块的时候,Native可以通过这个全局对象的方法,将想要调用模块的名称和方法的名称以及参数传进去,通过这个方法调用具体的模块

相反,Js调用native模块的时候,也通过一个中间方法调用相应的模块

ReactNative 中 js创建Native UI

调用UI Manager模块createView方法,传入相应的参数,创建客户端的视图

💡样式在ReactNative里面是通过对象表示的,将key/value传给Native端,让Native端解析这个对象,来渲染视图

😍总结:首先通过js引擎,调到Native端的代码,在Native端,UI manager创建对应视图