RN初学者可以知道哪些事情

124 阅读11分钟
  • React Native 的特点

      • 跨平台
      • React Native 使用了 Virtual DOM(虚拟 DOM),只需编写一套代码,便可以将代码打包成不同平台的 App,极大提高了开发效率,并且相对全部原生开发的应用来说,维护成本也相对更低。
    • 上手快
      • 相比于原生开发,JavaScript 学习成本低、语法灵活。允许让 Web 开发者更多地基于现有经验开发 App。React Native 只需使用 JavaScript 就能编写移动原生应用,它和 React 的设计理念是一样的,理论上会写 React,就会写 React Native!
  • 原生体验

      • 由于 React Native 提供的组件是对原生 API 的暴露,虽然我们使用的是 JavaScript 语言编写的代码,但是实际上是调用了原生的 API 和原生的 UI 组件。因此,体验和性能理论上足以媲美原生应用,换句话说RN性能瓶颈不在 API 和原生的 UI 组件
  • 热更新

      • React Native 开发的应用支持热更新,因为 React Native 的产物是 bundle 文件,其实本质上就是 JS 代码,在 App 启动的时候就会去服务器上获取 bundle 文件,我们只需要更新 bundle 文件,从而使得 App 不需要重新前往商店下载包体就可以进行版本更新,开发者可以在用户无感知的情况下进行功能迭代或者 bug 修复。
    • React Native 原理

      • JavaScriptCore
        • JavaScriptCore 是 JavaScript 引擎,通常会被叫做虚拟机,专门设计来解释和执行 JavaScript 代码。在 React Native 里面,JavaScriptCore 负责 bundle 产出的 JS 代码的解析和执行。
      • JS Engine
        • React Native 需要一个 JS 的运行环境,因为 React Native 会把应用的 JS 代码编译成一个 JS 文件(xx.bundle),React Native 框架的目标就是解释运行这个 JS 脚本文件,如果是 Native 拓展的 API,则直接通过 bridge 调用 Native 方法,最基础的比如绘制 UI 界面,映射 Virtual DOM 到真实的 UI 组件中
      • React Native 借助 JS Engine 的能力,基于 JavaScriptCore 来执行 JS,但是是通过 Bridge 来进行交互的,JS 不会直接引用 Native 层的对象实例,Native 也不会直接引用 JS 层的对象实例(在 React Native 里所有 Native 和 JS 互调都是通过 Bridge 层的几个最基础的方法衔接的)。
  • Hermes Engine

    • Hermes
      • 是 Facebook 在 2019 年发布的新一代 JS Engine, - Hermes 是一款小巧轻便的 JavaScript 引擎,专门针对在 Android 上运行 React Native 进行了优化:应用启动时间减少、减少内存使用量并缩小应用程序大小,此外因为它采用 JavaScript 标准实现,所以很容易在 React Native 应用中集成。
    • Hermes vs JavaScriptCore vs V8
      • 经过官方的数据验证,Faceback 团队提出的关键性指标相较于原先的 JavaScriptCore 方案都有了显著提高。
        • 首先,是产物文件的大小方面,RN 所依赖的必要 so 库,
          • Hermes 比 JavaScriptCore 减少了约 16%
          • V8 则要远大于 Hermes 和 JavaScriptCore
    • 在我们团队想通过RN重构weex页面的原因之一,和Hermes可能带来的性能提升有关

  • Bridge

    • 在 React Native 中,原生端和 JavaScript 交互是通过 Bridge 进行的,Bridge 的作用就是给 React Native 内嵌的 JS Engine 提供原生接口的扩展供 JS 调用。所有的本地存储、图片资源访问、图形图像绘制、3D 加速、网络访问、震动效果、NFC、原生控件绘制、地图、定位、通知等都是通过 Bridge 封装成 JS 接口以后注入 JS Engine 供 JS 调用。理论上,任何原生代码能实现的效果都可以通过 Bridge 封装成 JS 可以调用的组件和方法, 以 JS 模块的形式提供给 RN 使用。
  • ReactRootView

      • 原生项目如果想用 React Native,那么就需要用到 ReactRootView,它是 React Native 加载的地方,可以把它看作是一个容器
mReactInstanceManager = ReactInstanceManager.builder()
            .setApplication(requireActivity().application)
            .setCurrentActivity(activity)
            .setBundleAssetName("index.android.bundle") //.setJSBundleFile(bundleFilePath) (如果从网络上下载bundle资源)
            .setJSMainModulePath("index")
            .addPackage(customPage) // 加载自定义开发的native module 这些类可以在JavaScript代码中通过`NativeModules`访问
            .addPackages(packages)
            .setUseDeveloperSupport(BuildConfig.DEBUG)
            .setInitialLifecycleState(LifecycleState.RESUMED)
            .build()
