跳至主内容

1 篇文章 标记为 "engineering"

查看所有标签

全新 iOS WebView 组件发布

· 1 分钟阅读
Facebook 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

长期以来,Apple 一直建议开发者使用 WKWebView 替代 UIWebView。在即将发布的 iOS 12 中,UIWebView 将被正式弃用。由于 React Native 的 iOS WebView 实现重度依赖 UIWebView 类,基于这些变化,我们为 WebView React Native 组件开发了全新的 iOS 原生后端,采用 WKWebView 实现。

这些变更的最终代码已通过 此提交 合并,将在 0.57 版本中发布。

要启用这个新实现,请使用 useWebKit 属性:

<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>

改进亮点

UIWebView 原本缺乏有效机制来实现 WebView 内运行的 JavaScript 与 React Native 之间的通信。当消息从 WebView 发送时,我们依赖一种变通方案进行传递:先将消息数据编码到特殊协议的 URL 中,再导航 WebView 至该 URL。在原生端,我们拦截并取消此导航,解析 URL 中的数据,最终调用 React Native。这种实现方式不仅容易出错且存在安全隐患。很高兴向大家宣布,我们现已利用 WKWebView 的特性完全重构了此机制。

WKWebView 相较于 UIWebView 的其他优势包括更快的 JavaScript 执行速度和多进程架构。更多细节请参考 2014 年 WWDC

注意事项

如果您的组件使用了以下属性,切换到 WKWebView 时可能会遇到问题。目前建议避免使用这些属性:

行为不一致:

automaticallyAdjustContentInsetscontentInsets (提交记录)

WKWebView 添加 contentInsets 时,WKWebView 的视口(viewport)尺寸不会改变,仍保持与框架相同的尺寸。而 UIWebView 的实际视口尺寸会发生变化(当内容边距为正数时视口会缩小)。

backgroundColor (提交记录)

使用新的 iOS WebView 实现时,此属性可能导致背景色出现闪烁现象。此外,WKWebViewUIWebview 渲染透明背景的方式存在差异,具体细节请查看提交说明。

不受支持:

scalesPageToFit (提交记录)

由于 WKWebView 本身不支持 scalesPageToFit 属性,我们无法在 React Native 的 WebView 组件中实现此功能。

无障碍 API 更新

· 1 分钟阅读
Ziqi Chen
加州大学伯克利分校学生
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

背景动机

随着技术进步和移动应用在日常生活中日益重要,开发无障碍应用的需求也变得越来越迫切。

React Native 有限的无障碍 API 一直是开发者的痛点,因此我们对无障碍 API 进行了多项更新,让创建包容性移动应用更加便捷。

现有 API 的问题

问题一:两个相似却不同的属性 - accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

accessibilityComponentTypeaccessibilityTraits 用于告知 Android 的 TalkBack 和 iOS 的 VoiceOver 用户正在交互的 UI 元素类型。这两个属性存在两大问题:

  1. 功能相同却使用两套不同机制。旧 API 中这两个平台专属属性不仅使用不便,还给开发者带来困惑。iOS 的 accessibilityTraits 支持 17 种值,而 Android 的 accessibilityComponentType 仅支持 4 种值,且多数值不重叠。更麻烦的是输入格式也不同:accessibilityTraits 支持数组或单值,而 accessibilityComponentType 仅接受单值。

  2. Android 功能极其有限。旧属性下 TalkBack 只能识别 "button"、"radiobutton_checked" 和 "radiobutton_unchecked" 三种 UI 元素。

问题二:缺失无障碍提示

当无障碍标签无法明确表达操作结果时,无障碍提示能帮助使用 TalkBack 或 VoiceOver 的用户理解操作效果。这些提示可在设置中开关,但 React Native 的旧 API 完全不支持此功能。

问题三:忽略颜色反转

部分视障用户会开启手机颜色反转功能增强对比度。Apple 提供了允许开发者忽略特定视图的 iOS API,确保开启颜色反转时图片视频不变形,但 React Native 此前未支持该 API。

新 API 设计

解决方案一:整合 accessibilityComponentType (Android) 和 accessibilityTraits (iOS)

为解决 accessibilityComponentTypeaccessibilityTraits 之间的混淆问题,我们将其合并为单一属性。这符合逻辑——两者功能本质相同,合并后开发者无需再考虑平台差异。

技术背景

iOS 中,UIAccessibilityTraits 是可设置于任何 NSObject 的属性。通过 JavaScript 传到原生层的 17 种特性会映射到 Objective-C 的 UIAccessibilityTraits 元素。每个特性用长整型表示,多特性通过按位或(OR)组合。

Android 的 AccessibilityComponentType 则是 React Native 自创概念,不直接对应 Android 原生属性。Android 通过无障碍委托处理:每个视图有默认委托,如需定制需创建新委托并覆盖特定方法。当开发者设置 AccessibilityComponentType 时,原生代码会根据传入组件创建新委托。

具体变更

新属性旨在成为两属性的超集。我们决定新属性主要基于现有属性 accessibilityTraits 进行建模,因为 accessibilityTraits 的值要多得多。这些特性的功能将通过修改无障碍委托在 Android 上实现。

accessibilityTraits 在 iOS 支持 17 种值,但新属性未全采纳。因部分特性效果不明确,且许多值实际极少使用。

这些特性值通常体现两种用途:描述 UI 元素角色或状态。观察发现,开发者通常组合一个角色值(如 "button")与状态值(如 "selected" 或 "disabled")。因此我们拆分出两个新属性:accessibilityRoleaccessibilityState

accessibilityRole

新属性 accessibilityRole 用于向 TalkBack 或 VoiceOver 说明 UI 元素角色,支持以下值:

  • none

  • button

  • link

  • search

  • image

  • keyboardkey

  • text

  • adjustable

  • header

  • summary

  • imagebutton

此属性仅接受单值,因 UI 元素通常不承担多重角色。特例是图片(image)和按钮(button)的组合,故新增了 imagebutton 角色。

accessibilityStates

新属性 accessibilityStates 用于说明 UI 元素状态,接受包含以下一个或多个值的数组:

  • selected

  • disabled

解决方案二:添加无障碍提示

我们新增 accessibilityHint 属性,设置后 TalkBack 或 VoiceOver 将向用户朗读提示内容。

accessibilityHint

此属性接受字符串形式的无障碍提示内容。

iOS 中设置该属性会同步原生视图的 AccessibilityHint 属性,当 iPhone 开启无障碍提示时 VoiceOver 将朗读。

