RN基础知识及原理解析

174 阅读25分钟

本文主要介绍了React和React Native关系,核心组件的概念、作用及相互之间的关系,调试原理,以及与Flutter的对比等。

什么是React

  • React 是一个用于构建用户界面的 JavaScript库/框架 ,主要应用于 Web开发 ,由 Facebook 开发。
  • 核心思想:组件化、声明式编程、虚拟 DOM,用于快速、高效地构建复杂的 Web 应用。

什么是React Native

  • React Native 是基于 React 的框架,用于开发跨平台的移动应用 (支持 iOS 和 Android),也逐渐衍生出支持macOS,windows等平台。

  • 核心思想继承 React,但将虚拟 DOM 映射为原生组件 ,最终生成真正的 Native 应用,而不是运行于浏览器中的 Web 应用。

React和React Native两者的关系

  • 共性 :

    • 使用相同的开发理念:组件化、单向数据流。

        • 关于单向数据流:

          • 目的:单向数据流让数据的来源和流向变得清晰明确,使你能够更直观地了解数据的生命周期。如果允许父组件和子组件之间随意修改数据,或者数据能够双向流动,那么应用的数据流向将变得难以预测,从而让调试和维护变得困难。

          • 表现:

            • 父组件通过 props 向子组件传递数据。

              • 父组件将数据作为属性(props)传给子组件,子组件通过 props 对数据进行访问。
              • 子组件不能直接修改 props,只能用于展示或根据需求做逻辑处理。
              • 数据从父组件流向子组件,这就是单向数据流的核心。
            • 状态管理也遵循单向数据流的原则。

              • 组件的状态(state)是私有的,它只能存在于定义它的组件内部。
              • 如果子组件需要更新父组件中的数据,通常通过回调函数(由父组件传递给子组件的 props)实现。
    • 使用相同的核心库和语言:React + JavaScript/Typescript。

    • 都支持状态管理工具(Redux、MobX 等)。

  • 差异 :

    • 运行环境 :React 运行在浏览器中;React Native 运行在 iOS/Android等原生设备中。
    • 组件 :React 使用 HTML(如 <div>),React Native 使用 Native 组件(如 <View>)。
    • 样式 :React 使用 CSS;React Native 使用 StyleSheet(内联样式,类似 CSS)。
    • 路由:React一般通过一般通过 React Router 等工具来完成路由。而RN由于没有浏览器的 URL 路由,需要用专门的导航库,比如 React Navigation。
    • 为了方便理解,我更倾向于:把React理解为基础框架,而React Native是基于React框架做的native层面的拓展。
    • 实际开发的时候,总体来说差异不大,只是RN因为支持的组件更有限,所以灵活性差一些。开发的时候注意差异点中的组件样式路由部分即可。只要一个概念、组件、库不涉及具体的平台,那都是可以用的。

为什么需要React Native

跨端提效是唯一的目的。相比Flutter、Qt等其他跨端框架来说:

  • RN的学习效率更高--得益于使用了js,ts等解释型语言,热加载等优势。
  • RN的受众群体更大--得益于与前端的影响力。
  • RN的稳定性--Facebook和其社区始终在维护。

React和React-Native中的核心概念/模块介绍:

通用概念/模块:

JSX

  • 概念:React and React Native use JSX, a syntax that lets you write elements inside JavaScript like so: Hello, I am your cat!. The React docs have a comprehensive guide to JSX you can refer to learn even more. Because JSX is JavaScript, you can use variables inside it. Here you are declaring a name for the cat, name, and embedding it with curly braces inside .

