跳到主要内容

React Native ✅

React Native 是 Facebook 开发的跨平台移动开发框架, 让开发者可以使用 JavaScript 和 React 构建原生移动应用。 本章涵盖 RN 架构原理、Bridge 通信机制等核心概念。

React Native 工作原理是什么?

答案

核心概念:

React Native 基于三线程架构:JavaScript 线程运行 React 代码, Native UI 线程处理原生渲染,Bridge 负责两者间的异步通信。 通过 Virtual DOM 机制管理 UI 状态变化,最终映射到真实的原生组件上, 实现"Learn once, write anywhere"的跨平台开发体验。

架构组成:

  • JavaScript 线程: 执行 React 组件逻辑、状态管理和业务代码
  • 原生 UI 线程: 处理真实的原生组件渲染和用户交互
  • Bridge 桥接: 序列化消息实现 JS 与原生代码的双向异步通信
  • Virtual DOM: React 的虚拟 DOM 机制,高效管理 UI 状态和更新
  • 原生组件映射: Text → UILabel/TextView,View → UIView/ViewGroup

示例说明:

// React Native 架构演示
// 模拟 RN 的线程模型和通信机制

/**
 * 模拟 JavaScript 线程
 * 负责执行 React 组件逻辑和状态管理
 */
class JavaScriptThread {
  constructor() {
    this.components = new Map();
    this.virtualDOM = null;
    this.bridge = null;
  }
  
  setBridge(bridge) {
    this.bridge = bridge;
  }
  
  // 模拟组件渲染
  renderComponent(componentTree) {
    console.log('🔵 JS Thread: 开始渲染组件树');
    this.virtualDOM = this.buildVirtualDOM(componentTree);
    console.log('🔵 JS Thread: Virtual DOM 构建完成', this.virtualDOM);
    
    // 通过 Bridge 发送渲染指令到原生端
    this.bridge.sendToNative('RENDER', this.virtualDOM);
    return this.virtualDOM;
  }
  
  buildVirtualDOM(component) {
    return {
      type: component.type,
      props: component.props,
      children: component.children?.map(child => 
        typeof child === 'string' ? child : this.buildVirtualDOM(child)
      ) || []
    };
  }
  
  // 处理用户事件
  handleEvent(eventType, eventData) {
    console.log(`🔵 JS Thread: 处理事件 ${eventType}`, eventData);
    
    // 模拟状态更新
    if (eventType === 'PRESS') {
      const newState = { ...eventData, pressed: true };
      console.log('🔵 JS Thread: 状态更新', newState);
      
      // 触发重新渲染
      this.bridge.sendToNative('UPDATE', newState);
    }
  }
}

/**
 * 模拟原生线程(UI线程)
 * 负责实际的 UI 渲染和用户交互
 */
class NativeUIThread {
  constructor() {
    this.nativeComponents = new Map();
    this.bridge = null;
  }
  
  setBridge(bridge) {
    this.bridge = bridge;
  }
  
  // 接收来自 JS 线程的渲染指令
  receiveFromJS(action, data) {
    switch (action) {
      case 'RENDER':
        this.renderNativeUI(data);
        break;
      case 'UPDATE':
        this.updateNativeUI(data);
        break;
      default:
        console.log('🔴 Native Thread: 未知操作', action);
    }
  }
  
  renderNativeUI(virtualDOM) {
    console.log('🔴 Native Thread: 开始渲染原生 UI');
    
    const nativeElement = this.createNativeElement(virtualDOM);
    this.nativeComponents.set('root', nativeElement);
    
    console.log('🔴 Native Thread: 原生 UI 渲染完成', nativeElement);
    return nativeElement;
  }
  
  createNativeElement(vdom) {
    const mapping = {
      'View': 'UIView',
      'Text': 'UILabel',
      'TouchableOpacity': 'UIButton',
      'Image': 'UIImageView'
    };
    
    return {
      nativeType: mapping[vdom.type] || vdom.type,
      props: vdom.props,
      children: vdom.children?.map(child => 
        typeof child === 'string' ? child : this.createNativeElement(child)
      ) || []
    };
  }
  
  updateNativeUI(newState) {
    console.log('🔴 Native Thread: 更新原生 UI', newState);
    // 模拟原生组件属性更新
    const rootComponent = this.nativeComponents.get('root');
    if (rootComponent) {
      rootComponent.state = newState;
      console.log('🔴 Native Thread: UI 更新完成');
    }
  }
  
  // 模拟用户交互
  simulateUserTouch(elementId) {
    console.log('🔴 Native Thread: 用户触摸事件', elementId);
    // 通过 Bridge 发送事件到 JS 线程
    this.bridge.sendToJS('PRESS', { elementId, timestamp: Date.now() });
  }
}

/**
 * 模拟 React Native Bridge
 * 负责 JS 线程和原生线程之间的通信
 */
class ReactNativeBridge {
  constructor() {
    this.jsThread = null;
    this.nativeThread = null;
    this.messageQueue = [];
    this.isProcessing = false;
  }
  
  connect(jsThread, nativeThread) {
    this.jsThread = jsThread;
    this.nativeThread = nativeThread;
    
    jsThread.setBridge(this);
    nativeThread.setBridge(this);
    
    console.log('🌉 Bridge: 连接 JS 线程和原生线程');
  }
  