Android 的实现是将提示内容追加到无障碍标签末尾。这样做模拟了 iOS 的行为,但缺点是 Android 无法像 iOS 那样在设置中单独关闭提示。

此设计源于无障碍提示通常对应特定操作(如点击),我们希望保持跨平台行为一致。

问题三解决方案

accessibilityIgnoresInvertColors

我们将 Apple 的 AccessibilityIgnoresInvertColors API 暴露给 JavaScript。现在当您有不希望颜色反转的视图(如图片)时,设置此属性为 true 即可避免反转。

使用方式

这些新属性将在 React Native 0.57 版本中提供。

升级指南

若您当前正在使用 accessibilityComponentTypeaccessibilityTraits,可按以下步骤迁移到新属性。

方法一:使用 jscodeshift 工具

简单场景可通过运行 jscodeshift 脚本完成替换。

脚本可自动替换以下情况:

accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}

accessibilityRole= “trait”

该脚本还会移除 AccessibilityComponentType 实例(假设您每次设置 AccessibilityComponentType 时都会同时设置 AccessibilityTraits)。

方法二:手动代码替换

对于 AccessibilityTraits 使用无对应 AccessibilityRole 值的情况,以及将多个特征值传递到 AccessibilityTraits 的情况,需要手动修改。

通常而言,

accessibilityTraits= {[“button”, “selected”]}

需手动替换为

accessibilityRole=“button”
accessibilityStates={[“selected”]}

这些属性已在 Facebook 代码库中实际应用。令人惊喜的是,Facebook 的代码迁移相当简单:jscodeshift 脚本处理了约半数实例,其余手动修改,整个过程仅耗时数小时。

希望新版 API 能为您带来便利!请持续打造无障碍应用!#包容性设计

React Native 2018 年现状

· 1 分钟阅读
Sophie Alpert
Facebook React 工程经理
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

距离我们上次发布 React Native 的现状更新已有一段时间。

在 Facebook,我们比以往任何时候都更广泛地在重要项目中使用 React Native。其中最受欢迎的产品是 Marketplace——这是我们应用中每月被 8 亿用户使用的顶级标签页之一。自 2015 年创建以来,Marketplace 完全采用 React Native 构建,包含遍布应用不同部分的数百个全屏视图。

我们也在应用的许多新模块中使用 React Native。如果您上个月观看了 F8 主题演讲,会注意到献血、危机应对、隐私快捷设置和健康检查等功能——这些都是近期采用 React Native 构建的特性。在 Facebook 主应用之外的项目同样在使用 React Native。新款 Oculus Go VR 头显包含完全使用 React Native 构建的配套移动应用,更不用说头显内部许多体验都由 React VR 驱动。

当然,我们也使用其他多种技术构建应用。LithoComponentKit 是我们在应用中广泛使用的两个库;它们都提供类似 React 的组件 API 来构建原生界面。React Native 的目标从来不是取代所有其他技术——我们专注于改进 React Native 本身,但也乐于看到其他团队借鉴 React Native 的理念,例如将即时重载功能引入非 JavaScript 代码。

架构

2013 年启动 React Native 项目时,我们将其设计为在 JavaScript 和原生环境之间建立单一"桥接",该桥接具有异步、可序列化和批处理特性。正如 React DOM 将 React 状态更新转换为 document.createElement(attrs).appendChild() 等 DOM API 的命令式可变调用,React Native 被设计为返回列出待执行变更的单一 JSON 消息,例如 [["createView", attrs], ["manageChildren", ...]]。我们设计的整个系统从不依赖同步响应,并确保列表中所有内容都能完全序列化为 JSON 并还原。这样做是为了获得灵活性:基于此架构,我们得以构建诸如Chrome 调试等工具,该工具通过 WebSocket 连接异步运行所有 JavaScript 代码。

过去五年我们发现,这些初始原则使得某些功能的构建更加困难。异步桥接意味着无法将 JavaScript 逻辑直接集成到许多需要同步响应的原生 API 中。批处理桥接会对原生调用进行排队,导致 React Native 应用更难调用原生实现的函数。而可序列化桥接则意味着不必要的内存复制,而非直接在两个环境间共享内存。对于完全采用 React Native 构建的应用,这些限制通常可以接受。但对于 React Native 与现有应用代码需复杂集成的场景,这些限制令人沮丧。

我们正在进行 React Native 的大规模架构重构,以使框架更灵活,并与 JavaScript/原生混合应用中的原生基础设施更好集成。 通过该项目,我们将应用过去五年积累的经验,逐步将架构升级至更现代化的形态。我们正在重写 React Native 的许多内部实现,但大部分变更都在底层:现有 React Native 应用将继续运行,几乎不需要修改。

为了让 React Native 更轻量级并更好地融入现有原生应用,这次架构重构包含三大内部改进。首先,我们正在改变线程模型。用户界面更新不再需要在三个不同线程上执行工作,新架构允许在任何线程上同步调用 JavaScript 来处理高优先级更新,同时仍将低优先级任务移出主线程以保持响应能力。其次,我们将异步渲染能力整合到 React Native 中,以支持多级渲染优先级并简化异步数据处理。最后,我们正在简化桥接机制,使其更快速轻量:原生代码与 JavaScript 之间的直接调用将更高效,并便于构建跨语言堆栈追踪等调试工具。

这些改进完成后,更深入的集成将成为可能。当前若想整合原生导航/手势处理或 UICollectionView、RecyclerView 等原生组件,必须采用复杂技巧。线程模型改造后,构建此类功能将变得直接高效。

我们将在今年晚些时候这项工作接近完成时公布更多细节。

社区生态

除 Facebook 内部社区外,我们欣喜地看到外部 React Native 用户和贡献者生态蓬勃发展。我们期待通过优化开发者体验和降低贡献门槛,为社区提供更完善的支持。

正如架构改进能提升 React Native 与其他原生设施的协同能力,我们也致力于精简 JavaScript 侧架构:包括支持可替换的虚拟机与打包工具,使其更契合 JavaScript 生态系统。我们理解重大变更的更新节奏可能带来适配压力,因此将探索减少大版本更新的方案。同时针对启动优化等尚未系统文档化的专业领域,我们将补充更详实的指南。这些改进将在未来一年逐步落地。

只要您在使用 React Native,就是社区的重要成员——请继续告诉我们如何为您打造更出色的开发体验。

虽然 React Native 只是移动开发者工具箱中的选项之一,但我们坚信其价值——去年 500+ 贡献者提交的 2500 多次代码更新,正推动它日臻完善。

在 React Native 中使用 TypeScript

· 1 分钟阅读
Ash Furrow
Artsy 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