components

  • 概念:组件是一段通过 JavaScript 和 JSX 语法组合的代码,用于描述用户界面的某一部分。每个组件本质上是一个函数或类,它接受输入(props ),返回要渲染的内容(通常是 JSX)。

  • 分类

    • 函数组件:--推荐
    •   type TextProps = {
          name: string;
        }
      
        function Greeting(props: TextProps): React.JSX.Element {
          return <Text>Hello, {props.name}!</Text>;
        }
      
        // 使用组件
        <Greeting name="React" />
      
    • 类组件:--不推荐,旧的写法
    •   type TextProps = {
          name: string;
        }
      
        class Greeting extends React.Component<TextProps> {
          render() {  // 需要一个render犯法
            return <Text>Hello, {this.props.name}!</Text>;
          }
        }
      
        // 使用组件
        <Greeting name="React" />
      
  • 注意点:一个组件只能返回一个闭合的标签。如果组件需要返回多个兄弟标签,他们必须被包含在一个单一的顶层容器中。这个容器可以是

    ,等,但是为了减少没必要的DOM节点/Native层级,建议可以使用React Fragment: <></>来代替。

    • 在RN里面为了优化层级过深导致的性能问题,框架会自己做View Flatting来合并无用/相似试图,当然我们能用React Fragment来代替是最好的。

const Cafe = () => {
  return (
    <>
      <Cat name="Munkustrap" />
      <Cat name="Spot" />
    </>
  );
};

props

  • 概念:Props is short for “properties”. Props let you customize React components. --参数、属性
  • 举例:Here you pass each a different name for Cat to render:
type CatProps = {
  name: string;
};

const Cat = (props: CatProps) => {
  return (
    <View>
      <Text>Hello, I am {props.name}!</Text>
    </View>
  );
};

const Cafe = () => {
  return (
    <View>
      <Cat name="Maru" />
      <Cat name="Jellylorum" />
      <Cat name="Spot" />
    </View>
  );
};

state:

  • 概念:While you can think of props as arguments you use to configure how components render, state is like a component’s personal data storage. State is useful for handling data that changes over time or that comes from user interaction. State gives your components memory.--类比成员变量
  • 举例
export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>Counters that update separately</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

TypeScript

  • 概念:TypeScript 是 JavaScript 的超集,它通过引入静态类型检查和额外的功能(例如接口、泛型等)来增强代码的健壮性和开发体验。

  • 作用

    • 强类型系统 : 提供静态类型检查,帮助开发者在开发阶段发现潜在的错误,减少运行时的错误。
    • 提高代码可维护性 : 明确定义变量、函数参数和返回值的类型,使代码更易于维护和阅读。
    • 编写安全的组件 : 在 React 的组件开发中,TypeScript 能帮助定义 props 和 state 的类型,减少错误。
  • 适用场景 : TypeScript 更适合中大型项目、团队开发,或者想要高代码质量、减少错误的开发者。虽然在小项目中可以选择不使用 TypeScript,但它在现代 JavaScript/React Native 开发中越来越流行。

RN独有的概念/模块介绍:

Metro

  • 概念:Metro 是 React Native 的默认 JavaScript bundler(打包工具), 是为 React Native 定制的,它的性能针对开发环境进行了优化,例如增量编译、更快的模块解析以及开发者体验相关的功能(如 hot-reload 热重载和 fast-refresh 快速刷新)---类比web中打包工具webpack。

  • 作用

    • 快速打包 : 用于将开发者的 JavaScript 代码及其依赖打包为一个可以在 iOS 和 Android 等原生平台上运行的 JavaScript 文件。
    • 模块化解析 : Metro 负责解析所有依赖的模块并把它们打包到一个 bundle 文件中,可以有效地加载和运行。
    • 热重载(Hot Reloading)和 Live Reload : 在开发过程中支持热重载和自动刷新,以提高开发效率。
    • 优化打包时间 : Metro 是为 React Native 专门优化的打包工具,能够处理大量小模块并快速打包。
  • 适用场景 : Metro 是 React Native 必备的工具,通常不需要额外安装或替换。它在开发和构建阶段都不可或缺。