  // JS 线程向原生线程发送消息
  sendToNative(action, data) {
    console.log('🌉 Bridge: JS → Native', { action, data });
    this.messageQueue.push({
      from: 'JS',
      to: 'Native',
      action,
      data,
      timestamp: Date.now()
    });
    
    this.processQueue();
  }
  
  // 原生线程向 JS 线程发送消息
  sendToJS(eventType, eventData) {
    console.log('🌉 Bridge: Native → JS', { eventType, eventData });
    this.messageQueue.push({
      from: 'Native',
      to: 'JS',
      eventType,
      eventData,
      timestamp: Date.now()
    });
    
    this.processQueue();
  }
  
  // 异步处理消息队列(模拟真实的异步通信)
  async processQueue() {
    if (this.isProcessing || this.messageQueue.length === 0) return;
    
    this.isProcessing = true;
    
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      
      // 模拟网络延迟
      await new Promise(resolve => setTimeout(resolve, 10));
      
      if (message.to === 'Native') {
        this.nativeThread.receiveFromJS(message.action, message.data);
      } else {
        this.jsThread.handleEvent(message.eventType, message.eventData);
      }
    }
    
    this.isProcessing = false;
  }
  
  // 获取通信统计
  getStats() {
    return {
      queueLength: this.messageQueue.length,
      isProcessing: this.isProcessing
    };
  }
}

/**
 * 演示完整的 React Native 工作流程
 */
async function demonstrateRNArchitecture() {
  console.log('=== React Native 架构演示 ===\n');
  
  // 1. 初始化各个线程和桥接
  const jsThread = new JavaScriptThread();
  const nativeThread = new NativeUIThread();
  const bridge = new ReactNativeBridge();
  
  // 2. 连接线程
  bridge.connect(jsThread, nativeThread);
  
  console.log('📱 应用启动,各线程已连接\n');
  
  // 3. 模拟 React 组件树
  const appComponent = {
    type: 'View',
    props: { style: { flex: 1, backgroundColor: 'white' } },
    children: [
      {
        type: 'Text',
        props: { style: { fontSize: 18, color: 'black' } },
        children: ['Hello React Native!']
      },
      {
        type: 'TouchableOpacity',
        props: { id: 'button1', onPress: true },
        children: [
          {
            type: 'Text',
            props: { style: { color: 'blue' } },
            children: ['点击我']
          }
        ]
      }
    ]
  };
  
  // 4. 渲染组件
  console.log('--- 步骤 1: 组件渲染 ---');
  await jsThread.renderComponent(appComponent);
  
  // 等待渲染完成
  await new Promise(resolve => setTimeout(resolve, 100));
  
  // 5. 模拟用户交互
  console.log('\n--- 步骤 2: 用户交互 ---');
  nativeThread.simulateUserTouch('button1');
  
  // 等待事件处理
  await new Promise(resolve => setTimeout(resolve, 100));
  
  // 6. 显示最终状态
  console.log('\n--- 最终状态 ---');
  console.log('Bridge 统计:', bridge.getStats());
  console.log('原生组件:', nativeThread.nativeComponents.get('root'));
}

/**
 * 性能分析演示
 */
function analyzePerformance() {
  console.log('\n=== 性能分析 ===');
  
  const performanceMetrics = {
    jsThreadTime: '16ms (React reconciliation)',
    bridgeTime: '2ms (message serialization)', 
    nativeThreadTime: '8ms (UI rendering)',
    totalFrameTime: '26ms',
    targetFrameTime: '16.67ms (60 FPS)'
  };
  
  console.log('典型渲染周期分析:');
  Object.entries(performanceMetrics).forEach(([key, value]) => {
    console.log(`  ${key}: ${value}`);
  });
  
  console.log('\n性能优化建议:');
  console.log('1. 减少 Bridge 通信频次');
  console.log('2. 使用 InteractionManager 延迟非关键更新');
  console.log('3. 利用 shouldComponentUpdate 避免不必要渲染');
  console.log('4. 使用 FlatList 处理长列表');
}

// 运行演示
if (require.main === module) {
  demonstrateRNArchitecture().then(() => {
    analyzePerformance();
  });
}

module.exports = {
  JavaScriptThread,
  NativeUIThread,
  ReactNativeBridge
};

Open browser consoleTests

性能特点:

  • Bridge 通信是异步的,避免阻塞 UI 线程
  • 批量更新减少 Bridge 调用次数,提升性能
  • 原生组件直接渲染,获得接近原生应用的体验
  • Hot Reload 支持快速开发迭代

面试官视角:

该题考察对 React Native 底层架构的理解:

  • 要点清单: 理解三线程架构模型;掌握 Bridge 通信机制; 了解 Virtual DOM 到原生组件的映射;知道 RN 与原生开发的区别
  • 加分项: 有 RN 项目开发经验;了解性能优化策略; 知道 Hermes 引擎的作用;熟悉原生模块开发
  • 常见失误: 认为 RN 直接编译为原生代码;不理解异步通信的重要性; 混淆 RN 与 WebView 方案;忽视性能考虑

延伸阅读:

动画与手势应如何实现与优化?

答案

核心概念:

动画与手势若由 JS 线程驱动,易受阻塞影响。应尽量使用 原生驱动与工作线程(Reanimated)实现平滑 60fps 体验。

实践要点:

  • 动画库: react-native-reanimatedreact-native-animated
  • 手势库: react-native-gesture-handler 代替 RN 内置手势
  • 原生驱动: 复杂动画使用 Reanimated worklets 与 UI 线程执行
  • 协调: 手势状态机 + 动画中断/复位策略