JavaScript!我们都爱它。但有些人也钟爱类型系统。幸运的是,有多种方案可以为 JavaScript 增加强类型支持。我最喜欢的是 TypeScript,不过 React Native 默认支持 Flow。选择哪种方案完全取决于个人偏好,它们各自有不同的方式为 JavaScript 添加类型的魔力。今天,我们将探讨如何在 React Native 应用中使用 TypeScript。

本文参考了微软的 TypeScript-React-Native-Starter 仓库指南。

更新:自本文撰写以来,配置过程变得更加简单。只需运行以下单个命令,即可替代本文描述的所有设置:

npx react-native init MyAwesomeProject --template react-native-template-typescript

不过 Babel 的 TypeScript 支持确实_存在_一些限制,上文提到的博文对此有详细说明。本文概述的步骤仍然有效,Artsy 在生产环境中仍在使用 react-native-typescript-transformer。但要在 React Native 和 TypeScript 中快速上手,使用上述命令是最便捷的方式。如有需要,之后可以随时切换。

无论如何,祝您使用愉快!原始博文继续如下。

准备工作

由于您可能在不同平台上开发,面向多种设备类型,基础设置可能较为复杂。您应首先确保能在不使用 TypeScript 的情况下运行普通的 React Native 应用。请按照 React Native 官网的说明开始操作。当您成功部署到设备或模拟器后,就可以开始创建 TypeScript React Native 应用了。

您还需要安装 Node.jsnpmYarn

初始化

当您尝试搭建好普通 React Native 项目后,就可以开始添加 TypeScript 了。让我们立即开始吧。

react-native init MyAwesomeProject
cd MyAwesomeProject

添加 TypeScript

下一步是将 TypeScript 添加到项目中。以下命令将:

  • 向项目添加 TypeScript

  • 添加 React Native TypeScript Transformer 到项目

  • 初始化空的 TypeScript 配置文件(我们稍后将配置)

  • 添加空的 React Native TypeScript Transformer 配置文件(我们稍后将配置)

  • 添加 React 和 React Native 的类型定义

好的,让我们运行这些命令:

yarn add --dev typescript
yarn add --dev react-native-typescript-transformer
yarn tsc --init --pretty --jsx react
touch rn-cli.config.js
yarn add --dev @types/react @types/react-native

tsconfig.json 文件包含 TypeScript 编译器的所有设置。上述命令创建的默认配置基本可用,但请打开文件并取消注释以下行:

{
/* Search the config file for the following line and uncomment it. */
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
}

rn-cli.config.js 包含 React Native TypeScript Transformer 的设置。打开该文件并添加以下内容:

module.exports = {
getTransformModulePath() {
return require.resolve('react-native-typescript-transformer');
},
getSourceExts() {
return ['ts', 'tsx'];
},
};

迁移至 TypeScript

将生成的 App.js__tests_/App.js 文件重命名为 App.tsxindex.js 需要保留 .js 扩展名。所有新文件应使用 .tsx 扩展名(如果文件不包含 JSX,则使用 .ts)。

如果此时尝试运行应用,可能会遇到类似 object prototype may only be an object or null 的错误。这是因为在同一行同时导入 React 的默认导出和命名导出时出现了问题。请打开 App.tsx 文件并修改顶部导入语句:

-import React, { Component } from 'react';
+import React from 'react'
+import { Component } from 'react';

部分问题源于 Babel 和 TypeScript 处理 CommonJS 模块的方式差异。未来两者将统一为相同的行为模式。

至此,您应该能够正常运行 React Native 应用了。

添加 TypeScript 测试环境

React Native 默认集成 Jest,要在 TypeScript 环境下测试 React Native 应用,我们需要安装 ts-jestdevDependencies

yarn add --dev ts-jest

接着打开 package.json 文件,将 jest 字段替换为以下配置:

{
"jest": {
"preset": "react-native",
"moduleFileExtensions": [
"ts",
"tsx",
"js"
],
"transform": {
"^.+\\.(js)$": "<rootDir>/node_modules/babel-jest",
"\\.(ts|tsx)$": "<rootDir>/node_modules/ts-jest/preprocessor.js"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$",
"testPathIgnorePatterns": [
"\\.snap$",
"<rootDir>/node_modules/"
],
"cacheDirectory": ".jest/cache"
}
}

这将配置 Jest 使用 ts-jest 处理 .ts.tsx 文件。

安装依赖类型声明

为了获得最佳的 TypeScript 开发体验,我们需要让类型检查器理解依赖项的结构和 API。部分库会在发布包中包含 .d.ts 文件(类型声明/类型定义文件),这些文件能描述底层 JavaScript 的结构。对于其他库,我们需要显式安装 @types/ npm 作用域下的对应类型包。

例如,这里我们需要为 Jest、React、React Native 和 React Test Renderer 安装类型声明:

yarn add --dev @types/jest @types/react @types/react-native @types/react-test-renderer

我们将这些类型声明包保存为开发依赖,因为这是一个 React Native 应用,仅在开发阶段使用这些依赖而非运行时。如果是要发布到 NPM 的库,可能需要将部分类型依赖添加为常规依赖。

您可在此处深入了解如何获取 .d.ts 文件

忽略更多文件

在版本控制中,建议忽略 .jest 文件夹。如果使用 git,直接在 .gitignore 文件中添加条目即可:

# Jest
#
.jest/

建议此时将文件提交到版本控制系统作为检查点。

git init
git add .gitignore # import to do this first, to ignore our files
git add .
git commit -am "Initial commit."

添加组件

现在让我们为应用添加组件。创建 Hello.tsx 组件文件——虽然这是个教学示例组件而非实际应用代码,但能展示如何在 React Native 中使用 TypeScript 编写非简单组件。

创建 components 目录并添加以下示例:

// components/Hello.tsx
import React from 'react';
import {Button, StyleSheet, Text, View} from 'react-native';

export interface Props {
name: string;
enthusiasmLevel?: number;
}

interface State {
enthusiasmLevel: number;
}

export class Hello extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

if ((props.enthusiasmLevel || 0) <= 0) {
throw new Error(
'You could be a little more enthusiastic. :D',
);
}

this.state = {
enthusiasmLevel: props.enthusiasmLevel || 1,
};
}

onIncrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel + 1,
});
onDecrement = () =>
this.setState({
enthusiasmLevel: this.state.enthusiasmLevel - 1,
});
getExclamationMarks = (numChars: number) =>
Array(numChars + 1).join('!');

