ReactNative工作原理

来源:互联网 发布:真空料理机 鱼汤 知乎 编辑:程序博客网 时间:2024/05/16 15:40

《React Native应用开发实例解析》

React的起源

React,这个用于构建用户界面的JavaScript库,就是React Native的核心。为了理解React,先要熟悉几个概念。第一个概念,声明式编程范式(范式就是计算机程序架构与组件的构建风格),用这种范式表达计算逻辑时不需要描述控制流程。简单地说,声明式编程就是你编写代码描述想要做什么,而不是怎么做。

第二个概念,异步,大多数JavaScript开发者已经很熟悉。同步是指“按顺序执行一段代码”,代码语句一行接一行地执行。这意味着每行代码都要等待前一行执行完成。异步代码让代码语句从主程序流程中脱离,主程序代码在异步调用之后立即继续执行,而无需等待异步代码完成。

React具有声明式、异步、响应式的特性,使代码可预测,并让我们更有把握进行快速迭代。

为什么选择React

HTML编写的Web应用中有文档对象模型DOM。DOM通过对象的形式来展现结构化文档。对于Web开发者来说,文档即HTML代码,DOM又称作HTML DOM,HTML的元素在DOM中叫节点。Web浏览器负责处理DOM的具体实现,并提供API接口以便对DOM进行遍历和修改。这样我们就能用JavaScript和CSS与DOM交互,比如查找节点并修改内容、移除节点、插入新节点。无论何时想要动态改变网页内容,只要通过API接口修改DOM即可(如今的DOM API几乎实现了跨平台和跨浏览器的兼容性)。

不过,关键问题在于DOM没有对动态创建UI进行优化。尽管可以用JavaScript和jQuery库操作DOM,但大量的操作就会引发性能问题。那么React是如何解决这个难题的呢?