示例说明:

// Reanimated v2 基础示例(示意)
import Animated, { useSharedValue, useAnimatedStyle,
withSpring } from 'react-native-reanimated'

const x = useSharedValue(0)
const style = useAnimatedStyle(() => ({ transform: [{ translateX: x.value }] }))
// onPress: x.value = withSpring(100)

面试官视角:

  • 要点清单: 原生驱动的必要性;手势与动画配合
  • 加分项: 复杂手势场景(列表侧滑/吸附)落地经验
  • 常见失误: JS 驱动长链路动画;忽视中断与回弹

延伸阅读:

布局与样式有哪些坑与最佳实践?

答案

核心概念:

RN 使用 Yoga 实现 Flexbox,样式非 CSS 全集;像素密度、 阴影、文本排版与安全区域等需专门适配。

实践要点:

  • Flex: 明确轴向、flexShrink/flexBasis、最小约束
  • 尺寸单位: DP 与像素密度;PixelRatio.get() 按需换算
  • 阴影与圆角: iOS shadow*/Android elevation 差异
  • 安全区域: SafeAreaView/react-native-safe-area-context
  • 文本: 逐行截断、行高、字重在两端差异

示例说明:

import { PixelRatio } from 'react-native'
const hairline = 1 / PixelRatio.get()

面试官视角:

  • 要点清单: Yoga 差异点;双端阴影与安全区
  • 加分项: 组件库规范化;动态字号/无障碍兼容
  • 常见失误: 依赖 Web CSS 心智;忽略多密度适配

延伸阅读:

网络与离线能力如何设计?

答案

核心概念:

移动网络不稳定,需具备重试、缓存、离线队列与冲突解决。

实践要点:

  • 请求层: 超时/重试/退避;统一错误码与埋点
  • 缓存层: SWR/Stale-While-Revalidate 思路;本地缓存
  • 离线: 队列累积 + 网络恢复重放;幂等设计
  • 推送同步: 重要数据用推送/轮询刷新

示例说明:

// 退避重试(示意)
for (let i = 0; i < 3; i++) try { return await fetch(url) } catch {}

面试官视角:

  • 要点清单: 不稳定网络策略;一致性与幂等
  • 加分项: 离线表单/冲突合并经验
  • 常见失误: 只做简单失败重试;无监控

延伸阅读:

调试与性能诊断如何落地?

答案

核心概念:

通过 Flipper、Perf Monitor、Systrace/Trace,定位 JS 与 原生瓶颈,量化优化效果。

实践要点:

  • Flipper: 网络、布局、崩溃、性能插件
  • Profiling: JS CPU profile、堆快照
  • Trace: Android Systrace、iOS Instruments
  • 指标: 启动、掉帧、内存、交互延迟

示例说明:

// 开启 RN Perf Monitor(开发)
// Dev Menu → Performance Monitor

面试官视角:

  • 要点清单: 工具链使用熟练;可量化定位
  • 加分项: 建立团队度量看板;基准测试
  • 常见失误: 仅凭体验;缺数据支撑

延伸阅读:

本地存储方案如何取舍?

答案

核心概念:

根据性能/安全/结构化需求选择 AsyncStorage、MMKV、 SQLite/Realm/WatermelonDB 等。

实践要点:

  • KV: AsyncStorage(通用);MMKV(高性能、C++)
  • 结构化: Realm/SQLite/WatermelonDB 同步/索引
  • 加密: Keychain/Keystore + 本地加密

示例说明:

import MMKV from 'react-native-mmkv'
const storage = new MMKV(); storage.set('k', 'v')

面试官视角:

  • 要点清单: 与业务匹配的选型与迁移
  • 加分项: 数据迁移/同步冲突处理
  • 常见失误: 大对象塞入 AsyncStorage;未加密

延伸阅读:

错误处理与崩溃治理如何做?

答案

核心概念:

JS 错误与原生崩溃需分别捕获与上报;对用户友好降级。

实践要点:

  • JS: Error Boundary、全局未捕获与 Promise rejection
  • 原生: 崩溃收集(Sentry/Firebase Crashlytics)
  • 回退: 关键功能降级/回滚;CodePush 回滚

示例说明:

// 简化版 Error Boundary
class EB extends React.Component { componentDidCatch(){/*report*/} render(){
return this.props.children }
}

面试官视角:

  • 要点清单: 分层收集与隔离;用户可感知的恢复
  • 加分项: 回滚链路自动化;错误聚类
  • 常见失误: 只上报不回滚;错误打爆日志

延伸阅读:

应用体积如何优化?

答案

核心概念:

体积影响下载转化与冷启动。通过资源压缩、代码瘦身与分割 交付降低包体。

实践要点:

  • 资源: WebP/AVIF、按密度打包、移除未用资源
  • 代码: Hermes、Proguard/R8、deadcode 移除
  • 分发: Android App Bundle,iOS Bitcode(历史)与 Slices

面试官视角:

  • 要点清单: 资源/代码/分发三层优化
  • 加分项: 量化体积与启动收益
  • 常见失误: 资源未清理;多架构全部打入

延伸阅读:

  • App Size — 相关分发与签名文档

Monorepo 与多包协作如何落地?

答案

核心概念:

通过 Yarn Workspaces/Turbo/PNPM 管理多包,Metro/Gradle/ Pods 需相应配置。