Hermes

  • 概念:Hermes is an open-source JavaScript engine optimized for React Native. For many apps, using Hermes will result in improved start-up time, decreased memory usage, and smaller app size when compared to JavaScriptCore. Hermes is used by default by React Native and no additional configuration is required to enable it. React Native also supports using JavaScriptCore as the JavaScript engine. Follow these instructions to opt-out of Hermes.--js/ts解释器和运行时

  • 主要作用

    • 优化性能 : Hermes 通过提前将 JavaScript 编译为字节码(Bytecode),降低在设备上的运行和启动时间。设备运行时无需解析 JavaScript 源代码,也不用进行即时编译(JIT),直接执行预编译的字节码,从而降低了运行时的开销。但它的运行时性能(执行速度)可能不会显著超越 JSC 或其他高效的 JavaScript 引擎。
    • 节省资源 : Hermes 的设计很轻量,适合在资源有限的设备(如低端手机)上运行。
    • 调试能力 : 提供了集成的调试工具(如 Hermes Debugger),帮助开发者跟踪和优化代码问题。
  • 适用场景 : Hermes 主要用于优化 React Native 应用,在特别注重性能的场景(如大型复杂应用或低端设备支持)中尤为重要。它可以手动启用,但目前已成为许多 React Native 应用的默认设置。

Expo:

  • 概念:Expo 是一个开发框架和工具集,它基于 React Native,旨在简化移动应用开发流程,特别是开发的启动、配置和发布阶段。目的是:你不需要安装开发sdk,不需要苹果开发者账号,不需要了解原始应用开发,也可以开发原生app。

  • 作用

    • 快速项目启动 : 提供带有预配置设置的模板项目,可以快速开始开发 React Native 应用。
    • 模块化工具集 : 集成了一系列常用功能(如相机、定位、推送通知、图像处理等),无需额外配置原生模块。
    • 托管服务 : 提供云构建服务,可以轻松构建和发布应用到 App Store 和 Google Play,而无需配置复杂的原生代码。
    • 开发服务器 : 提供一个易于使用的开发环境,可以快速检查和测试代码。
    • 无需配置原生代码 : 在不需要深入原生代码时,使用 Expo 的管理工作流可以完全避免使用原生代码。
  • 适用场景 : 不擅长原生代码或者希望快速开发和部署简单应用的开发者。对于需要自定义原生模块的场景,需要使用纯React Native CLI模式。

  • 限制:在Expo模式下不允许直接访问或修改原生代码,无法添加自定义原生模块,只能使用 Expo 支持的功能。

Yoga

  • 概念:Yoga is an embeddable C++ layout system used in popular UI frameworks like React Native. Yoga itself is not a UI framework, and does not do any drawing itself. Yoga's only responsibility is determining the size and position of boxes.--布局 引擎

  • 举例

    • 对于iOS来说:React Native 并没有直接使用 iOS 的 Auto Layout 去管理 Flexbox,而是集成了一个轻量化的布局引擎Yoga 。Yoga 是一个跨平台的布局库,它实现了 Flexbox 算法,并支持将这些布局规则应用于真实的组件。Yoga会根据 JavaScript 中定义的Flexbox布局规则计算每个 React Native 组件需要的大小和位置。Yoga的布局结果会应用到 iOS 的 UIView 进行最终渲染。Yoga 为所有组件预先计算了布局,动态生成了 frame 布局,而非直接利用 Auto Layout 约束。