React的开发者采取了虚拟DOM的做法,虚拟DOM更加轻量,对真实DOM进行了抽象化,而且独立于特定浏览器的具体实现。每当触发需要改变DOM的事件时,React会创建一个新的虚拟DOM树,并将其与已有的树进行对比,计算出最少的DOM变化集合,把它们放入队列再全部批量执行,接着重新渲染视图。这种做法没有把重负荷操作全部加在真实DOM 上,因此比直接操作DOM快了很多。React的这种行为没有采用脏数据检查(dirty checking,持续检测模型变动),而是利用观察者模型进行变动检测,通过差分算法(diffing algorithm,http://calendar.perfplanet.com/2013/diff/)判断最少的DOM操作,因此很有效率。

React为可变命令式DOM接口编程提供了声明式封装。声明式的编程方法只需描述程序应该达成什么目的,而不用关心程序应该怎么运行。然而命令式编程需要逐步编写程序,将输入值计算成期望的输出。

  • 开发简单,声明式编程:只需告诉React,你希望应用长成什么样。按照设计稿编写声明式视图,定义应用的状态。React会根据应用状态,仅更新、渲染对应的组件,非常高效。这让代码的编写和维护变得非常容易,同时更具可预测性,更容易调试。

  • 组件化开发:Facebook宣称,“用了React,你只需要开发组件”。开发一整套组件,再把它们拼装成应用。

现在我们知道了React是什么,接下来简要了解一下这些魔法背后的原理。

React的工作原理

编写React应用的首要任务便是开发组件,组件属于视图部分,同时也定义了应用状态,而数据层内容取决于当前渲染的视图。

  • 将UI拆解成组件:组件驱动开发,是指将代码拆分成组件,理论上组件只负责一件事。有个很简单的技巧,称为单一职责原则(the single responsibility principle),指一个组件理论上只应该做一件事。如果组件需要进一步扩展,就应该将它拆解成更小的子组件。这样可以让代码更容易理解、维护和测试。

  • 交互式UI:要使UI具有交互性,你需要触发UI背后的数据模型的改变。React中通过状态很容易做到这点。每个组件拥有内部状态、逻辑、事件处理器(如点击按钮和改变表单输入),也可以包含行内样式。一旦状态发生了改变,React就重新渲染视图。

需要注意的是,React有两种类型的数据“模型”:属性(props,英文properties的简写)和状态(state)。弄清两者的区别很重要。简而言之,如果组件有时候需要修改自身的某个特性,那么这个特性应该归类于组件状态的一部分,除此以外便是组件的属性。属性可以比作组件的静态数据,而状态是动态的,不过两者都可以触发重新渲染。

现在我们知道了React的工作原理,接着继续了解一下同样的概念如何打造出React Native。

为什么选择React Native

React最神奇的地方在于,它被设计成能对任意的命令式视图系统进行封装,不仅限于DOM。因此Facebook的工程师某天就想到,为什么不用React来封装真正的原生移动UI呢?React Native就这么诞生了!

正如React用虚拟DOM产生的魔法一样,React Native通过原生宿主平台的API也实现了一样的效果。React Native应用借助宿主平台上Objective-C语言(iOS平台)或Java语言(Android平台)的UI库,渲染真正的原生UI组件,不仅限于WebView,这就解释了为何React Native能给应用带来更强的性能、更贴近原生的视觉感受以及使用体验。

React Native允许开发者通过JavaScript函数的代理,直接调用原生模块。

从性能角度上分析,React Native把所有应用代码和业务逻辑从主线程转移到后台线程运行。它可以批量处理要原生执行的请求,等控制权转让给主线程时再异步执行。React Native会分析你的UI,将最少的数据传给主线程(又称UI线程)以便用原生组件进行渲染。

使用React Native,你会得到原生的用户体验以及Web的开发体验。

  • 简单易学:如果你曾经开发过移动端,你可能会惊讶于React Native如此简单易用。

  • 快速迭代:不用等待应用构建,只要按下刷新快捷键,所有改动会立刻在应用中呈现。

  • 智能调试:与React一样,React Native也会在报错时抛出简明扼要的描述信息。

  • 原生模块:React Native的设计目的就在于,让你能够编写真正的原生代码,并在自定义原生模块的帮助下获得平台提供的完整能力。

  • 一次性学习,全平台开发:同一支工程师团队可以为任何平台开发应用,不需要为每个平台学习不同的基础技术。

React Native的工作原理

原生代码与JavaScript代码通过桥接层进行交互,这是一个异步的批量串行处理过程。桥接层介于原生层和JavaScript代码之间,正如它的名称一样,它的作用很像桥(bridge)。用户输入、计时器、网络请求和响应等事件注册在原生代码中。React Native在原生层收集事件产生的数据,串行处理后通过桥接层传给JavaScript层。JavaScript层拿到数据后处理并生成一系列指令。这些指令由整型、字符串等数据类型构成,同样经过批量串行处理后传回原生层。桥接层的原生端决定哪个原生模块负责处理传回的指令并调用相应的方法,同时在需要的情况下更新UI。这种架构提升了React Native应用的性能,并且让React Native应用能以每秒60帧的速率运行。
这里写图片描述

React Native需要通过JavaScript执行环境运行JavaScript代码。在iOS和Android系统的模拟器以及设备上,React Native使用Safari的JavaScript引擎JavaScriptCore(http://trac.webkit.org/wiki/JavaScriptCore)。通过Chrome调试React Native时,JavaScript代码会在Chrome V8引擎内运行,并通过WebSocket与原生代码进行交互。React Native Windows应用的JavaScript运行环境是Chakra(微软Edge浏览器的JavaScript引擎https://github.com/Microsoft/ChakraCore),UWP(通用Windows平台)应用包不需要添加任何额外的二进制文件就可以使用Chakra引擎。

运行React Native应用时发生了什么

下面来看看React Native应用启动时发生了什么。启动应用时有以下三个任务并行完成。

  • 加载JavaScript打包文件,React Native的打包工具会像Webpack和Browserify一样把代码连同全部依赖打包成单个文件。

  • 与此同时,React Native开始加载原生模块。一旦某个原生模块完成加载就在桥接层注册,桥接层确认该模块。此时整个应用便知道该模块已可用并能创建该模块的实例。

  • 启动JavaScript虚拟机,提供JavaScript代码的执行环境。

一旦原生模块和JavaScript执行环境准备就绪,应用就会加载JSON配置文件。配置文件中包含了模块数组、常量导出模块以及模块的方法。这个文件的重要之处在于,当你请求依赖的原生模块并调用方法时,JavaScript会读取它并在执行环境中创建对象。

下一步,执行JavaScript打包文件。创建Shadow视图(负责计算布局的独立线程称为shadow队列)来渲染应用的布局。Shadow队列把flexbox这样的属性转换成绝对位置和大小。与此同时,创建原生视图来渲染应用。最后结合两者将整个应用渲染到屏幕上。

现在来考虑一下应用中触发事件或者发生交互的情况。iOS系统的UIKit会识别触发的事件,并通过原生模块把事件分发给React(传给JavaScript层)。JavaScript捕获事件后会调用对应的事件处理器。如果事件处理器需要再次调用原生组件,将通过桥接层调用原生组件方法。

需要补充的是,iOS系统所有的原生模块有各自的线程池(GCD队列),而Android系统的模块共享同一个线程池,但两者的原生模块线程池都脱离于主线程存在。

以上对React Native工作原理的解释还处于很高的层级,在底层执行过程中实际上还有很多优化和动态的过程。

原创粉丝点击