render() {
return (
<View style={styles.root}>
<Text style={styles.greeting}>
Hello{' '}
{this.props.name +
this.getExclamationMarks(this.state.enthusiasmLevel)}
</Text>

<View style={styles.buttons}>
<View style={styles.button}>
<Button
title="-"
onPress={this.onDecrement}
accessibilityLabel="decrement"
color="red"
/>
</View>

<View style={styles.button}>
<Button
title="+"
onPress={this.onIncrement}
accessibilityLabel="increment"
color="blue"
/>
</View>
</View>
</View>
);
}
}

// styles
const styles = StyleSheet.create({
root: {
alignItems: 'center',
alignSelf: 'center',
},
buttons: {
flexDirection: 'row',
minHeight: 70,
alignItems: 'stretch',
alignSelf: 'center',
borderWidth: 5,
},
button: {
flex: 1,
paddingVertical: 0,
},
greeting: {
color: '#999',
fontWeight: 'bold',
},
});

哇!内容有点多,我们来分解说明:

  • 我们不再渲染 divspanh1 等 HTML 元素,而是渲染 ViewButton 等组件。这些是跨平台工作的原生组件。

  • 样式使用 React Native 提供的 StyleSheet.create 函数定义。React 的样式表允许我们通过 Flexbox 控制布局,并使用类似 CSS 的语法进行样式设计。

添加组件测试

组件创建完成后,让我们为其编写测试。

我们已安装 Jest 作为测试运行器。接下来将为组件编写快照测试,需要安装快照测试所需的附加组件:

yarn add --dev react-addons-test-utils

现在在 components 目录下创建 __tests__ 文件夹,并为 Hello.tsx 添加测试:

// components/__tests__/Hello.tsx
import React from 'react';
import renderer from 'react-test-renderer';

import {Hello} from '../Hello';

it('renders correctly with defaults', () => {
const button = renderer
.create(<Hello name="World" enthusiasmLevel={1} />)
.toJSON();
expect(button).toMatchSnapshot();
});

首次运行测试时,它会创建渲染组件的快照并存储在 components/__tests__/__snapshots__/Hello.tsx.snap 文件中。当您修改组件后,需要更新快照并检查是否有意外变更。您可以在这里了解更多关于测试 React Native 组件的内容。

后续计划

请查看官方 React 教程和状态管理库 Redux。这些资源在开发 React Native 应用时非常有用。此外,您还可以了解 ReactXP,这是一个完全用 TypeScript 编写的组件库,同时支持 Web 端 React 和 React Native。

祝您在类型更安全的 React Native 开发环境中玩得开心!

为 React Native 构建 <InputAccessoryView>

· 1 分钟阅读
Peter Argany
Facebook 软件工程师
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

背景动机

三年前,有人提交了一个 GitHub issue,要求 React Native 支持 input accessory view。

在随后的几年里,这个问题收到了无数的 "+1" 和各种变通方案,但 RN 本身却没有任何实质改变——直到今天。我们从 iOS 平台开始,公开了访问原生 input accessory view 的接口,并很高兴分享我们的实现方案。

背景知识

究竟什么是 input accessory view?阅读 Apple 开发者文档可知,它是一个自定义视图,当接收者成为第一响应者时,可以固定在系统键盘顶部。任何继承自 UIResponder 的对象都可重新声明 .inputAccessoryView 属性,并在此管理自定义视图。响应者基础架构会挂载该视图,并使其与系统键盘保持同步。用于关闭键盘的手势(如拖动或点击)会在框架层应用于 input accessory view。这让我们能实现交互式键盘收起功能——这是 iMessage 和 WhatsApp 等顶级通讯应用的核心特性。

将视图固定在键盘顶部有两种常见场景:第一种是创建键盘工具栏,例如 Facebook 发帖编辑器的背景选择器。

在此场景中,键盘聚焦于文本输入框,input accessory view 用于提供额外的键盘功能。这些功能与输入框类型相关:地图应用中可能是地址建议,文本编辑器中则可能是富文本格式工具。


在此场景中,拥有 <InputAccessoryView> 的 Objective-C UIResponder 很明确:当 <TextInput> 成为第一响应者时,底层会变成 UITextViewUITextField 实例。

第二种常见场景是粘性文本输入框:

此时文本输入框本身是 input accessory view 的组成部分。这常见于消息应用,用户可在滚动查看历史消息的同时编写新消息。


此例中谁拥有 <InputAccessoryView>?还能是 UITextViewUITextField 吗?文本输入框竟_位于_ input accessory view 内部,这形成了循环依赖。单独解决这个问题就值得另写一篇博客。剧透:拥有者是一个通用的 UIView 子类,我们会手动触发它的 becomeFirstResponder 方法。

接口设计

现在我们知道 <InputAccessoryView> 是什么以及如何使用它。下一步是设计一个同时满足两种场景的接口,并与现有 React Native 组件(如 <TextInput>)良好协作。

对于键盘工具栏,我们需要考虑以下几点:

  1. 需要能将任意 React Native 视图结构提升到 <InputAccessoryView>

  2. 这个独立视图结构需能接收触摸事件并操作应用状态

  3. 需要将 <InputAccessoryView> 关联到特定 <TextInput>

  4. 需要能在多个文本输入框间共享 <InputAccessoryView> 而无需重复代码

我们可以使用类似于 React portals 的概念来实现 #1。在这种设计中,我们将 React Native 视图"传送"到由响应者基础设施管理的 UIView 层次结构中。由于 React Native 视图会渲染为 UIViews,这实际上相当直接——我们只需重写:

- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex

并将所有子视图传输到新的 UIView 层次结构中。对于 #2,我们为 <InputAccessoryView> 设置一个新的 RCTTouchHandler。状态更新通过常规事件回调实现。对于 #3 和 #4,我们在创建 <TextInput> 组件时,使用 nativeID 字段在原生代码中定位辅助视图的 UIView 层次结构。该函数利用底层原生文本输入的 .inputAccessoryView 属性,从而在 ObjC 实现中有效地将 <InputAccessoryView><TextInput> 关联起来。

支持粘性文本输入(场景二)带来额外约束。这种设计中输入辅助视图本身包含文本输入子元素,因此无法通过 nativeID 关联。我们改为将通用离屏 UIView.inputAccessoryView 属性设置为我们原生的 <InputAccessoryView> 层次结构。通过手动使该通用 UIView 成为第一响应者,响应者基础设施便会挂载该层次结构。此概念在前述博客文章中有详细阐述。

陷阱与解决方案

当然,在构建此 API 的过程中并非一帆风顺。以下是我们遇到的一些陷阱及其解决方案。