Fabric :

  • 概念:Fabric is React Native 's new rendering system, a conceptual evolution of the legacy render system. The core principles are to unify more render logic in C++.

  • 工作流

    • 三棵树:

      • React Element Tree:React代码描述的树。一个JS的Tree数据结构。
      • React Shadow Tree: React Element Tree对应在Fabric中的树。一个C++的Tree结构。
      • How View Tree:最后在对应native平台屏幕上呈现的树。
    • 三个阶段:

      • Render: React executes product logic which creates a React Element Trees in JavaScript. From this tree, the renderer creates a React Shadow Tree in C++.
      • Commit: After a React Shadow Tree is fully created, the renderer triggers a commit. This promotes both the React Element Tree and the newly created React Shadow Tree as the “next tree” to be mounted. This also schedules the calculation of its layout information.
      • Mount: The React Shadow Tree, now with the results of layout calculation, is transformed into a Host View Tree.
    • Diff比较前后两颗Shadow Trees。以下是更新机制和性能优化的关键信息摘录:

      • When a state update occurs, the renderer needs to conceptually update the React **Element Tree in order to update the host views that are already mounted. But in order to preserve thread safety, both the React Element Tree as well as the React Shadow Tree must be immutable. This means that instead of mutating the current React Element Tree and React Shadow Tree , React must create a new copy of each tree which incorporates the new props, styles, and children.
      • When React creates a new React Element Tree that incorporates the new state, it must clone every React Element and React Shadow Node that is impacted by the change. After cloning, the new React Shadow Tree is committed.
      • When a React **Element is cloned to include the new state, every React Element that is on the path up to the root is cloned. React will only clone a React Element if it requires an update to its props, style, or children. Any React Elements that are unchanged by the state update are shared by the old and new trees.---性能优化

当红色节点变成黄色时候的整个过程:

  • 为什么只是变了一个节点也会生成一个新的Shadow Tree?--因为做diff需要从root node开始比较。

Diff的基本原理:
Shadow Tree Diff基本原理:

Shadow Tree 的 diff 过程由 React Reconciler 控制,核心依据是 React 的协调算法(Reconciliation) ,主要依赖以下规则:

  • 节点类型(Type)和 Key

    • 相同类型 & 相同 Key → 复用节点,触发 update(仅更新属性/子节点)。
    • 不同类型或不同 Key → 销毁旧节点,创建新节点(触发 remount)。
  • 子树协调

    • React 会递归比较子节点,但若父节点类型变化,整个子树会直接销毁并重建(即使子节点未变)。
Host View Tree 的更新机制
  • Fabric引擎尽可能复用原节点做update而非remount,但受限于平台能力。

  • 当 Shadow Tree 的 diff 算法确定某个元素需要更新,并且这个更新无法通过简单的属性更新(即 update)完成时,确实会触发更复杂的操作:卸载(unmount)旧视图,然后创建并挂载(mount)新视图到 Host View Tree 上。---这是一个RN的性能问题点。

以上这些概念之间的关系:

暂时无法在飞书文档外展示此内容

关于调试:

调试的原理: npx react-native run-ios之后发生了什么?

  1. 启动 Metro Bundler:如果开发服务器(Metro Bundler)尚未启动,run-ios 会首先启动它。如上所说Metro Bundler 是 React Native 的核心 JavaScript 打包器和模块工具,用于构建和上传 JavaScript 代码到模拟器或设备。这一步其实是先启动Metro Bundler代理服务:npx react-native start,作用:

    1. 开启一个开发服务器(通常是 HTTP 本地服务,默认运行在 localhost:8081)。
    2. 扫描你的 JavaScript 文件,打包项目代码并加载到设备或模拟器中。
    3. 提供热重载功能,让你修改代码后可以实时更新到应用中。
  2. 调用 Xcode 构建 iOS 项目React Native 应用的原生部分(位于项目的 ios/ 目录)会通过 Xcode 构建。run-ios 调用 Xcode 的 CLI 工具(xcodebuild),使用默认的配置来编译并启动 iOS 原生项目。

  3. 启动特定的 iOS 模拟器 :构建完成后,React Native CLI 会启动默认的 iOS 模拟器(例如 iPhone 14)。

    1. 如果已有 iOS 模拟器运行,它会直接运行应用到当前模拟器。
    2. 如果运行应用时连接了物理设备,你可以通过额外参数指定目标设备。
  4. 将应用加载到模拟器/设备 :Metro Bundler 会将你的逻辑代码(JavaScript 部分)加载到 iOS 模拟器或设备中,并运行。

  5. 你可以通过 Metro 的热重载功能动态修改应用代码保存并实时生效。

  6. 等待调试器挂载上来。