实践要点:

  • Metro: watchFolders/resolver 适配 workspace
  • Gradle/Pods: 本地依赖/版本对齐/缓存
  • 发布: 内部包版本策略与变更日志

示例说明:

// metro.config.js(示意)
module.exports = { watchFolders: [path.resolve(__dirname, '../packages')] }

面试官视角:

  • 要点清单: 构建链路完整与稳定性
  • 加分项: 复杂依赖树治理;多应用共用组件库
  • 常见失误: Metro 未配置导致解析失败

延伸阅读:

推送通知与消息到达如何实现?

答案

核心概念:

推送包含系统通知与应用内消息,需处理权限、到达率与 多厂商通道。

实践要点:

  • 服务: FCM/APNs,国内多厂商聚合
  • 权限: iOS 权限弹窗策略与引导
  • 到达: 前后台/杀进程、离线消息拉取
  • 埋点: 展示/点击/转化闭环

面试官视角:

  • 要点清单: 权限与到达;国内外通道差异
  • 加分项: 富媒体通知与深链联动
  • 常见失误: 权限弹窗时机错误;忽视渠道到达差异

延伸阅读:

RN 状态管理如何选择与落地?

答案

核心概念:

RN 可选方案包括 Redux/Toolkit、Recoil、Zustand、MobX, 以及 Context + hooks。选择取决于数据流复杂度、团队经验、 可调试性与生态。

实践要点:

  • Redux Toolkit: 规约化、可预测、生态完善、DevTools 友好
  • Zustand: 轻量、无样板、选择器减少重渲染
  • Recoil: 原子化依赖关系,细粒度更新
  • Context: 仅限轻量场景,避免全量重渲染

示例说明:

// Zustand store 示例(避免全量重渲染)
import create from 'zustand'

type State = { count: number; inc: () => void }
export const useCounter = create<State>((set) => ({
count: 0,
inc: () => set((s) => ({ count: s.count + 1 }))
}))

面试官视角:

  • 要点清单: 能结合规模/调试/性能做方案权衡
  • 加分项: 拆分 selector、避免重渲染的实践经验
  • 常见失误: 滥用 Context;缺少持久化与恢复策略

延伸阅读:

图片与资源加载如何优化?

答案

核心概念:

移动端瓶颈在于网络延迟、解码与内存。策略包含缓存、渐进、 占位与尺寸裁剪,必要时采用原生库。

实践要点:

  • 缓存: react-native-fast-image、分辨率按需
  • 占位与渐进: 先显示低清,再替换高清
  • 内存控制: 控制并发、列表卸载、避免巨大位图
  • 预加载: 关键图片/字体启动前预热

示例说明:

// FastImage 渐进占位
import FastImage from 'react-native-fast-image'

<FastImage
source={{ uri: highRes }}
defaultSource={require('./placeholder.png')}
resizeMode={FastImage.resizeMode.cover}
/>

面试官视角:

  • 要点清单: 缓存/占位/尺寸/并发的全链路意识
  • 加分项: OOM 实战排查;首屏关键资源策略
  • 常见失误: 原图直出;在列表中不做回收

延伸阅读:

RN 测试体系如何搭建?

答案

核心概念:

单测(Jest)、组件测试(react-native-testing-library)、 端到端(Detox)。目标是覆盖关键逻辑、可交互流程与回归用例。

实践要点:

  • Jest: 纯函数/hooks 单测、网络与时间 mock
  • RNTL: 可访问性查询、模拟用户交互
  • Detox: 真机/模拟器 E2E、CI 并行稳定性

示例说明:

// RNTL 交互测试示例
import { render, fireEvent } from '@testing-library/react-native'

test('increase counter', () => {
const { getByText } = render(<Counter />)
fireEvent.press(getByText('Plus'))
expect(getByText('1')).toBeTruthy()
})

面试官视角:

  • 要点清单: 分层测试策略;Mock 与稳定性
  • 加分项: CI 集成 Detox;快照与可访问性检查
  • 常见失误: 仅快照;E2E 不稳定无隔离

延伸阅读:

答案

核心概念:

通过 URL 触发应用打开并路由到指定页面。iOS 使用 Universal Links,Android 使用 App Links/Intent 过滤器。

实践要点:

  • 路由层解析参数并去抖
  • Web 与 App 互跳、回跳协议
  • 推送/分享/营销链接收口到统一解析

示例说明:

// React Navigation deep link 配置(示意)
const linking = {
prefixes: ['myapp://', 'https://example.com'],
config: { screens: { Profile: 'u/:id' } }
}

面试官视角:

  • 要点清单: 配置、解析、容错与安全
  • 加分项: H5/App/小程序一体化链接治理
  • 常见失误: 未考虑历史回退/多实例

延伸阅读:

权限与后台任务如何处理?

答案

核心概念:

权限请求需遵循平台规范并提供解释文案;后台任务涉及 电量与平台限制,需最小化策略。

实践要点:

  • 权限: react-native-permissions 统一封装
  • 后台: 背景定位/下载/任务调度按平台限制实现
  • 隐私: 收集前告知/最小化、系统设置跳转

示例说明:

import { check, request, PERMISSIONS } from 'react-native-permissions'

await request(PERMISSIONS.IOS.CAMERA)

面试官视角:

  • 要点清单: 合规、文案、失败与兜底
  • 加分项: 后台策略与降级;异常监控
  • 常见失误: 直接拒绝未兜底;过度后台