mReactRootView = ReactRootView(activity)
mReactRootView!!.startReactApplication(mReactInstanceManager, componentName, null`)
  • ReactRootView 其实做了这些事情

    • 创建了负责 React Native 和 Native 通信的 RCTBridge 实例的初始化。
    • 初始化了真正展示视图的 RCTRootContentView。
    • 通过 runApplication 方法把必要的参数(moduleName, params)传给 JS 侧的 AppRegistry 的 runApplication 方法,从而运行起了 React Native 的功能。(在 React native 中,根组件是需要通过 AppRegistry 的 registerComponent 方法进行注册的。所谓根组件,就是 Native to JS 的入口文件)
  • React Native 是如何实现 Native <---> JS 通信的呢?

    • React Native 自己实现了 Bridge
    • native 调用 JS

      • 在 React Native 里面,JS 的方法可以通过 global.batchedBridge.callFunctionReturnFlushedQueue 这个方法进行调用,所以在 Native 侧,只需将 React Native 里面的 global.batchedBridge 对象中的方法和 Native 侧的 JSIExecutor 方法进行绑定(本质上 Native 指针指向 JS 函数)
      • Native 侧的 callFunctionReturnFlushedQueue 主要做了这样的事情:
      • 通过 moduleid 和 methodid 完成方法的调用,通过这两个参数可以找到 JS 侧定义的方法模块。
    • JS 调用 Native

      • 当 JS 调用 Native 模块的时候,会调用一个 Native 暴露出来的全局方法:nativeFlushQueueImmediate,并通过传入要调用的 moduleName 、methodName、callback 参数给这个方法,然后这个方法再通知给 Native 侧找到相应的模块并执行
  • React (Native)的 Virtual DOM

    • 在 React Native 里面,是如何把 Virtual DOM 渲染成真实的 UI 的呢?
    • 首先,在 React 里面,用来表示 dom 属性的对象有以下关键属性
    • React 里面的 Virtual DOM 把真实 DOM 分为了以下几种类型:

      • 原子类型
        • 类型为字符串,结构上不可再分解,渲染由平台底层支持。
        • 在浏览器端:原子类型表示为浏览器支持的原始标签,例如 div、ul、li、p、a、span 等等。
        • 在 Native 端:原子类型表示为 Native 端的各种基础 UI 组件,例如 RCTText、RCTView 等等
      • 组合类型
        • 类型为函数构造器,它给我们提供了一种自定义元素 UI 和行为的能力,当渲染器遇到组合类型的元素时,会使用它的构造器创建一个实例并运行 render 方法得到一个新元素(原子类型,或者组合类型),然后再拿该元素继续进行渲染或者分解。
        • 用户自定义的组件元素
  • 渲染器

    • 在浏览器里面:
      • JavaScript 可以调用 DOM API 去完成创建 UI 的工作,而在 React Native 里面,是通过 UI Manager 来创建视图的,基于 Virtual DOM ,React Native 把不同平台创建视图的逻辑封装了一层,不同平台通过 Bridge 调用 UI Manager 来创建不同的 Native 视图。

 

    • 在 Native 端:
      • 在浏览器里面,JavaScript 可以调用 DOM API 去完成创建 UI 的工作,而在 React Native 里面,是通过 UI Manager 来创建视图的,基于 Virtual DOM ,React Native 把不同平台创建视图的逻辑封装了一层,不同平台通过 Bridge 调用 UI Manager 来创建不同的 Native 视图。

 

  • 三个线程

    • 在 React Native 里面,真正有三个重要的线程在执行,他们分别是 Shadow thread、UI thread 和 JS thread。
      • JS thread:
        • 其实是 JavaScript 线程,负责 JS 和原生代码的交互线程,因为 JS 是单线程模型,所以需要一个单独的线程来驱动,并且 JS 和 Native 交互是异步的。
      • Shadow thread:
        • 这个线程是负责 Native 布局,提供给 yoga 引擎使用。
      • UI thread:
        • 这个可以看作是主线程,可以看作是 UI Manager 线程,负责页面的交互和控件绘制逻辑。
  • 热更新

    • React Native 的产物 bundle 文件,本质上是 JS 的逻辑代码加上 React Native 的 Runtime 的集合,所以在应用一启动的时候就会去获取 bundle 文件,之后解析 bundle 文件,最后再由 JS Engine 去执行具体的业务代码逻辑。这就可以允许开发者在云端去更新 bundle 文件,然后应用启动的时候获取最新的 bundle 文件,这一整个流程下来就实现了热更新。
  • 增量更新(拆包)

    • 对于 React Native 的代码打包之后只会生成一个 Bundle 文件,这里面包含了基础业务逻辑、React Native 的基础库类,所以我们可以把一个包拆分成:一个基础包+ n 个业务包,其中基础包是不变的,这就是 runtime,业务包就是具体的业务,后面如果有更新,也只需要再打出一个业务包就行。
    • 目前行业的解决方案有 facebook 官方提供的 metro bundle:facebook.github.io/metro/
  • React Native 的不足

    • 由于 React Native 和原生交互依赖的只有一个 Bridge,而且 JS 和 Native 交互是异步的,所以对需要和 Native 大量实时交互的功能可能会有性能上的不足,比如动画效率,性能是不如原生的。
    • React Native 始终是依赖原生的能力,所以摆脱不了对原生的依赖,相对 Flutter 的自己来画 UI 来说,React Native 显得有些尴尬。
  • React Native 的未来

    • JSI 是什么?
      • 在 RN 中, JSI 是 JavaScript Interface 的缩写,JSI 是一个轻量级的通用的 API 框架,可以应用于任意的 JavaScript virtual machine,让各种平台可以方便地使用不同的 JavaScript 解析引擎(JavaScript virtual machine 包含 JavaScript Engine)。
      • JSI 是用 C++写的,用于取代原先的 bridge,提高通信效率,已在 RN 的 0.58 中实现。
      • 目前在 RN 中,默认使用的 JavaScript virtual machine 也就是 JavascriptCore,有了 JSI ,我们就能轻松地直接调用原生 UI Views 或 Native Modules 用 Java/ObjC 实现的方法(类似 RPC),而不是像原来那样用一层 bridge 来排队等待原生层返回的消息
    • JSI 能做什么?

      • JSI 本身不是 React Native 的一部分——它是一个统一的、轻量的、通用适用于任何(理论上) JavaScript 虚拟机的接口层。
      • 当把 JSI 加入到新架构中后,它使得一些真正重要的改进成为可能。
      • 第一个改进很直观——javaScriptCore 现在可以更容易地被替换成其它引擎,其它选项包括微软的 ChakraCore 和谷歌的 V8。
      • 第二个改进,可以说是整个新架构的基石,是通过使用 JSI,JavaScript 可以持有对 C++ 宿主对象的引用,并且对它进行调用。
      • 这意味着:JavaScript 和 Native 之间真正地相互知晓,并且不再需要通过 JSON 序列化传递消息,这会消除 Bridge 的阻塞问题。
  • 和其他跨端技术比较

    • Flutter vs React Native
      • 首先来简单了解下 Flutter 和 React Native 的背景,Flutter 是由谷歌开发的软件开发工具包(SDK)。它可以帮助开发人员使用单一代码库构建 iOS 和 Android 应用程序。React Native 与 Flutter 具有相同的目的,但方式不同。它是由 Facebook 建立的,基于 React 用于创建移动应用程序,而不会影响应用程序的外观和感觉。
      • 开发体验
        • React Native 在界面开发延续了 React 开发风格,支持 css-in-js(其实就是用 js 来写 css),而且在 0.59 版本之后支持了 React Hook 函数式编程,开发的时候大多只关心样式界面的搭建,原生能力有客户端或者 Bridge 实现。
        • Flutter 最大的特点在于:Flutter 是一套平台无关的 UI 框架,并且在 Flutter 里面万物皆 Widget。很多时候开发一个控件需要嵌套多个 Widget 去实现,与 JS 里面的回调地狱有点像,而这也是被吐槽代码嵌套样式难看的原因。
      • 状态管理
        • React Native 和 Flutter 对于状态管理,两者有着很高的相似度,虽然内部实现很大差别,但是都可以获取 state 和 setState 的方式去更新页面的状态。
      • 产物
        • React Native 产生的是 bundle 文件,实际上就是 JS 脚本文件;而 Flutter 编译后 Android 产生的主要是一些应用程序指令段、数据段,虚拟机数据段、指令段,iOS 则是 App.framework,其实也是一些原生的数据集。
      • 原生能力 & 性能
        • 其实两者的在这方面的区别不是很大,性能方面 React Native 稍微差一点。但是在原生灵活性上 React Native 要有优势。

 

  • Rn(React Native)使用StyleSheet而不是CSS的原因主要有以下几点:

    • 平台差异:React Native的目标是实现一次编写,多平台运行的应用。CSS是为Web设计的,而React Native则需要考虑iOS和Android等移动平台的原生组件和渲染机制。因此,使用专门为React Native设计的StyleSheet更符合其跨平台的需求。
    • 性能优化:React Native注重性能优化,而CSS在浏览器内核中的实现相对复杂,可能存在性能瓶颈。StyleSheet的设计更加轻量且高效,能够提供更好的性能表现。
    • 语法简洁性:StyleSheet的语法相对于CSS来说更加简洁和直观,更符合JavaScript的编程习惯,使得开发者能够更快速地编写和维护样式代码。
    • 样式隔离:在React Native中,每个组件都有自己的样式作用域,这有助于避免样式冲突。而CSS的全局作用域可能导致样式污染,使得样式管理变得复杂。StyleSheet通过组件内部的样式定义,实现了更好的样式隔离。