热重载是如何实现:

  1. 文件监听与增量编译:Metro 监听项目文件变动(如 *.js, *.jsx)。当文件被修改时,Metro 只重新编译改动的模块(而非整个应用),生成增量更新包。

  2. 增量更新包传输:编译后的代码通过 WebSocket(默认端口 8081)实时发送到设备(iOS/Android 模拟器或真机)。

  3. 运行时模块替换:React Native 的 HMR 运行时(HMRClient)接收新模块代码。通过 module.hot API(Webpack 风格的 HMR 接口)决定如何更新:

    1. 如果是 React 组件,保留组件状态,重新执行 render()。
    2. 如果是 非组件模块(如工具函数),替换整个模块。
  4. React 组件更新:React Native 会 保留组件实例(即不卸载组件),调用 forceUpdate() 触发重新渲染,从而保留内部的状态。

各种调试工具:

  1. RN代码运行移动端的时候,只能通过attach package的方式,通常是先启动server,然后启动调试器(等待native进程拉取rn bundle),最后启动native app从server拉取rn bundle。
  2. 除了expo工程之外,其他都可以使用vscode内置的rn调试器进行调试。当用vscode插件调试expo iOS应用时会提示:NOTE: You are using an unsupported debugging client. Use the Dev Menu in your app (or type j in the Metro terminal) to open React Native DevTools.可能expo不支持vscode中的调试插件,这个时候就有ReactNative DevTools吧,也非常好用:reactnative.dev/docs/debugg… iOS模拟器通过ctl+cmd+z打开。
  3. react-native cli创建的工程是可以支持vscode hermes调试的,lanuch.json配置如下:
"name": "Attach to Hermes application",
"request": "attach",
"type": "reactnativedirect",
"cwd": "${workspaceFolder}"

View Flatting

一句话:开发者出于可维护性和便利性很容易写出嵌套很严重的React Element Tree,但一个嵌套严重的Tree映射到Host View Tree的时候对于性能是很不利的。所以RN做了一些优化(合并或者删除一些无用嵌套,比如<></>),最使得最终mount到native的嵌套没那么多,从而优化布局性能。

Threading Model

React Native renderer is designed to be thread-safe. At a high level thread safety is guaranteed by using immutable data structures in the internals of the framework (enforced by C++ “const correctness” feature). This means that every update in React creates or clones new objects in the renderer instead of updating data structures. This allows the framework to expose thread safe and synchronous APIs to React.

The renderer uses two different threads:

  • UI thread (often called main): The only thread that can manipulate host views.

  • JavaScript thread: This is where React’s render phase, as well as layout, are executed.

React-Native的工作原理的简单总结

使用js/ts编写代码,通过React框架,建立React Elemnt Tree来建立并驱动对应的Native Tree。把逻辑部分用更轻量化、更易于开发和调试的js/ts来实现。对C端呈现来说完全是一个native app,完全使用native的渲染框架和能力。基于这个基本理解,我们可以认为:原生有什么,我们才能用什么,所有原生没有的,都得想其他办法解决。(好在当前原生提供的组件已经可以解决>99%的问题了。)

React Native的性能瓶颈(部分来自DeepSeek)