延伸阅读:

可访问性与国际化如何实践?

答案

核心概念:

可访问性提高可用性与合规;国际化涉及多语言、方向与 本地化格式化。

实践要点:

  • A11y: accessibilityLabel、可聚焦、触达区域、对比度
  • I18n: react-intl/i18next、RTL 支持与动态切换
  • 测试: TalkBack/VoiceOver 回归用例

示例说明:

<Button accessibilityLabel="submit order" title="Submit" />

面试官视角:

  • 要点清单: A11y 与 I18n 的工程系统化
  • 加分项: 可访问性自动化校验;RTL 实战
  • 常见失误: 仅文案翻译;忽视语义与读屏

延伸阅读:

安全与数据保护的最佳实践?

答案

核心概念:

移动端安全关注数据存储、传输与运行环境。RN 需结合 原生能力与后端配合。

实践要点:

  • 存储: Keychain/Keystore、安全区、避免明文
  • 网络: TLS pinning、超时与重试、请求签名
  • 环境: 越狱/Root 检测、调试开关、代码混淆
  • 日志: 不打敏感数据;崩溃与性能上报脱敏

面试官视角:

  • 要点清单: 端到端的威胁建模;最小化原则
  • 加分项: Pinning 与签名落地;反调试经验
  • 常见失误: Token 落地明文;日志泄露

延伸阅读:

CI/CD 与构建交付如何设计?

答案

核心概念:

统一版本、自动化打包签名、静态检查、测试与发布流程, 兼顾多环境与多渠道。

实践要点:

  • 管道: Lint/TypeCheck → 单测 → 构建 → E2E → 发布
  • 缓存: Metro/Gradle/CocoaPods 缓存
  • 密钥: 安全存放、短有效期、最小权限
  • Release: 灰度、回滚与符号表管理

面试官视角:

  • 要点清单: 稳定、可重复与可观测
  • 加分项: 多仓/Monorepo;差异化构建
  • 常见失误: 缓存不当导致非确定性;密钥泄漏

延伸阅读:

RN 的跨平台能力与局限是什么?

答案

核心概念:

React Native 通过 JS 运行时 + Bridge 将同一套业务逻辑复用到 iOS/Android。UI 层使用原生控件渲染,性能接近原生。跨平台并非 “一次编写到处运行”,平台差异与生态差异仍需工程化治理。

能力与边界:

  • 可共享: 业务逻辑、状态管理、网络、数据层、通用组件库
  • 平台差异: 权限体系、导航栈、手势、输入法、文件系统
  • UI 差异: 字体排版、阴影、滚动弹性、系统样式与动态颜色
  • 硬件能力: 相机蓝牙 NFC 等需依赖原生模块,兼容性各异
  • 生态差异: 三方库维护水平不一,需评估稳定性与安全性

示例说明:

// 简单的跨平台分支与样式适配示例
import { Platform } from 'react-native'

export const isIOS = Platform.OS === 'ios'
export const Touchable = isIOS
? require('react-native').TouchableOpacity
: require('react-native-gesture-handler').TouchableOpacity

面试官视角:

该题检验对跨平台“边界”的认知与工程取舍。

  • 要点清单: 明确共享与不可共享的层次;能举出典型差异; 说明适配策略与测试策略
  • 加分项: 形成可复用的适配封装;多包体多渠道交付经验; 端能力降级与兜底策略
  • 常见失误: 认为 100% 共享;忽视输入法、权限、布局差异; 滥用三方库不做评估

延伸阅读:

新架构 Fabric / TurboModule 与旧架构对比?

答案

核心概念:

新架构以 JSI 为基础,取消传统异步字符串序列化的 Bridge, 引入 Fabric 渲染管线与 TurboModule 原生模块系统, 实现更快的同步/异步交互与更低开销。

对比要点:

  • 通信: 旧 Bridge(JSON 串) → JSI(直接对象/函数)
  • 渲染: UIManager → Fabric(更细粒度、并发友好)
  • 模块: NativeModule → TurboModule(Codegen、类型安全)
  • 性能: 启动更快、交互延迟更低、内存占用更小
  • 迁移: 打开新架构开关、升级库、按需改造自定义模块

示例说明:

// TurboModule 类型定义 (示意)
export interface Spec {
getDeviceName(): Promise<string>
}

面试官视角:

  • 要点清单: 说清 JSI/Fabric/TurboModule 的关系; 性能收益点;迁移风险点
  • 加分项: 有新架构落地经验;能解释 Codegen 流程与类型安全
  • 常见失误: 把 JSI 等同“全部同步”;忽视三方库兼容性

延伸阅读:

RN 性能优化实战有哪些?

答案

核心概念:

性能瓶颈常见于渲染频繁、列表场景、手势动画与启动链路。 原则是“减少渲染、低成本通信、原生驱动动画、按需加载”。

优化清单:

  • 重渲染控制: memouseCallbackuseMemo、 分离 Context 消费边界
  • 列表: FlatList/SectionList 合理 getItemLayoutwindowSizeremoveClippedSubviews
  • 动画: react-native-reanimateduseNativeDriver: true
  • 交互: react-native-gesture-handler 降低 JS 线程压力
  • 通信: 合并批量更新,减少桥消息数
  • 启动: 减少 JS 体积、启用 Hermes、延迟非关键初始化

示例说明:

