ReactNative Day1
教程学习进度:
大前端
gitpod.io
gitpod.io DashBoard
material Development
learn react online
基础笔记
引用自:
🤯掘金
React 是一套可以用简洁的语法高效绘制 DOM 的框架
对前端的理解是这样的:
- 用 HTML 创建 DOM,构建整个网页的布局、结构
- 用 CSS 控制 DOM 的样式,比如字体、字号、颜色、居中等
- 用 JavaScript 接受用户事件,动态的操控 DOM
在 HTML 和 JavaScript 代码中,id 和 onclick 事件触发的函数必须完全对应,否则就无法正确的响应事件。如果想知道一个 HTML 标签会如何被响应,我们还得跑去 JavaScript 代码中查找,这种原始的配置发方法, 随着 FaceBook 推出了 React 框架,这个问题得到了大幅度改善。我们可以把一组相关的 HTML 标签,也就是 app 内的 UI 控件,封装进一个组件(Component)中。
import React from 'react';
import { Text, View } from 'react-native';
const YourApp = () => {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
<Text>
Hello world 🎉
</Text>
</View>
);
}
export default YourApp;
如果你想问:“为什么 JavaScript 代码里面出现了 HTML 的语法”,那么恭喜你已经初步体会到 React 的奥妙了。这种语法被称为 JSX,它是一种 JavaScript 语法拓展。JSX 允许我们写 HTML 标签或 React 标签,它们终将被转换成原生的 JavaScript 并创建 DOM。
在 React 框架中,除了可以用 JavaScript 写 HTML 以外,我们甚至可以写 CSS,这在后面的例子中可以看到。
上文已经解释过了何谓“简洁的语法”,因为我们可以暂时放下 HTML 和 CSS,只关心如何用 JavaScript 构造页面。
所谓的“高效”,是因为 React 独创了 Virtual DOM 机制。Virtual DOM 是一个存在于内存中的 JavaScript 对象,它与 DOM 是一一对应的关系,也就是说只要有 Virtual DOM,我们就能渲染出 DOM。
当界面发生变化时,得益于高效的 DOM Diff 算法,我们能够知道 Virtual DOM 的变化,从而高效的改动 DOM,避免了重新绘制 DOM。
MVC模式
MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。
- Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。
- View(视图) - 视图代表模型包含的数据的可视化。
- Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。
React 并不是前端开发的全部。从之前的描述也能看出,它专注于 UI 部分,对应到 MVC 结构中就是 View 层。要想实现完整的 MVC 架构,还需要 Model 和 Controller 的结构。在前端开发时,我们可以采用 Flux 和 Redux 架构,它们并非框架(Library),而是和 MVC 一样都是一种架构设计(Architecture)。
React Native
一个基于 JavaScript,具备动态配置能力,面向前端开发者的移动端开发框架
这是一个面向前端开发者的框架。它的宗旨是让前端开发者像用 React 写网页那样,用 React Native 写移动端应用。这就是为什么 React Native 自称:
Learn once,Write anywhere!
原理概述
React Native是需要UIKit框架的,调用的是 Objective-C 代码。总之,JavaScript 只是辅助,它只是提供了配置信息和逻辑的处理结果。React Native 与 Hybrid 完全没有关系,它只不过是以 JavaScript 的形式告诉 Objective-C 该执行什么代码。
其次,React Native 能够运行起来,全靠 Objective-C 和 JavaScript 的交互。对于没有接触过 JavaScript 的人来说,非常有必要理解 JavaScript 代码如何被执行。
我们知道 C 系列的语言,经过编译,链接等操作后,会得到一个二进制格式的可执行文,所谓的运行程序,其实是运行这个二进制程序。
而 JavaScript 是一种脚本语言,它不会经过编译、链接等操作,而是在运行时才动态的进行词法、语法分析,生成抽象语法树(AST)和字节码,然后由解释器负责执行或者使用 JIT 将字节码转化为机器码再执行。整个流程由 JavaScript 引擎负责完成。
获得授权,非商业转载请注明出处。
苹果提供了一个叫做 JavaScript Core 的框架,这是一个 JavaScript 引擎。通过下面这段代码可以简单的感受一下 Objective-C 如何调用 JavaScript 代码:
JSContext *context = [[JSContext alloc] init];
JSValue *jsVal = [context evaluateScript:@"21+7"];
int iVal = [jsVal toInt32];
这里的 JSContext 指的是 JavaScript 代码的运行环境,通过 evaluateScript 即可执行 JavaScript 代码并获取返回结果。
JavaScript 是一种单线程的语言,它不具备自运行的能力,因此总是被动调用。很多介绍 React Native 的文章都会提到 “JavaScript 线程” 的概念,实际上,它表示的是 Objective-C 创建了一个单独的线程,这个线程只用于执行 JavaScript 代码,而且 JavaScript 代码只会在这个线程中执行。
Objective-C与JavaScript交互
调用
由于 JavaScript Core 是一个面向 Objective-C 的框架,在 Objective-C 这一端,我们对 JavaScript 上下文知根知底,可以很容易的获取到对象,方法等各种信息,当然也包括调用 JavaScript 函数。
真正复杂的问题在于,JavaScript 不知道 Objective-C 有哪些方法可以调用。
React Native 解决这个问题的方案是在 Objective-C 和 JavaScript 两端都保存了一份配置表,里面标记了所有 Objective-C 暴露给 JavaScript 的模块和方法。这样,无论是哪一方调用另一方的方法,实际上传递的数据只有 ModuleId、MethodId 和 Arguments 这三个元素,它们分别表示类、方法和方法参数,当 Objective-C 接收到这三个值后,就可以通过 runtime 唯一确定要调用的是哪个函数,然后调用这个函数。
再次重申,上述解决方案只是一个抽象概念,可能与实际的解决方案有微小差异,比如实际上 Objective-C 这一端,并没有直接保存这个模块配置表。具体实现将在下一节中随着源码一起分析。
闭包与回调
既然说到函数互调,那么就不得不提到回调了。对于 Objective-C 来说,执行完 JavaScript 代码再执行 Objective-C 回调毫无难度,难点依然在于 JavaScript 代码调用 Objective-C 之后,如何在 Objective-C 的代码中,回调执行 JavaScript 代码。
目前 React Native 的做法是:在 JavaScript 调用 Objective-C 代码时,注册要回调的 Block,并且把 BlockId 作为参数发送给 Objective-C,Objective-C 收到参数时会创建 Block,调用完 Objective-C 函数后就会执行这个刚刚创建的 Block。
Objective-C 会向 Block 中传入参数和 BlockId,然后在 Block 内部调用 JavaScript 的方法,随后 JavaScript 查找到当时注册的 Block 并执行。
、
源码分析
要想深入理解 React Native 的工作原理,有两个部分有必要阅读一下,分别是初始化阶段和方法调用阶段。(有所删改)
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:@"PropertyFinder" initialProperties:nil
launchOptions:launchOptions];
用户能看到的一切内容都来源于这个 RootView,所有的初始化工作也都在这个方法内完成。在这个方法内部,在创建 RootView 之前,React Native 实际上先创建了一个 Bridge 对象。它是 Objective-C 与 JavaScript 交互的桥梁,后续的方法交互完全依赖于它,而整个初始化过程的最终目的其实也就是创建这个桥梁对象。
初始化方法的核心是 setUp 方法,而 setUp 方法的主要任务则是创建 BatchedBridge。BatchedBridge 的作用是批量读取 JavaScript 对 Objective-C 的方法调用,同时它内部持有一个 JavaScriptExecutor,顾名思义,这个对象用来执行 JavaScript 代码。
创建 BatchedBridge 的关键是 start 方法,它可以分为五个步骤:
-
读取 JavaScript 源码
-
初始化模块信息
-
初始化 JavaScript 代码的执行器,即 RCTJSCExecutor 对象
-
生成模块列表并写入 JavaScript 端
-
执行 JavaScript 源码
我们逐个分析每一步完成的操作:
1、读取 JavaScript 源码
这一部分的具体代码实现没有太大的讨论意义。我们只要明白,JavaScript 的代码是在 Objective-C 提供的环境下运行的,所以第一步就是把 JavaScript 加载进内存中,对于一个空的项目来说,所有的 JavaScript 代码大约占用 1.5 Mb 的内存空间。
需要说明的是,在这一步中,JSX 代码已经被转化成原生的 JavaScript 代码。
2、初始化模块信息
aaaaaa,这里太复杂了,之后来看!
这里-》搜索下一步操作是遍历这个数
这一步在方法 initModulesWithDispatchGroup: 中实现,主要任务是找到所有需要暴露给 JavaScript 的类。每一个需要暴露给 JavaScript 的类(也成为 Module,以下不作区分)都会标记一个宏:RCT_EXPORT_MODULE,这个宏的具体实现并不复杂:
#define RCT_EXPORT_MODULE(js_name) RCT_EXTERN
void RCTRegisterModule(Class);
(NSString *)moduleName { return @#js_name; } (void)load { RCTRegisterModule(self); }
blablabla
React Native 优缺点:
优点:
-
复用了 React 的思想,有利于前端开发者涉足移动端。
-
能够利用 JavaScript 动态更新的特性,快速迭代。
-
相比于原生平台,开发速度更快,相比于 Hybrid 框架,性能更好。
缺点 -
做不到 Write once, Run everywhere,也就是说开发者依然需要为 iOS 和 Android 平台提供两套不同的代码,比如参考官方文档可以发现不少组件和API都区分了 Android 和 iOS 版本。即使是共用组件,也会有平台独享的函数。
-
不能做到完全屏蔽 iOS 端或 Android 的细节,前端开发者必须对原生平台有所了解。加重了学习成本。对于移动端开发者来说,完全不具备用 React Native 开发的能力。
-
由于 Objective-C 与 JavaScript 之间切换存在固定的时间开销,所以性能必定不及原生。比如目前的官方版本无法做到 UItableview(ListView) 的视图重用,因为滑动过程中,视图重用需要在异步线程中执行,速度太慢。这也就导致随着 Cell 数量的增加,占用的内存也线性增加。
利用脚本语言进行原生平台开发的一次成功尝试,降低了前端开发者入门移动端的门槛,一定业务场景下具有独特的优势,几乎不可能取代原生平台开发。