主要瓶颈:

  • 渲染管线(Rendering Pipeline)

    • Shadow Tree 计算与布局(Yoga)

      • React 的 Reconciler 生成 Shadow Tree 后,需要经过 Yoga 布局引擎计算(Flexbox)。
      • 瓶颈点:复杂嵌套布局(如多层 View)会导致递归计算耗时。
      • 优化:减少嵌套层级,用 FlatList 替代 ScrollView,避免 onLayout 过度触发。
    • 原生视图操作(Fabric vs. Legacy)

      • Legacy 架构:Bridge 序列化/反序列化 + 异步更新 → 延迟高。
      • Fabric 架构:JSI 同步通信 + 增量更新 → 大幅改善,但仍有线程切换开销。
  • 线程模型(Threading Model)

    • JS 线程、UI 线程、原生模块线程的通信

      • 默认情况下,JS 线程负责逻辑计算,UI 线程负责渲染,频繁通信会导致卡顿。
      • 典型场景:滚动列表时 JS 线程忙于计算,阻塞 UI 线程渲染。
      • 优化:使用 useMemo/useCallback 减少 JS 计算,或用 react-native-reanimated 将动画逻辑移到 UI 线程。
  • 不必要的组件 Remount

    • 原因

      • Key 或组件类型变化 → 触发 Shadow Tree 完全重建。
      • 父组件状态更新导致子树重新挂载(如未合理使用 memo)。
    • 影响:原生视图销毁/重建(Android 的 View 或 iOS 的 UIView 开销较大)。

    • 优化:稳定 key,用 React.memo 避免子组件无效更新。

  • JSI 的潜在问题

    • 优势:同步调用替代 Bridge,减少延迟。

    • 瓶颈

      • JS 与 C++ 的频繁交互:大量 JSI 调用仍可能阻塞 JS 线程(如密集的 measure 操作)。
      • 线程安全:JSI 对象跨线程访问需加锁(如 SharedArrayBuffer)。
    • 优化:避免在循环中频繁调用 JSI 方法(如 UIManager.measure)。

  • 列表渲染(FlatList 优化)

    • 长列表性能问题

      • 未优化 renderItem 函数导致重复计算。
      • 缺少 getItemLayout 时动态测量高度开销大。
    • 优化

      • 使用 getItemLayout 固定高度列表。
      • 惰性加载图片(如 react-native-fast-image)。
  • 图片与内存

    • 图片解码:大图加载阻塞 UI 线程(尤其是 Android)。
    • 内存泄漏:未清理事件监听或原生模块引用。
    • 优化:压缩图片,使用 Image.prefetch,监控内存警告。

次要瓶颈:

  • Tree Diff 计算。

    • Reconciler 的 Diff 算法:时间复杂度 O(n),对 VDOM 树遍历。
    • 实际影响:除非组件树极深(如 1000+ 节点),否则不是主要瓶颈。
    • 优化:避免深层嵌套,拆分大组件。
  • Hermes 引擎的 JS 执行

    • Hermes 预编译字节码 已显著提升性能,但在低端设备上可能仍有解析开销。
    • 优化:启用 Hermes,删除未使用的代码(如 babel-plugin-transform-remove-console)。

哪些应用不适合用React Native开发?

  1. 计算量很大的:React Native 的 JavaScript 线程和原生模块通信存在性能瓶颈,大量计算会阻塞 UI 线程,导致卡顿。虽然可以通过原生模块(Native Modules)将计算逻辑移到原生端,但这样会失去跨平台优势,增加开发复杂度。
  2. 手势操作很多的:如绘图类。复杂手势可能导致 JavaScript 线程和原生线程通信延迟,影响流畅性。
  3. 视图刷新很快的:如股票交易类软件。React Native 的 UI 更新需要通过 JavaScript 桥接通信到原生端(甚至导致原生端组件反复的重建),频繁刷新(如每秒多次更新的股价)会导致性能瓶颈。
  4. 对启动速度敏感的应用:RN 应用启动时需要初始化 JavaScript 引擎,可能比纯原生应用慢(尤其在低端设备上)。
  5. 强依赖原生硬件的功能:RN 对深度硬件交互(如 ARKit、自定义蓝牙协议)的支持有限,需大量原生代码开发,失去跨平台意义。

当我们需要一个组件,但是目前RN库和三方库都没有(比如复杂的脑图组件),那有哪些解决方案呢?