// 控制重渲染示例
const Row = React.memo(({ item }: { item: Item }) => {
return <ItemCell item={item} />
})

面试官视角:

  • 要点清单: 能从“渲染、动画、通信、启动”四维度给出方案
  • 加分项: 现场定位与量化(Perf Monitor、Flipper、Systrace)
  • 常见失误: 一味乐观更新;忽视长列表窗口化与键盘交互

延伸阅读:

如何编写原生模块与原生 UI 组件?

答案

核心概念:

通过模块化接口把原生能力暴露给 JS 层。旧架构使用 NativeModule, 新架构推荐 TurboModule/Codegen, UI 层使用 Fabric 组件。

实现流程:

  • 定义接口: TypeScript/Flow 声明(供 Codegen)
  • 平台实现: iOS(ObjC/Swift),Android(Java/Kotlin)
  • 注册导出: 模块/视图管理器注册,导出到 JS
  • 测试发布: 示例 App 自测、E2E、版本策略

示例说明:

// JS 声明 (示意)
export interface DeviceSpec { getBatteryLevel(): Promise<number> }

面试官视角:

  • 要点清单: 清楚工程化流程与差异点;理解 Codegen 与类型边界
  • 加分项: 拥有跨端模块模板脚手架;发布多版本兼容
  • 常见失误: 同步 API 滥用;线程/生命周期处理不当

延伸阅读:

导航方案如何选择与取舍?

答案

核心概念:

主流为 React Navigation(JS 驱动) 与 react-native-navigation(Wix,原生驱动)。选择取决于性能、 可扩展与团队经验。

对比要点:

方案驱动性能动画生态
React NavigationJSReanimated丰富
RNN(Wix)原生原生偏少

面试官视角:

  • 要点清单: 能结合业务体量、转场复杂度给出选择理由
  • 加分项: 自定义转场/手势方案;Deep Link 与权限路由
  • 常见失误: 路由结构混乱、状态泄漏、硬返回冲突

延伸阅读:

打包发布与 OTA 有哪些要点?

答案

核心概念:

移动端交付涉及签名、打包变体、灰度与 OTA(CodePush)。需兼顾 商店合规与风险控制。

实践要点:

  • 签名与证书: iOS Profile/证书、Android keystore
  • 多环境/多渠道: scheme/flavor 管理配置
  • Hermes: 体积与启动收益评估
  • CodePush/OTA: 与商店条款、敏感变更边界
  • 可观测性: 崩溃/性能/业务指标(Flipper、Sentry、Apm)

面试官视角:

  • 要点清单: 交付流程清晰;能阐述 OTA 合规与回滚策略
  • 加分项: 自动化流水线、符号表管理、灰度策略
  • 常见失误: OTA 越权更新、密钥泄漏、渠道包混乱

延伸阅读:

Hermes 引擎的价值与限制是什么?

答案

核心概念:

Hermes 是面向 RN 的轻量 JS 引擎,支持预编译字节码、快速 启动、较低内存占用。但与 V8/JSCore 存在内建特性差异, 需留意三方依赖兼容性。

收益与权衡:

  • 启动: 字节码快启,减少解析时间
  • 内存: 小型设备更友好
  • 体积: 引擎体积 + 应用字节码总体需评估
  • 兼容: Intl/Proxy 等特性支持差异需验证

面试官视角:

  • 要点清单: 描述启动与内存收益;能给出不适用场景
  • 加分项: 线上切换评估数据;字节码缓存与多包体策略
  • 常见失误: 忽视 polyfill/Intl;误判对动画库的影响

延伸阅读:

JSBridge 是什么?原理是什么?

答案

核心概念:

JSBridge 是连接 JavaScript 和原生代码的通信桥梁, 在混合应用开发中实现 WebView 与原生功能的双向调用。 通过消息序列化、回调管理和异步队列机制, 让 Web 技术能够访问设备原生能力如相机、存储、定位等, 同时保持良好的性能和安全性。

通信机制:

  • JS → Native: JavaScript 通过注入对象或 URL Schema 调用原生方法
  • Native → JS: 原生代码通过 evaluateJavaScript 执行 WebView 中的 JS 函数
  • 消息序列化: JSON 格式传递复杂数据结构和参数
  • 回调管理: 使用唯一 ID 管理异步回调,确保结果正确返回
  • 错误处理: 完善的异常捕获和错误回调机制

示例说明:

// JSBridge 通信机制演示
// 模拟 WebView 与原生应用之间的双向通信

/**
 * 模拟 WebView 环境
 * 提供 JavaScript 端的 Bridge 接口
 */
class WebViewJSBridge {
  constructor() {
    this.callbacks = new Map();
    this.callbackId = 0;
    this.messageQueue = [];
    this.isReady = false;
  }
  
  // 初始化 Bridge
  init() {
    console.log('🌐 WebView: JSBridge 初始化');
    this.isReady = true;
    this.flushQueue();
  }
  
  // JavaScript 调用原生方法
  callNative(method, params, callback) {
    const callbackId = this.generateCallbackId();
    
    if (callback) {
      this.callbacks.set(callbackId, callback);
    }
    
    const message = {
      method,
      params,
      callbackId,
      timestamp: Date.now()
    };
    
    console.log('🌐 WebView: 调用原生方法', message);
    
    if (this.isReady) {
      this.sendToNative(message);
    } else {
      this.messageQueue.push(message);
    }
  }
  