最初的构建方案涉及监听 NSNotificationCenter 的 UIKeyboardWill(Show/Hide/ChangeFrame) 事件。该模式在某些开源库和 Facebook 应用内部模块中使用。遗憾的是,在滑动操作时,UIKeyboardDidChangeFrame 事件未能及时触发以更新 <InputAccessoryView> 的帧位置。此外,键盘高度变化也不会被这些事件捕获,导致如下缺陷:

在 iPhone X 上,文本键盘与表情键盘高度不同。依赖键盘事件操控文本输入框的应用都需修复此缺陷。我们的解决方案是坚持使用 .inputAccessoryView 属性,这意味着响应者基础设施会处理此类帧更新。


另一个棘手问题是避免与 iPhone X 的 Home 指示条重叠。您可能认为:"苹果为此专门开发了 safeAreaLayoutGuide,这很简单!" 我们最初也如此天真。首要问题是原生 <InputAccessoryView> 实现在即将显示前没有可锚定的窗口。虽然可通过重写 -(BOOL)becomeFirstResponder 强制执行布局约束,但遵循这些约束会将辅助视图上移后,新问题又会出现:

输入辅助视图成功避开 Home 指示条后,不安全区域后的内容却变得可见。解决方案来自这个 radar。我将原生 <InputAccessoryView> 层次结构包裹在不遵循 safeAreaLayoutGuide 约束的容器中,该容器覆盖不安全区域的内容,而 <InputAccessoryView> 保持在安全边界内。


使用示例

以下示例构建了用于重置 <TextInput> 状态的键盘工具栏按钮:

class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}

render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}

仓库中提供了粘性文本输入的另一个示例

何时可以使用?

该功能的完整实现提交位于此处<InputAccessoryView> 将在即将发布的 v0.55.0 版本中提供。

祝键盘操作愉快 :)

在 React Native 中使用 AWS

· 1 分钟阅读
Richard Threlkeld
AWS Mobile 高级技术产品经理
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

AWS 作为云服务提供商在科技行业广为人知,其服务涵盖计算、存储和数据库技术,以及全托管的无服务器解决方案。AWS Mobile 团队一直与客户及 JavaScript 生态成员紧密合作,致力于让云端连接的移动和 Web 应用更安全、更易扩展,同时简化开发和部署流程。我们最初推出了完整的入门套件,近期还有更多新进展。

本篇博客将为 React 和 React Native 开发者介绍以下值得关注的技术:

  • AWS Amplify:面向云服务的 JavaScript 声明式开发库

  • AWS AppSync:具备离线和实时特性的全托管 GraphQL 服务

AWS Amplify

使用 Create React Native App 和 Expo 等工具可以轻松初始化 React Native 应用。但当您尝试将具体用例与基础设施服务匹配时,连接云端服务可能颇具挑战。例如,您的 React Native 应用可能需要上传照片:这些照片需要按用户隔离保护吗?这可能意味着您需要注册或登录流程。您希望使用自建用户目录还是社交媒体提供商?也许您的应用还需要在用户登录后调用包含定制业务逻辑的 API。

为帮助 JavaScript 开发者解决这些问题,我们发布了名为 AWS Amplify 的库。其设计按任务"类别"划分,而非基于 AWS 的具体实现。例如,若您需要用户注册登录后上传私有照片,只需在应用中引入 AuthStorage 类别:

import { Auth } from 'aws-amplify';

Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));

Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));

在上方代码示例中,您可以看到 Amplify 协助处理的常见任务,例如通过邮箱或短信使用多因素认证(MFA)验证码。当前支持的类别包括:

  • Auth:提供凭证自动化功能。开箱即用的实现使用 AWS 凭证进行签名,并采用 Amazon Cognito 的 OIDC JWT 令牌。支持常见功能如 MFA 特性。

  • Analytics:单行代码即可在 Amazon Pinpoint 中追踪认证/未认证用户。可根据需要扩展自定义指标或属性。

  • API:通过 AWS 签名版本 4 安全地与 RESTful API 交互。该模块在配合 Amazon API Gateway 的无服务器架构中表现优异。

  • Storage:简化了 Amazon S3 内容的上传、下载和列表操作。还可轻松按用户分组公开或私有数据。

  • Caching:跨 Web 应用和 React Native 的 LRU 缓存接口,采用特定实现的持久化方案。

  • i18n and Logging:提供国际化与本地化能力,以及调试和日志功能。

Amplify 的亮点之一是在设计中对特定编程环境编码了"最佳实践"。例如,我们发现客户和 React Native 开发者常为快速实现功能而在开发阶段走捷径,这些方案最终进入生产环境后,可能影响扩展性或安全性,迫使重构基础设施和代码。

我们帮助开发者避免此类问题的一个典型案例是AWS Lambda无服务器参考架构。这些架构展示了在构建后端时联合使用Amazon API Gateway和AWS Lambda的最佳实践。该模式已被编码集成到Amplify的API类别中。您可以使用此模式与多个REST端点交互,并将自定义业务逻辑所需的标头一直传递到Lambda函数。我们还发布了AWS Mobile CLI工具,用于为新建或现有React Native项目快速配置这些功能。只需通过npm安装,然后按配置提示操作即可:

npm install --global awsmobile-cli
awsmobile configure

另一个针对移动生态的编码化最佳实践是密码安全。默认的Auth类别实现利用Amazon Cognito用户池处理用户注册和登录。该服务采用安全远程密码协议(SRP)来保护用户认证过程。如果您有兴趣研究该协议的数学原理,会注意到在原始根上计算密码验证器生成群组时必须使用大质数。在React Native环境中JIT被禁用,导致此类安全计算中的BigInteger运算性能较低。为此我们发布了Android和iOS原生桥接模块,可直接集成到项目中:

npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js

令人振奋的是,Expo团队已将此功能纳入其最新SDK,您现在无需eject项目即可使用Amplify。

最后针对React Native(及React)开发,Amplify提供高阶组件(HOC)简化功能封装,例如实现应用注册登录功能:

import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';

Amplify.configure(aws_exports);

class App extends React.Component {
...
}

export default withAuthenticator(App);

基础组件<Authenticator />也支持完全自定义UI。它提供用户状态管理属性(如登录状态/MFA验证等待状态)及状态变更回调函数。

同样地,您会发现适用于不同场景的通用React组件。例如在Storage模块中展示Amazon S3所有私有图片时可自定义组件:

<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>

如前所示,您可以通过props控制组件的多种功能(如公开/私有存储选项)。某些UI组件甚至支持自动收集用户交互数据:

return <S3Album track/>