还是那句话:原生没有直接提供的,你得自己想办法。

方案性能等级开发效率是否需要native代码实现原理成本
WebView (jsMind/MindMap.js)⭐⭐⭐⭐WebView 中加载 HTML/JS 实现的思维导图库,通过 postMessage 与 RN 通信。
自定义原生组件⭐⭐⭐⭐是,而且大量用原生代码(iOS/Android)实现视图,通过 React Native 桥接暴露给 JS 调用。就跟React Native目前已经实现的 一样极高,每个平台要自己实现
SVG/Canvas (react-native-svg)⭐⭐⭐⭐⭐⭐使用 react-native-svg 或 Canvas API 手动绘制节点和连线,最终绘制交给native层。> 它通过使用 React 的声明式语法,将 SVG 的结构和属性转换为原生平台可以理解和渲染的形状和样式。
混合方案 (react-native-skia)⭐⭐⭐⭐⭐⭐基于 Skia 引擎(跨平台C++渲染引擎)通过 JSI 直接渲染,高性能绘图,类似 Flutter 的渲染逻辑。(需要评估引入skia引擎对包体的增量-很大)高,要学习skia

React Native vs Flutter

  • 代码结构:理念一致,都是组件化、声明式、数据驱动。写法上Flutter更接近传统native的写法,Flutter 的代码结构更“一体化”(Widget、逻辑、样式都在一个类中)。而RN支持把style剥离出来,更优雅,可读性更好。

  • 性能:

    • Flutter没有JSI这一层,也没有大量的序列化和反序列工作。再上Dart语言的优势,整体性能肯定是好于RN的。但就普通应用和页面的实现来说,多数停留着展示和简单交互,没有明显差距。
    • Flutter什么都自己实现的同时,也意味着无法直接受益于系统组件的优化(例如 iOS 的滚动行为)。要知道很多平台的组件库都优化了很多年。
  • 视觉还原能力:

    • 首先要强调的是两者能做到高保真还原(事实上从单一平台上看,任何一个框架的渲染引擎都具备“高保真像素级还原”的这个能力,否则它就无法留存下来)。
    • Flutter被冠以“高保真像素级还原”这个名头的主要原因在于它确实没有使用任何原生定义好的组件,是通过skia跨平台渲染引擎一个个点、线、面画出来的,所以灵活性很强。更重要的是skia本身是跨平台的,所以天然具有跨平台高保真还原的优势。也就是说最终所谓的高保真还原优势体现在:当没有一个合适的组件,需要我们通过一笔一划的方式画出来的时候,Flutter只需要实现一遍即可,效率更高!
    • 与此同时,Flutter在某些平台上可能显得“不原生”(例如 Android 的 Material 3 动态颜色需要额外适配)。
  • 生态与社区

    • RN 的 JavaScript 生态更庞大(尤其是第三方服务 SDK)。
    • Flutter 的 Pub 包质量较高,但数量可能不如 RN。
  • 多平台支持

    • Flutter 对桌面和 Web 的支持更成熟,RN 的桌面(如 Windows/macOS)仍不完善。