  // 发送消息到原生端(模拟)
  sendToNative(message) {
    // 在真实环境中,这里会调用原生注入的方法
    // 比如 window.webkit.messageHandlers.bridge.postMessage(message)
    if (window.NativeBridge) {
      window.NativeBridge.handleMessage(JSON.stringify(message));
    }
  }
  
  // 接收来自原生的回调
  receiveFromNative(callbackId, result, error) {
    console.log('🌐 WebView: 收到原生回调', { callbackId, result, error });
    
    const callback = this.callbacks.get(callbackId);
    if (callback) {
      this.callbacks.delete(callbackId);
      
      if (error) {
        callback(new Error(error), null);
      } else {
        callback(null, result);
      }
    }
  }
  
  // 处理原生主动调用
  handleNativeCall(method, params) {
    console.log('🌐 WebView: 原生调用 JS 方法', { method, params });
    
    const handlers = {
      'updateUI': (data) => {
        console.log('🌐 WebView: 更新 UI', data);
        return { success: true, message: 'UI updated' };
      },
      'showAlert': (data) => {
        console.log('🌐 WebView: 显示警告', data.message);
        return { success: true };
      }
    };
    
    const handler = handlers[method];
    if (handler) {
      return handler(params);
    } else {
      throw new Error(`Unknown method: ${method}`);
    }
  }
  
  generateCallbackId() {
    return `cb_${++this.callbackId}_${Date.now()}`;
  }
  
  flushQueue() {
    while (this.messageQueue.length > 0) {
      const message = this.messageQueue.shift();
      this.sendToNative(message);
    }
  }
}

/**
 * 模拟原生应用端
 * 提供原生端的 Bridge 接口
 */
class NativeAppBridge {
  constructor() {
    this.webView = null;
    this.nativeModules = new Map();
    this.initNativeModules();
  }
  
  setWebView(webView) {
    this.webView = webView;
  }
  
  // 初始化原生模块
  initNativeModules() {
    // 设备信息模块
    this.nativeModules.set('DeviceInfo', {
      getDeviceInfo: () => ({
        platform: 'iOS',
        version: '15.0',
        model: 'iPhone 13',
        screenWidth: 375,
        screenHeight: 812
      })
    });
    
    // 存储模块
    this.nativeModules.set('Storage', {
      setItem: (key, value) => {
        console.log('📱 Native: 存储数据', { key, value });
        // 模拟异步存储
        return new Promise(resolve => {
          setTimeout(() => resolve({ success: true }), 100);
        });
      },
      getItem: (key) => {
        console.log('📱 Native: 获取数据', key);
        return new Promise(resolve => {
          setTimeout(() => resolve(`stored_value_for_${key}`), 100);
        });
      }
    });
    
    // 相机模块
    this.nativeModules.set('Camera', {
      openCamera: (options) => {
        console.log('📱 Native: 打开相机', options);
        return new Promise(resolve => {
          setTimeout(() => resolve({
            success: true,
            imagePath: '/tmp/captured_image.jpg',
            timestamp: Date.now()
          }), 1000);
        });
      }
    });
    
    // 地理位置模块
    this.nativeModules.set('Location', {
      getCurrentPosition: () => {
        console.log('📱 Native: 获取地理位置');
        return new Promise(resolve => {
          setTimeout(() => resolve({
            latitude: 40.7128,
            longitude: -74.0060,
            accuracy: 10,
            timestamp: Date.now()
          }), 500);
        });
      }
    });
  }
  
  // 处理来自 WebView 的消息
  async handleMessage(messageStr) {
    try {
      const message = JSON.parse(messageStr);
      console.log('📱 Native: 处理 WebView 消息', message);
      
      const { method, params, callbackId } = message;
      
      // 解析模块和方法名
      const [moduleName, methodName] = method.split('.');
      const module = this.nativeModules.get(moduleName);
      
      if (!module || !module[methodName]) {
        throw new Error(`Method not found: ${method}`);
      }
      
      // 调用原生方法
      const result = await module[methodName](params);
      
      // 回调给 WebView
      if (callbackId) {
        this.callWebViewCallback(callbackId, result, null);
      }
      
    } catch (error) {
      console.error('📱 Native: 处理消息错误', error.message);
      if (message.callbackId) {
        this.callWebViewCallback(message.callbackId, null, error.message);
      }
    }
  }
  
  // 调用 WebView 回调
  callWebViewCallback(callbackId, result, error) {
    if (this.webView) {
      // 在真实环境中,这里会执行 WebView 中的 JavaScript
      // 比如 webView.evaluateJavaScript(`window.JSBridge.receiveFromNative(...)`)
      this.webView.receiveFromNative(callbackId, result, error);
    }
  }
  
  // 原生主动调用 WebView 方法
  callWebView(method, params) {
    console.log('📱 Native: 调用 WebView 方法', { method, params });
    
    if (this.webView) {
      try {
        const result = this.webView.handleNativeCall(method, params);
        console.log('📱 Native: WebView 方法执行结果', result);
        return result;
      } catch (error) {
        console.error('📱 Native: WebView 方法执行失败', error.message);
        throw error;
      }
    }
  }
}

/**
 * JSBridge 使用示例
 */
class BridgeExample {
  constructor() {
    this.jsBridge = new WebViewJSBridge();
    this.nativeBridge = new NativeAppBridge();
    
    // 建立连接
    this.nativeBridge.setWebView(this.jsBridge);
    
    // 模拟原生注入
    window.NativeBridge = this.nativeBridge;
    
    this.jsBridge.init();
  }
  