AWS Amplify采用约定优于配置的开发范式,支持全局初始化或按类别初始化。最快捷的方式是使用aws-exports文件,同时也支持开发者独立使用库对接现有资源。

如需深入了解设计理念并查看完整演示,请观看AWS re:Invent大会视频

AWS AppSync

在发布AWS Amplify后不久,我们还推出了AWS AppSync。这项全托管GraphQL服务同时具备离线和实时能力。尽管您可以在不同客户端编程语言(包括原生Android/iOS)中使用GraphQL,但它在React Native开发者中尤为流行——因为其数据模型完美契合单向数据流和组件层级结构。

AWS AppSync 让您能够连接到您自己 AWS 账户中的资源,这意味着您完全拥有并掌控自己的数据。这是通过数据源实现的,该服务支持 Amazon DynamoDBAmazon ElasticsearchAWS Lambda。您可以在单个 GraphQL API 中将多种功能(如 NoSQL 和全文搜索)组合成一个架构。AppSync 服务还能根据架构自动配置资源,因此即使您不熟悉 AWS 服务,只需编写 GraphQL SDL 并点击按钮,系统就能自动完成部署。

AWS AppSync 的实时功能通过基于事件的 GraphQL 订阅模式实现。由于订阅功能使用 GraphQL 指令在架构层进行控制,而架构可使用任意数据源,这意味着您可以通过 Amazon DynamoDB 和 Amazon Elasticsearch Service 的数据库操作触发通知,或通过 AWS Lambda 从基础设施其他部分触发。

与 AWS Amplify 类似,您可以在 AWS AppSync 的 GraphQL API 中使用企业级安全功能。该服务支持通过 API 密钥快速上手,但在生产环境中可无缝切换到 AWS IAM 或 Amazon Cognito 用户池的 OIDC 令牌。您可以在解析器层级通过类型策略控制访问权限,甚至能在运行时执行细粒度访问控制检查(例如验证用户是否为特定数据库资源所有者),还支持通过群组成员检查来执行解析器或数据库记录访问。

为帮助 React Native 开发者快速上手,AWS AppSync 控制台主页提供内置 GraphQL 示例架构。该示例会自动部署 GraphQL 架构、配置数据库表并连接查询/变更/订阅操作。我们还提供了可直接运行的 React Native 示例项目(以及 React 版本),帮助您在几分钟内同时启动客户端和云端组件。

使用 AWSAppSyncClient 非常简单,它直接集成 Apollo ClientAWSAppSyncClient 会自动处理 GraphQL API 的安全签名、离线功能以及订阅握手协商流程:

import AWSAppSyncClient from "aws-appsync";
import { Rehydrated } from 'aws-appsync-react';
import { AUTH_TYPE } from "aws-appsync/lib/link/auth-link";

const client = new AWSAppSyncClient({
url: awsconfig.graphqlEndpoint,
region: awsconfig.region,
auth: {type: AUTH_TYPE.API_KEY, apiKey: awsconfig.apiKey}
});

AppSync 控制台提供包含 GraphQL 终端节点、AWS 区域和 API 密钥的配置文件下载。您可配合 React Apollo 使用该客户端:

const WithProvider = () => (
<ApolloProvider client={client}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
);

接下来即可使用标准 GraphQL 查询:

query ListEvents {
listEvents{
items{
__typename
id
name
where
when
description
comments{
__typename
items{
__typename
eventId
commentId
content
createdAt
}
nextToken
}
}
}
}

上例展示了与 AppSync 示例应用架构的查询交互,不仅包含与 DynamoDB 的交互,还实现了数据分页(含加密令牌)以及 EventsComments 的类型关联。由于应用配置了 AWSAppSyncClient,数据会自动持久化离线存储,并在设备重连时同步更新。

您可以通过此技术深度解析视频查看客户端技术细节和 React Native 演示。

反馈

这些库背后的团队渴望了解这些库和服务对您的工作效果如何。他们还想了解我们还能做些什么,让您在使用云服务开发 React 和 React Native 应用时更加轻松。请在 GitHub 上联系 AWS Mobile 团队:AWS AmplifyAWS AppSync

在 React Native 中实现 Twitter 应用加载动画

· 1 分钟阅读
Eli White
Eli White
Software Engineer @ Meta
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

Twitter iOS 应用的加载动画让我非常喜欢。

当应用准备就绪时,Twitter 小鸟标志会优雅地展开,显露出应用界面。

我想探索如何在 React Native 中重现这个加载动画。


要理解构建方法,首先需要拆解加载动画的各个组成部分。观察细节最简单的方法是放慢速度观看。

这个动画包含几个关键部分需要我们实现:

  1. 小鸟标志的缩放

  2. 小鸟扩展时显示下方应用界面

  3. 最后阶段轻微缩小应用界面

我花了相当长时间才搞明白如何制作这个动画。

最初我有个错误假设:认为蓝色背景和小鸟是覆盖在应用上方的图层,随着小鸟放大逐渐变得透明从而显示下方应用。这种方法行不通,因为小鸟变透明后露出的会是蓝色背景,而不是应用界面!

幸运的是,亲爱的读者,您不必经历同样的挫折。本教程将带您直达关键实现部分!


正确的实现方式

在编写代码前,理解如何拆解这个效果很重要。为帮助可视化,我在 CodePen 上重建了这个效果(嵌入在下方段落中),您可以交互式查看不同图层。

这个效果包含三个主要图层:最底层是蓝色背景层。虽然它看起来像是在应用上方,实际位于最后方。

中间是纯白色图层。最前面则是我们的应用层。


这个动画的核心技巧是将 Twitter 标志作为 mask(遮罩),同时遮盖应用层和白色层。关于遮罩细节我不做深入探讨,网上有大量相关资源

在此背景下,遮罩的基本原理是:遮罩图像的不透明像素会显示被遮盖内容,而透明像素则隐藏被遮盖内容。

我们将 Twitter 标志作为遮罩,用它遮盖两个图层:纯白色层和应用层。

通过将遮罩放大到超过整个屏幕的尺寸来显示应用界面。

在遮罩放大过程中,我们逐渐增加应用层的不透明度,从而显示应用界面并隐藏后方的纯白层。最后阶段,我们将应用层从大于1的初始比例缩小至1。动画结束后隐藏非应用图层。

常言道一图胜千言。那么交互式可视化值多少词呢?点击"Next Step"按钮逐步查看动画效果。图层展示提供了侧视角度,网格线帮助可视化透明图层。

现在进入 React Native 实现

好的。既然我们理解了构建目标和动画原理,现在可以进入代码环节——这才是您阅读的真正目的。

实现的关键组件是 MaskedViewIOS,这是 React Native 的核心组件。

import {MaskedViewIOS} from 'react-native';

<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;

MaskedViewIOS 接收 maskElementchildren 两个 props。子元素会被 maskElement 遮罩。注意遮罩不必须是图片,它可以是任意视图。上述示例的效果是渲染蓝色视图,但仅通过 maskElement 的 "Basic Mask" 文字区域可见。我们刚刚创建了复杂的蓝色文字效果。

我们需要先渲染蓝色背景层,然后在顶部用 Twitter 标志遮罩来渲染应用层和白色层。

{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;

这样就能得到下图所示的层级结构。

动画实现部分

现在我们已经准备好所有组件,下一步是添加动画效果。为了让动画更流畅,我们将使用 React Native 的 Animated API。

Animated 允许我们在 JavaScript 中声明式地定义动画。默认情况下,这些动画在 JavaScript 线程运行,并逐帧通知原生层更新。但由于 JavaScript 无法保证每帧及时更新,可能导致掉帧(卡顿)—— 这绝非我们想要的效果!

Animated 提供了特殊机制避免卡顿:通过设置 useNativeDriver 标志,可以在动画开始时将定义从 JavaScript 发送到原生层,让原生端独立处理动画更新,无需每帧与 JavaScript 通信。useNativeDriver 的限制是只能修改特定属性(主要是 transformopacity),目前无法用 useNativeDriver 实现背景色等属性的动画。当然,未来我们会扩展支持范围,也欢迎提交 PR 来增加项目所需属性,让整个社区受益 😀。

为确保动画流畅,我们将在这些约束条件下工作。深入了解 useNativeDriver 底层原理,请参阅相关博客文章

分解动画步骤

我们的动画包含 4 个关键部分:

  1. 放大小鸟图标,逐渐显示应用和纯白层

  2. 淡入应用界面

  3. 轻微缩小应用界面

  4. 完成后隐藏白色层和蓝色层

Animated 提供两种主要动画定义方式:Animated.timing 可精确控制时长和缓动曲线;Animated.spring 则基于物理模型。使用 Animated.spring 时,你可以通过摩擦力和张力参数实现弹性效果。

由于多个动画需要同步进行(例如在遮罩展开中途开始淡入应用),且它们彼此关联,我们将使用 Animated.timing 配合单一 Animated.Value 实现。

Animated.Value 是封装动画状态值的容器。通常整个动画只需一个该值,大多数组件会将其存储在 state 中。

我们将动画视为随时间推进的连续过程:设置 Animated.Value 初始值为 0(0% 完成),最终值为 100(100% 完成)。

初始组件状态设置如下:

state = {
loadingProgress: new Animated.Value(0),
};

准备启动动画时,通知 Animated 将该值渐变至 100。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // This is important!
}).start();

接着我尝试估算动画各个部分在不同时间节点应有的参数值。下面列出了动画各组成部分的进度对应表,展示了随着时间推进各参数的理想变化值。

Twitter小鸟遮罩的初始缩放值应为1,在迅速放大前会先略微缩小。在动画进度10%时,其缩放值应为0.8,最终则放大至70。选择70这个值其实相当随意——它必须足够大才能完全显露下方内容,而60显然不够😀。有趣的是,这个最终值越大,缩放速度看起来就越快,因为必须在相同时间内达到更大倍数。经过反复调试才找到适合当前Logo的数值。不同尺寸的Logo/设备需要不同的最终缩放值,以确保完整显示屏幕内容。

应用界面需要保持不透明状态一段时间,至少要覆盖Logo缩小阶段。根据官方动画效果,我计划在小鸟放大到一半时开始显示应用界面,并快速完成过渡。因此在动画进度15%时开始显现,到30%时实现完全可见。

应用界面初始缩放值为1.1,随动画推进逐渐缩小至正常尺寸。

代码实现

上述过程本质是将整体动画进度映射到各独立元素的参数值。我们通过Animated的.interpolate方法实现这种映射。基于this.state.loadingProgress的插值计算,我们为动画的三个组成部分分别创建了对应的样式对象。

const loadingProgress = this.state.loadingProgress;

const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp means when the input is 30-100, output should stay at 1
}),
};

const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};

const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};

获得这些样式对象后,就可以在渲染前文提到的视图片段时应用它们。注意只有Animated.ViewAnimated.TextAnimated.Image能使用包含Animated.Value的样式对象。

const fullScreenBlueLayer = (
<View style={styles.fullScreenBlueLayer} />
);
const fullScreenWhiteLayer = (
<View style={styles.fullScreenWhiteLayer} />
);

return (
<View style={styles.fullScreen}>
{fullScreenBlueLayer}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Animated.Image
style={[styles.maskImageStyle, imageScale]}
source={twitterLogo}
/>
</View>
}>
{fullScreenWhiteLayer}
<Animated.View
style={[opacityClearToVisible, appScale, {flex: 1}]}>
{this.props.children}
</Animated.View>
</MaskedViewIOS>
</View>
);

太棒了!现在动画效果已符合预期,接下来只需清理不再需要的蓝色背景层和白色遮罩层。

要确定清理时机,我们需要监测动画完成状态。幸运的是,调用Animated.timing时,.start方法支持传入动画结束的回调函数。

Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true,
}).start(() => {
this.setState({
animationDone: true,
});
});

现在通过state中的状态值判断动画是否完成,我们可以据此控制蓝白图层的显隐逻辑。

const fullScreenBlueLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenBlueLayer]} />
);
const fullScreenWhiteLayer = this.state.animationDone ? null : (
<View style={[styles.fullScreenWhiteLayer]} />
);

大功告成!动画完美运行,结束后自动清理了辅助图层——我们成功复现了Twitter应用启动动画!

等等,我的代码不工作!

别担心,亲爱的读者。我也很反感那些只提供代码片段却不给完整源码的教程。

该组件已发布至npm,GitHub仓库名为react-native-mask-loader。您可以通过Expo在手机上直接体验:

延伸阅读/进阶挑战

  1. 这本Gitbook是学习Animated的绝佳资源,尤其适合已掌握React Native基础文档的开发者

  2. 实际Twitter动画在最后阶段会加速遮罩展开过程。尝试修改加载器,使用不同的缓动函数(或弹性动画)来模拟这种效果

  3. 当前遮罩的最终缩放值是硬编码的,在平板设备上可能无法完整显示内容。根据屏幕尺寸和图像大小动态计算最终缩放值会是很棒的PR改进

React Native 月刊 #6

· 1 分钟阅读
Tomislav Tenodi
Speck 创始人
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

React Native 月度会议仍在火热进行中!请务必查看本文底部的通知以了解下一期会议安排。

Expo

  • 恭喜 Devin AbbottHoussein Djirdeh 预发布了《Full Stack React Native》一书!该书通过构建多个小型应用带你系统学习 React Native。

  • 发布了首个(实验性)版本的 reason-react-native-scripts,帮助开发者轻松尝试 ReasonML

  • Expo SDK 24 已发布!该版本基于 React Native 0.51,包含多项新特性和改进:独立应用内置图片(无需首次加载缓存!)、图片处理 API(裁剪/缩放/旋转/翻转)、人脸识别 API、新版发布渠道功能(设置指定渠道的活跃版本并支持回滚)、用于追踪独立应用构建的网页仪表盘,以及修复了 OpenGL Android 实现与 Android 多任务处理的长期 bug 等。

  • 今年一月起,我们将向 React Navigation 投入更多资源。我们坚信仅使用 React 组件和 Animated、react-native-gesture-handler 等基础模块构建 React Native 导航完全可行且值得期待,并对规划中的改进充满期待。如果你想为社区贡献力量,请关注需要协助的 react-native-mapsreact-native-svg 项目!

Infinite Red

Microsoft

  • 已发起拉取请求,将 React Native Windows 核心桥接迁移至 .NET Standard 以实现操作系统无关性。期待更多 .NET Core 平台能基于此桥接扩展自定义线程模型、JavaScript 运行时和 UIManager(例如 JavaScriptCore、Xamarin.Mac、Linux Gtk# 和 Samsung Tizen 等方案)。

Wix

  • Detox

    • 为了扩大端到端测试规模,我们正在努力减少 CI 时间消耗,目前正在为 Detox 开发并行化支持功能。
    • 已提交拉取请求,支持自定义构建变体(flavor builds),以优化端到端测试的模拟环境。
  • DetoxInstruments

    • DetoxInstruments 的核心功能开发面临重大技术挑战:实现任意时刻 JavaScript 堆栈追踪需要定制 JSCore 以支持 JS 线程挂起机制。在 Wix 应用内部测试分析器时,我们获得了关于 JS 线程的宝贵洞察。
    • 该项目目前尚未达到稳定可用状态,但团队正积极投入研发,期待尽快发布正式公告。
  • React Native Navigation

    • V2 版本开发进度显著加速:此前仅由 1 名开发者投入 20% 时间维护,现已有 3 名开发者全职投入研发!
  • Android 性能优化

    • 将 React Native 内置的旧版 JSCore 替换为最新版本(基于 webkitGTK 项目尖端版本,采用定制化 JIT 配置),使 JS 线程性能提升 40%。下一步将编译其 64 位版本。该工作基于JSC Android 构建脚本,可通过此链接追踪当前进展。

后续会议安排

我们正讨论调整会议形式:未来可能聚焦单一特定主题(例如导航架构、React Native 模块独立仓库迁移、文档优化等)。这种方式有望最大化对 React Native 社区的贡献。该调整可能于下次会议实施,欢迎通过推特留言提出您希望探讨的主题。

React Native 月报 #5

· 1 分钟阅读
Tomislav Tenodi
Speck 创始人
非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

新一期 React Native 月度会议来了!让我们看看各个团队的最新进展。

Callstack

  • 我们持续优化 React Native 的持续集成系统。最重要的进展是将 CI 系统从 Travis 迁移到 CircleCI,为 React Native 建立了统一高效的 CI 流水线。

  • 我们举办了 Hacktoberfest - React Native 特别版活动,与参与者共同向开源项目提交了大量 Pull Request。

  • 我们持续推进 Haul 项目。上月发布的两个新版本已支持 webpack 3。未来计划增加 CRNAExpo 支持,并优化热模块替换功能。路线图已在 issue 跟踪器公开。欢迎提出改进建议或反馈意见!

Expo

  • 发布 Expo SDK 22(基于 React Native 0.49)并同步更新 CRNA

    • 包含改进的启动屏 API、基础 ARKit 支持、"DeviceMotion" API、iOS11 的 SFAuthenticationSession 支持等新特性
  • Snack 编辑器现支持多 JavaScript 文件,并可通过拖拽直接上传图片等资源

  • react-navigation 贡献代码,新增 iPhone X 支持

  • 重点优化大型 Expo 应用的开发体验:

    • 提供多环境部署支持:预发布环境、生产环境及自定义渠道。渠道将支持版本回滚和动态切换。欢迎通过 @expo_io 申请成为内测用户
    • 改进独立应用构建流程,在保持 OTA 更新能力的同时支持静态资源打包

Facebook

  • RTL 布局优化:

    • 引入方向感知样式:
      • 定位:
        • (left|right) → (start|end)
      • 外边距:
        • margin(Left|Right) → margin(Start|End)
      • 内边距:
        • padding(Left|Right) → padding(Start|End)
      • 边框:
        • borderTop(Left|Right)Radius → borderTop(Start|End)Radius
        • borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
        • border(Left|Right)Width → border(Start|End)Width
        • border(Left|Right)Color → border(Start|End)Color
    • 在 RTL 布局中,"left" 和 "right" 的原有含义将被逐步废弃。未来几个月将取消交换逻辑,确保 "left" 始终代表左侧,"right" 始终代表右侧。开发者可通过 I18nManager.swapLeftAndRightInRTL(false) 提前启用此变更
  • 我们正在为内部原生模块添加 Flow 类型注解,并利用这些类型生成 Java 接口和 ObjC 协议,原生实现必须遵循这些规范。我们希望该代码生成工具最早能在明年开源。

Infinite Red

  • 发布了新的开源工具,用于辅助 React Native 及其他项目开发。详情请见此处

  • 正在重构 Ignite,准备发布新版脚手架(代号:Bowser)

Shoutem

  • 优化 Shoutem 平台的开发流程。我们致力于简化从创建应用到实现首个自定义屏幕的全过程,大幅降低 React Native 新手的入门门槛。通过多次工作坊测试了新功能,同时改进了 Shoutem CLI 以支持新流程。

  • Shoutem UI 进行了多项组件优化和错误修复,并验证了与最新 React Native 版本的兼容性。

  • Shoutem 平台迎来多项重要更新,新集成功能已作为开源扩展项目开放使用。看到其他开发者积极参与扩展开发令人振奋,我们主动联系并提供技术指导支持。

下一次会议

下次会议定于 2017年12月6日(星期三)举行。如果您对会议成果改进有任何建议,欢迎随时在 Twitter 上联系我