一些有趣的小知识

  • 为什么需要两个花括号?

      1. Any JavaScript expression will work between curly braces
      2. Notice the double curly braces {{ }} surrounding style‘s width and height. In JSX, JavaScript values are referenced with {}. This is handy if you are passing something other than a string as props, like an array or number: <Cat food={["fish", "kibble"]} age={2} />. However, JS objects are also denoted with curly braces: {width: 200, height: 200}. Therefore, to pass a JS object in JSX, you must wrap the object in another pair of curly braces: {{width: 200, height: 200}}
  • 空标签<></>是什么意思:

    •   const Cafe = () => {
          return (
            <>
              <Cat name="Munkustrap" />
              <Cat name="Spot" />
            </>
          );
        };
      
    • These bits of JSX are fragments. Adjacent JSX elements must be wrapped in an enclosing tag. Fragments let you do that without nesting an extra, unnecessary wrapping element like View.
  • 条件运算符:condition ? r1 : r2

    •   <div>
          {isLoggedIn ? (
            <AdminPanel />
          ) : (
            <LoginForm />
          )}
        </div>
      
  • 优雅的 else branch: logical && syntax:

    •   <div>
          {isLoggedIn && <AdminPanel />}
        </div>
      
    • 在Javascript中,逻辑操作符&&的短路行为允许这种表达式。如果isLoggedIn=true, 则返回。如果是false/null/undefined,那就直接忽略在渲染中。
  • Why immutability is important:

    • 这一点在示例中体现的很明显:react.dev/learn/tutor…
    • By default, all child components re-render automatically when the state of a parent component changes. This includes even the child components that weren’t affected by the change. Although re-rendering is not by itself noticeable to the user (you shouldn’t actively try to avoid it!), you might want to skip re-rendering a part of the tree that clearly wasn’t affected by it for performance reasons. Immutability makes it very cheap for components to compare whether their data has changed or not. You can learn more about how React chooses when to re-render a component in the memo API reference.
  • 组件何时需要指定key:

    • Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
    • The best way to pick a key is to use a string that uniquely identifies a list item among its siblings. When you don’t have stable IDs for rendered items, you may use the item index as a key as a last resort.(We don’t recommend using indexes for keys if the order of items may change. This can negatively impact performance and may cause issues with component state.)
    • Keys only make sense in the context of the surrounding array.
    • Keys Must Only Be Unique Among Siblings.
  • 常见指令

    • npm install: 安装package.json中的依赖,一般用于本地初始化远端拉取的工程。

    • npm run:其实运行的是package.json中的script指令,如npm run start就是执行package.json中的start指令(比如对应react-native start)。其实是在react-native-cli上运行,完整指令是`npx react-native start,只不过npx是全局安装的话就可以省略了。所以你也可以不运行npm run xxx而直接运行[npx] react-native/expo start

    • 初始化工程的2种方式:

      • 基于纯React Native CLI工程:(需要设置 Android Studio 和 Xcode 等原生开发环境:编译器,sdk等。)

        1. 初始化工程:npx @react-native-community/cli init MyApp --version 0.73.11

        2. 完成之后进入工程根目录用npx react-native doctor进行自检,解决问题。

        3. 平台相关处理:

          1. 如果是iOS,就进入iOS目录执行pod install,之后启动native应用可以用xcode打开ios目录下的xcworkspace,也可以直接使用npx react-native run-ios。(有些时候node_module有错误,可以先全部删除rm -rf node_moudles.然后再npm install,在pod install)。
          2. 如果是安卓,则进入android目录完成gradle依赖同步。
      • 基于expo的React Native工程:

        1. 初始化工程:npx create-expo-app MyAwesomeProject
        2. 运行:npx expo start
      • 可以使用 Expo Go(android)或者相机(iOS) 应用通过扫码快速安装运行,无需安装 SDK 或配置模拟器。

  • npm, npx, react-native-cli及其关系

    • npm: Node package manager。也是全球流行的 JavaScript 包管理生态系统。它主要用来安装、管理和发布 JavaScript 的库和工具,是前端和 Node.js 开发的基础工具之一
    • npx:npx 是 npm(Node.js 的包管理器)自带的工具,专门用来执行 npm 包中的命令 ,不需要全局安装这些包。
    • react-native-cli: 一个实际的 React Native 工具包,负责处理 React Native 项目的初始化、构建和运行。是通过npm安装的。
    • 所以当运行npx react-native run-android的时候实际是调用npm包中的npx工具,让npx工具来调用react-native-cli这个工具包来运行RN的安装项目。

参考链接

React:

快速开始

完整的井字游戏示例

React-Native:

完整的基础知识

组件列表

API列表

架构介绍(渲染引擎,渲染机制,线程模型等)