  // 演示设备信息获取
  async demonstrateDeviceInfo() {
    console.log('\n--- 演示: 获取设备信息 ---');
    
    return new Promise((resolve) => {
      this.jsBridge.callNative('DeviceInfo.getDeviceInfo', {}, (error, result) => {
        if (error) {
          console.error('获取设备信息失败:', error.message);
        } else {
          console.log('设备信息:', result);
        }
        resolve();
      });
    });
  }
  
  // 演示数据存储
  async demonstrateStorage() {
    console.log('\n--- 演示: 数据存储 ---');
    
    // 存储数据
    await new Promise((resolve) => {
      this.jsBridge.callNative('Storage.setItem', 
        { key: 'user_id', value: '12345' }, 
        (error, result) => {
          if (error) {
            console.error('存储失败:', error.message);
          } else {
            console.log('存储成功:', result);
          }
          resolve();
        }
      );
    });
    
    // 获取数据
    await new Promise((resolve) => {
      this.jsBridge.callNative('Storage.getItem', 
        { key: 'user_id' }, 
        (error, result) => {
          if (error) {
            console.error('获取失败:', error.message);
          } else {
            console.log('获取到数据:', result);
          }
          resolve();
        }
      );
    });
  }
  
  // 演示相机功能
  async demonstrateCamera() {
    console.log('\n--- 演示: 相机功能 ---');
    
    return new Promise((resolve) => {
      this.jsBridge.callNative('Camera.openCamera', 
        { quality: 0.8, allowEdit: true }, 
        (error, result) => {
          if (error) {
            console.error('打开相机失败:', error.message);
          } else {
            console.log('拍照成功:', result);
          }
          resolve();
        }
      );
    });
  }
  
  // 演示原生主动调用
  demonstrateNativeToJS() {
    console.log('\n--- 演示: 原生主动调用 JS ---');
    
    // 模拟原生推送通知
    setTimeout(() => {
      this.nativeBridge.callWebView('showAlert', {
        message: '您有新消息',
        type: 'info'
      });
    }, 2000);
    
    // 模拟原生更新UI
    setTimeout(() => {
      this.nativeBridge.callWebView('updateUI', {
        type: 'badge',
        count: 5
      });
    }, 3000);
  }
  
  // 运行所有演示
  async runAllDemos() {
    console.log('=== JSBridge 通信演示 ===');
    
    await this.demonstrateDeviceInfo();
    await this.demonstrateStorage();
    await this.demonstrateCamera();
    this.demonstrateNativeToJS();
    
    console.log('\n=== 演示完成 ===');
  }
}

/**
 * JSBridge 安全性和性能考虑
 */
function analyzeBridgeConsiderations() {
  console.log('\n=== JSBridge 技术考量 ===');
  
  const considerations = {
    security: [
      '输入验证: 防止恶意 JavaScript 调用',
      '权限控制: 限制可调用的原生方法',
      '数据加密: 敏感数据传输加密',
      'CSP策略: 内容安全策略防护'
    ],
    performance: [
      '异步调用: 避免阻塞主线程',
      '批量处理: 合并多个小请求',
      '缓存机制: 缓存频繁访问的数据',
      '超时处理: 设置合理的超时时间'
    ],
    reliability: [
      '错误处理: 完善的错误回调机制',
      '重试机制: 关键操作支持重试',
      '降级策略: Bridge 失败时的备用方案',
      '版本兼容: 处理不同版本的兼容性'
    ]
  };
  
  Object.entries(considerations).forEach(([category, items]) => {
    console.log(`\n${category.toUpperCase()} 考虑:`);
    items.forEach(item => console.log(`  • ${item}`));
  });
}

// 运行演示
if (typeof window !== 'undefined') {
  // 浏览器环境
  window.BridgeExample = BridgeExample;
  window.runBridgeDemo = () => {
    const example = new BridgeExample();
    example.runAllDemos().then(() => {
      analyzeBridgeConsiderations();
    });
  };
  
  console.log('在浏览器控制台运行: runBridgeDemo()');
} else {
  // Node.js 环境
  const example = new BridgeExample();
  example.runAllDemos().then(() => {
    analyzeBridgeConsiderations();
  });
}

module.exports = {
  WebViewJSBridge,
  NativeAppBridge,
  BridgeExample
};

Open browser consoleTests

实现方式对比:

平台实现方式优点缺点
iOSWKWebView + MessageHandler性能好,安全性高iOS 8+ 支持
AndroidWebView + JavascriptInterface兼容性好安全风险需注意
统一方案PostMessage + URL Schema跨平台一致复杂度较高

安全考虑:

  • 输入验证: 严格验证来自 JS 的调用参数
  • 方法白名单: 只暴露必要的原生接口给 WebView
  • 权限控制: 敏感操作需要用户授权确认
  • 数据加密: 传输敏感信息时进行加密处理

面试官视角:

该题考察混合开发和跨平台通信的理解:

  • 要点清单: 理解 JSBridge 通信原理;掌握双向调用机制; 了解异步回调处理;知道安全性考虑
  • 加分项: 有混合应用开发经验;了解不同平台的实现差异; 知道性能优化方法;熟悉相关安全问题
  • 常见失误: 忽视异步特性导致的问题;不考虑安全性风险; 不理解回调管理的复杂性;混淆不同的实现方案

延伸阅读: