跳到主要内容

31 篇帖子标记为“工程”

查看所有标签

可访问性 API 更新

·7 分钟阅读
Ziqi Chen
加州大学伯克利分校学生

动机

随着技术进步和移动应用在日常生活中变得越来越重要,创建可访问应用程序的必要性也随之增长。

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 中的任何属性。可访问性由可访问性委托处理。每个视图都有一个默认的可访问性委托。如果您想自定义任何可访问性操作,您必须创建一个新的可访问性委托,覆盖您想要自定义的特定方法,然后将您正在处理的视图的可访问性委托设置为与新委托关联。当开发者设置 AccessibilityComponentType 时,原生代码会根据传入的组件创建一个新的委托,并设置该视图以拥有该可访问性委托。

所做更改

对于我们的新属性,我们希望创建这两个属性的超集。我们决定将新属性主要基于现有属性 accessibilityTraits 进行建模,因为 accessibilityTraits 具有明显更多的值。Android 在这些特性上的功能将通过修改 Accessibility Delegate 来进行 polyfill。

iOS 上的 accessibilityTraits 可以设置为 UIAccessibilityTraits 的 17 个值。然而,我们没有将所有这些值都包含在新属性的可能值中。这是因为设置其中一些特性的效果实际上不是很清楚,而且许多这些值实际上从未使用过。

设置 UIAccessibilityTraits 的值通常有两种目的。它们要么描述 UI 元素扮演的角色,要么描述 UI 元素所处的状态。我们观察到以前属性的大多数用法通常使用一个表示角色的值,并将其与“state selected”、“state disabled”或两者结合。因此,我们决定创建两个新的可访问性属性:accessibilityRoleaccessibilityState

accessibilityRole

新属性 accessibilityRole 用于告知 Talkback 或 Voiceover UI 元素的角色。此新属性可以采用以下值之一:

  • 按钮
  • 链接
  • 搜索
  • 图像
  • 键盘键
  • 文本
  • 可调整
  • 标题
  • 摘要
  • 图像按钮

此属性只允许传入一个值,因为 UI 元素通常在逻辑上不会同时拥有多个这些角色。图像和按钮是例外,因此我们添加了一个图像按钮(imagebutton)角色,它是两者的组合。

accessibilityStates

新属性 accessibilityStates 用于告知 Talkback 或 Voiceover UI 元素所处的状态。此属性接受一个包含以下一个或两个值的数组:

  • 已选择
  • 已禁用

解决方案二:添加可访问性提示

为此,我们添加了一个新属性 accessibilityHint。设置此属性将允许 Talkback 或 Voiceover 向用户朗读提示。

accessibilityHint

此属性以字符串形式接收要读取的可访问性提示。

在 iOS 上,设置此属性将设置视图上相应的原生属性 AccessibilityHint。如果 iPhone 中启用了可访问性提示,Voiceover 将会朗读该提示。

在 Android 上,设置此属性会将提示的值附加到可访问性标签的末尾。这种实现方式的优点是它模仿了 iOS 上提示的行为,但缺点是 Android 上的这些提示无法像 iOS 上那样在设置中关闭。

我们在 Android 上做出此决定的原因是,通常可访问性提示与特定操作(例如点击)相关联,我们希望在不同平台之间保持行为一致。

问题三的解决方案

accessibilityIgnoresInvertColors

我们将 Apple 的 API AccessibilityIgnoresInvertColors 暴露给了 JavaScript,所以现在当你有一个不想反转颜色的视图(例如图像)时,你可以将此属性设置为 true,它就不会被反转。

新用法

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

如何升级

如果您当前正在使用 accessibilityComponentTypeaccessibilityTraits,以下是您可以采取的升级到新属性的步骤。

1. 使用 jscodeshift

最简单的用例可以通过运行 jscodeshift 脚本来替换。

脚本替换以下实例

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

替换为

accessibilityRole= “trait”

此脚本还会删除 AccessibilityComponentType 的实例(假设您在设置 AccessibilityComponentType 的所有地方,也会设置 AccessibilityTraits)。

2. 使用手动代码修改

对于使用 AccessibilityTraits 但没有对应 AccessibilityRole 值的情况,以及将多个特性传递给 AccessibilityTraits 的情况,需要进行手动代码修改。

通常,

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

将被手动替换为

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

这些属性已在 Facebook 的代码库中使用。Facebook 的代码修改出奇地简单。jscodeshift 脚本修复了我们大约一半的实例,另一半则手动修复。总的来说,整个过程不到几个小时。

希望您能发现更新后的 API 有用!请继续使应用程序更易于访问!#包容性

2018 年 React Native 现状

·5 分钟阅读
Sophie Alpert
Facebook React 工程师经理

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

在 Facebook,我们比以往任何时候都更频繁地使用 React Native,并将其用于许多重要项目。我们最受欢迎的产品之一是 Marketplace,它是我们应用程序的顶级标签之一,每月有 8 亿人使用。自 2015 年创建以来,Marketplace 的所有部分都使用 React Native 构建,包括应用程序不同部分的 100 多个全屏视图。

我们还在应用程序的许多新部分中使用 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 状态更新转换为对 DOM API(如 document.createElement(attrs).appendChild())的命令式、可变调用一样,React Native 的设计目的是返回一个列出要执行的突变的 JSON 消息,例如 [["createView", attrs], ["manageChildren", ...]]。我们将整个系统设计为从不依赖于获取同步响应,并确保该列表中的所有内容都可以完全序列化为 JSON 并返回。我们这样做是为了获得它提供的灵活性:在这种架构之上,我们能够构建诸如Chrome 调试之类的工具,这些工具通过 WebSocket 连接异步运行所有 JavaScript 代码。

在过去的 5 年里,我们发现这些初始原则使得构建某些功能变得更加困难。异步桥接意味着您无法将 JavaScript 逻辑直接与许多期望同步响应的原生 API 集成。批处理桥接(队列原生调用)意味着 React Native 应用程序更难调用原生实现的功能。可序列化桥接意味着不必要的复制,而不是在两个世界之间直接共享内存。对于完全用 React Native 构建的应用程序来说,这些限制通常是可以忍受的。但对于 React Native 和现有应用程序代码之间复杂集成的应用程序来说,它们令人沮丧。

我们正在对 React Native 进行大规模的重新架构,以使该框架更加灵活,并更好地与混合 JavaScript/原生应用程序中的原生基础设施集成。 通过这个项目,我们将应用过去 5 年所学到的知识,并逐步将我们的架构提升到更现代的水平。我们正在重写 React Native 的许多内部组件,但大部分更改都在底层:现有的 React Native 应用程序将继续运行,几乎或根本不需要更改。

为了使 React Native 更轻量化并更好地适应现有的原生应用,此次重构有三个主要的内部变化。首先,我们正在改变线程模型。每次 UI 更新不再需要在三个不同的线程上执行工作,而是可以在任何线程上同步调用 JavaScript 进行高优先级更新,同时仍将低优先级工作从主线程中分离出来以保持响应性。其次,我们将异步渲染功能整合到 React Native 中,以允许多个渲染优先级并简化异步数据处理。最后,我们正在简化桥接以使其更快、更轻量;原生和 JavaScript 之间的直接调用更高效,并且将使构建跨语言堆栈跟踪等调试工具变得更容易。

一旦这些更改完成,将可能实现更紧密的集成。目前,如果不进行复杂的修改,无法集成原生导航和手势处理,或者像 UICollectionView 和 RecyclerView 这样的原生组件。在我们对线程模型进行更改之后,构建这样的功能将变得简单直接。

随着这项工作的临近完成,我们将在今年晚些时候发布更多细节。

社区

除了 Facebook 内部的社区,我们也很高兴看到 Facebook 之外蓬勃发展的 React Native 用户和贡献者群体。我们希望通过更好地服务 React Native 用户和让项目更容易贡献来更多地支持 React Native 社区。

正如我们的架构更改将帮助 React Native 更清晰地与其他原生基础设施互操作一样,React Native 在 JavaScript 端也应该更轻量,以更好地适应 JavaScript 生态系统,这包括使 VM 和打包器可互换。我们知道破坏性更改的速度可能难以跟上,因此我们希望找到减少主要版本发布次数的方法。最后,我们知道有些团队正在寻找更全面的文档,例如启动优化等主题,我们的专业知识尚未记录下来。预计在未来一年会看到这些变化。

如果您正在使用 React Native,您就是我们社区的一员;请继续告诉我们如何才能让 React Native 为您做得更好。

React Native 只是移动开发人员工具箱中的一个工具,但我们坚信它——我们每天都在改进它,去年有 500 多名贡献者提交了 2500 多次提交。

在 React Native 中使用 TypeScript

·8 分钟阅读
Ash Furrow
Artsy 软件工程师

JavaScript!我们都喜欢它。但我们中的一些人也喜欢类型。幸运的是,存在为 JavaScript 添加更强类型的选项。我最喜欢的是TypeScript,但 React Native 开箱即用地支持Flow。您喜欢哪一个取决于个人偏好,它们各有其为 JavaScript 添加类型魔术的方法。今天,我们将探讨如何在 React Native 应用中使用 TypeScript。

这篇博文以 Microsoft 的 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,因此要在 React Native 应用中使用 TypeScript 进行测试,我们需要将 ts-jest 添加到我们的 devDependencies 中。

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>

·7 分钟阅读
Peter Argany
Facebook 软件工程师

动机

三年前,GitHub 上发布了一个支持 React Native 输入附件视图的问题。

在随后的几年里,关于这个问题,有无数的“+1”,各种变通方法,但 RN 方面没有任何具体改变——直到今天。从 iOS 开始,我们正在公开一个 API,用于访问原生输入附件视图,我们很高兴分享我们是如何构建它的。

背景

输入附件视图到底是什么?阅读 Apple 的开发者文档,我们了解到它是一个自定义视图,当接收者成为第一响应者时,可以锚定到系统键盘的顶部。任何继承自 UIResponder 的对象都可以将 .inputAccessoryView 属性重新声明为读写,并在此管理自定义视图。响应者基础设施挂载视图,并使其与系统键盘保持同步。解散键盘的手势,如拖动或点击,在框架级别应用于输入附件视图。这使我们能够构建具有交互式键盘解散功能的内容,这是 iMessage 和 WhatsApp 等顶级消息应用程序不可或缺的功能。

将视图锚定到键盘顶部有两种常见用例。第一个是创建键盘工具栏,例如 Facebook 的发帖背景选择器。

在这种情况下,键盘聚焦在文本输入字段上,输入附件视图用于提供额外的键盘功能。此功能与输入字段的类型相关。在地图应用程序中,它可能是地址建议;在文本编辑器中,它可能是富文本格式工具。


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

第二个常见场景是固定文本输入

在这里,文本输入实际上是输入附件视图本身的一部分。这通常用于消息应用程序中,可以在滚动浏览以前消息的线程时编写消息。


谁拥有这个示例中的 <InputAccessoryView>?它可以再次是 UITextViewUITextField 吗?文本输入在输入附件视图*内部*,这听起来像是一个循环依赖。解决这个问题本身就是另一篇博客文章。剧透:所有者是一个通用的 UIView 子类,我们手动告诉它成为第一响应者

API 设计

我们现在知道 <InputAccessoryView> 是什么,以及我们如何使用它。下一步是设计一个对两种用例都有意义,并能与现有 React Native 组件(如 <TextInput>)良好协作的 API。

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

  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 属性。这样做有效地将 <InputAccessoryView><TextInput> 在其 ObjC 实现中连接起来。

支持固定文本输入(场景 2)增加了更多限制。对于这种设计,输入附件视图有一个文本输入作为子视图,因此通过 nativeID 链接不是一个选项。相反,我们将通用屏幕外 UIView.inputAccessoryView 设置为我们的原生 <InputAccessoryView> 层次结构。通过手动告诉这个通用 UIView 成为第一响应者,该层次结构由响应者基础设施挂载。这个概念在前面提到的博客文章中有详细解释。

陷阱

当然,在构建这个 API 时并非一帆风顺。以下是我们遇到的一些陷阱以及我们如何解决它们。

构建此 API 的最初想法涉及监听 NSNotificationCenter 的 UIKeyboardWill(Show/Hide/ChangeFrame) 事件。这种模式在一些开源库中和 Facebook 应用程序内部的某些部分中都有使用。不幸的是,UIKeyboardDidChangeFrame 事件没有及时调用来更新滑动时的 <InputAccessoryView> 帧。此外,键盘高度的变化也无法通过这些事件捕获。这会产生一类表现如下的错误:

在 iPhone X 上,文本键盘和表情符号键盘的高度不同。大多数使用键盘事件来操纵文本输入框的应用程序都必须修复上述错误。我们的解决方案是致力于使用 .inputAccessoryView 属性,这意味着响应者基础设施可以处理此类帧更新。


我们遇到的另一个棘手的 bug 是避免 iPhone X 上的 Home Bar。您可能会想,“Apple 开发了safeAreaLayoutGuide就是为了这个,这很简单!”我们当初也同样天真。第一个问题是,原生 <InputAccessoryView> 实现直到即将出现的那一刻才拥有可锚定的窗口。这没关系,我们可以覆盖 -(BOOL)becomeFirstResponder 并在那里强制执行布局约束。遵守这些约束会使附件视图向上移动,但又出现了一个新的 bug:

输入附件视图成功避开了 Home Bar,但现在不安全区域后面的内容可见。解决方案在于这个雷达。我将原生 <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

·10 分钟阅读
Richard Threlkeld
AWS Mobile 高级技术产品经理

AWS 在技术行业中以云服务提供商而闻名。这些服务包括计算、存储和数据库技术,以及完全托管的无服务器产品。AWS Mobile 团队一直与客户和 JavaScript 生态系统成员密切合作,使云连接的移动和网络应用程序更加安全、可扩展且更易于开发和部署。我们从一个完整的入门套件开始,但最近还有一些新的进展。

这篇博客文章讨论了 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:提供与 RESTful API 的安全交互,利用 AWS Signature Version 4。API 模块在与 Amazon API Gateway 配合的无服务器基础设施上表现出色。
  • Storage:简化了在 Amazon S3 中上传、下载和列出内容的命令。您还可以轻松地按用户将数据分组为公共或私有内容。
  • Caching:一个跨 Web 应用程序和 React Native 的 LRU 缓存接口,使用特定于实现的持久性。
  • i18n 和 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 用户池进行用户注册和登录。该服务实现了 安全远程密码协议 作为在身份验证尝试期间保护用户的方式。如果您倾向于阅读该协议的数学原理,您会注意到在计算密码验证器时必须使用一个大素数,通过一个原根来生成一个组。在 React Native 环境中,JIT 是禁用的。这使得此类安全操作中的大整数计算性能较低。为了解决这个问题,我们发布了可以在项目中链接的 Android 和 iOS 原生桥接:

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

我们也很高兴看到 Expo 团队已将此包含在他们最新的 SDK 中,这样您就可以在不弹出(ejecting)的情况下使用 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 订阅进行控制。由于 AWS AppSync 中的订阅通过模式进行控制(带有 GraphQL 指令),并且模式可以使用任何数据源,这意味着您可以从 Amazon DynamoDB 和 Amazon Elasticsearch Service 的数据库操作中触发通知,或从您的基础设施的其他部分(带有 AWS Lambda)触发通知。

与 AWS Amplify 类似,您可以在 AWS AppSync 的 GraphQL API 上使用企业安全功能。该服务让您可以使用 API 密钥快速入门。然而,当您投入生产时,它可以转换为使用 AWS Identity and Access Management (IAM) 或来自 Amazon Cognito 用户池的 OIDC 令牌。您可以在解析器级别通过类型上的策略控制访问。您甚至可以使用逻辑检查进行细粒度访问控制检查,例如在运行时检测用户是否是特定数据库资源的所有者。还有关于检查组成员资格以执行解析器或单个数据库记录访问的功能。

为了帮助 React Native 开发者了解更多这些技术,AWS AppSync 控制台主页上提供了一个内置的 GraphQL 示例 schema。此示例部署了一个 GraphQL schema,配置了数据库表,并自动为您连接了查询、变更和订阅。还有一个可用的AWS AppSync 的 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 的应用加载动画

·12 分钟阅读
Eli White
Eli White
Meta 软件工程师

Twitter 的 iOS 应用有一个我非常喜欢的加载动画。

当应用准备就绪时,Twitter 标志会令人愉悦地展开,露出应用程序。

我想弄清楚如何用 React Native 重现这个加载动画。


要理解如何构建它,我首先必须了解加载动画的不同部分。最容易看到细节的方法是放慢速度。

这里有几个主要部分,我们需要弄清楚如何构建。

  1. 缩放小鸟。
  2. 随着小鸟的变大,显示下方的应用
  3. 最后将应用稍微缩小

我花了好长时间才弄清楚如何制作这个动画。

我一开始有一个**不正确**的假设:蓝色背景和 Twitter 小鸟是应用程序**之上**的一层,并且随着小鸟变大,它变得透明,从而露出下方的应用程序。这种方法行不通,因为 Twitter 小鸟变得透明会显示蓝色层,而不是下方的应用程序!

幸运的是,亲爱的读者,您不必经历我同样的沮丧。您将获得这个直接跳到精彩部分的教程!


正确的方法

在开始编写代码之前,理解如何将其分解很重要。为了帮助可视化这个效果,我用 CodePen(嵌入在几个段落中)重新创建了它,这样您就可以交互式地查看不同的层。

这个效果有三个主要层次。第一个是蓝色背景层。尽管这看起来出现在应用程序的顶部,但它实际上在后面。

然后我们有一个纯白色层。最后,在最前面,是我们的应用程序。


这个动画的主要技巧是使用 Twitter 标志作为 mask,并同时遮罩应用程序和白色层。我不会深入探讨遮罩的细节,网上有大量资源可供参考

在这种背景下,遮罩的基本原理是:遮罩的不透明像素显示它们所遮罩的内容,而透明像素则隐藏它们所遮罩的内容。

我们使用 Twitter 徽标作为遮罩,并让它遮罩两个图层:纯白色图层和应用程序图层。

为了显示应用程序,我们放大遮罩,直到它大于整个屏幕。

当遮罩层放大时,我们淡入应用层的透明度,显示应用并隐藏其后的纯白色层。为了完成效果,我们让应用层从大于1的比例开始,并在动画结束时将其缩小到1。然后我们隐藏非应用层,因为它们将不再可见。

俗话说一图胜千言。那么一个交互式可视化值多少字呢?点击“下一步”按钮浏览动画。显示图层可以为您提供侧视图视角。网格在那里是为了帮助可视化透明层。

现在,关于 React Native

好的。现在我们知道了要构建什么以及动画的工作原理,我们可以开始编写代码了——这才是您真正来此的原因。

这个难题的核心是 MaskedViewIOS,一个核心的 React Native 组件。

import {MaskedViewIOS} from 'react-native';

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

MaskedViewIOS 接受 maskElementchildren 属性。子元素会被 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>;

这将为我们提供下方看到的图层。

现在是 Animated 部分

我们已经拥有实现这一切所需的所有组件,下一步是对它们进行动画处理。为了使这个动画感觉良好,我们将利用 React Native 的 Animated API。

Animated 允许我们在 JavaScript 中声明性地定义动画。默认情况下,这些动画在 JavaScript 中运行,并告诉原生层每帧要进行的更改。尽管 JavaScript 会尝试每帧更新动画,但它可能无法足够快地完成,从而导致掉帧(卡顿)发生。这不是我们想要的!

Animated 具有特殊行为,允许您获得无卡顿的动画。Animated 有一个名为 useNativeDriver 的标志,它在动画开始时将您的动画定义从 JavaScript 发送到原生层,允许原生端处理动画更新,而无需每帧都往返于 JavaScript。useNativeDriver 的缺点是您只能更新特定的属性集,主要是 transformopacity。您不能使用 useNativeDriver 来动画背景颜色等,至少目前不能——我们会随着时间的推移添加更多功能,当然,您随时可以提交 PR,为您的项目所需的属性做出贡献,造福整个社区 😀。

由于我们希望这个动画流畅,我们将在此限制内工作。要更深入地了解 useNativeDriver 的底层工作原理,请查看我们宣布此功能的博客文章

分解我们的动画

我们的动画有 4 个组成部分

  1. 放大鸟形,显示应用和纯白色层
  2. 应用淡入
  3. 应用缩小
  4. 完成后隐藏白色层和蓝色层

使用 Animated,定义动画主要有两种方式。第一种是使用 Animated.timing,它允许您精确指定动画的运行时间,并带有缓动曲线以平滑运动。另一种方法是使用基于物理的 API,例如 Animated.spring。使用 Animated.spring,您可以指定摩擦力和弹簧张力等参数,然后让物理效果运行您的动画。

我们有多个动画希望同时运行,它们之间都密切相关。例如,我们希望在遮罩正在显示的过程中,应用开始淡入。由于这些动画密切相关,我们将使用带有单个 Animated.ValueAnimated.timing

Animated.Value 是对原生值的包装,Animated 使用它来了解动画的状态。通常,对于一个完整的动画,您只希望有一个这样的值。大多数使用 Animated 的组件会将该值存储在 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 则不够大 😀。不过,这一部分有趣的是,数字越大,它看起来增长得越快,因为它必须在相同的时间内到达那里。这个数字经过了一些试验和错误才与此标志看起来不错。不同尺寸的标志/设备将需要不同的最终比例以确保整个屏幕都被显示。

应用应保持不透明一段时间,至少在 Twitter 标志缩小期间。根据官方动画,我希望在鸟形放大到一半时开始显示它,并很快完全显示出来。因此,在 15% 时我们开始显示它,在整个动画进行到 30% 时它完全可见。

应用比例从 1.1 开始,并在动画结束时缩小到其常规比例。

现在,代码实现。

我们上面所做的本质上是将动画进度百分比的值映射到各个部分的所需值。我们使用 Animated 的 .interpolate 方法来实现这一点。我们创建了 3 个不同的样式对象,每个对象对应动画的一个部分,并使用基于 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 是在您阅读 React Native 文档后学习 Animated 的绝佳资源。
  2. 实际的 Twitter 动画似乎在结尾处加快了遮罩的显示速度。尝试修改加载器,使用不同的缓动函数(或弹簧效果!)以更好地匹配该行为。
  3. 遮罩当前的最终比例是硬编码的,可能无法在平板电脑上完全显示整个应用。根据屏幕尺寸和图像尺寸计算最终比例将是一个很棒的 PR(Pull Request)。

React Native 月刊 #6

·4 分钟阅读
Tomislav Tenodi
Speck 创始人

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、新发布渠道功能(为给定渠道设置活动发布版本和回滚)、用于跟踪独立应用构建的 Web 仪表板,以及修复了 OpenGL Android 实现和 Android 多任务处理器的长期存在错误,这只是其中的一小部分。
  • 从今年一月开始,我们将为 React Navigation 分配更多资源。我们坚信使用 React 组件和 Animated、react-native-gesture-handler 等基本元素来构建 React Native 导航是可能且理想的,我们对计划中的一些改进感到非常兴奋。如果您希望为社区做出贡献,请查看 react-native-mapsreact-native-svg,它们都需要一些帮助!

Infinite Red

Microsoft

  • 已启动一个 pull request,将核心 React Native Windows 桥迁移到 .NET Standard,使其有效地与操作系统无关。希望许多其他 .NET Core 平台能够使用自己的线程模型、JavaScript 运行时和 UIManager(例如 JavaScriptCore、Xamarin.Mac、Linux Gtk# 和三星 Tizen 选项)扩展该桥。

Wix

  • Detox
    • 为了使我们能够扩展 E2E 测试,我们希望最大限度地减少在 CI 上花费的时间,我们正在为 Detox 开发并行化支持。
    • 提交了一个 pull request,以支持自定义构建风味,从而更好地支持 E2E 中的模拟。
  • DetoxInstruments
    • 开发 DetoxInstruments 的杀手级功能被证明是一项非常具有挑战性的任务,在任何给定时间获取 JavaScript 回溯都需要自定义 JSCore 实现来支持 JS 线程挂起。在 Wix 内部应用上测试分析器揭示了关于 JS 线程的一些有趣见解。
    • 该项目目前尚不稳定,无法广泛使用,但正在积极开发中,我们希望很快宣布。
  • React Native Navigation
    • V2 的开发速度已大幅提升,到目前为止,我们只有 1 名开发人员投入其 20% 的时间,现在我们有 3 名开发人员全职投入!
  • Android 性能
    • 用最新版本(webkitGTK 项目的顶端,带有自定义 JIT 配置)替换 RN 中捆绑的旧 JSCore,使 JS 线程的性能提高了 40%。接下来是编译其 64 位版本。这项工作基于 适用于 Android 的 JSC 构建脚本。在此处查看其当前状态 这里

下次会议

有人讨论将本次会议重新定位,讨论一个单一而具体的主题(例如导航、将 React Native 模块移入单独的仓库、文档等)。这样我们觉得可以最好地为 React Native 社区做出贡献。这可能会在下次会议中进行。请随时推特您希望涵盖的主题。

React Native 月刊 #5

·4 分钟阅读
Tomislav Tenodi
Speck 创始人

React Native 月度会议继续进行!让我们看看我们的团队都在忙些什么。

Callstack

  • 我们一直在研究 React Native CI。最重要的是,我们已从 Travis 迁移到 Circle,使 React Native 拥有一个单一、统一的 CI 流水线。
  • 我们组织了 Hacktoberfest - React Native 版,在那里我们与参与者一起尝试向开源项目提交许多 pull request。
  • 我们继续致力于 Haul。上个月,我们发布了两个新版本,包括 webpack 3 支持。我们计划添加 CRNAExpo 支持,并努力改进 HMR。我们的路线图在问题跟踪器上公开。如果您想提出改进建议或提供反馈,请告诉我们!

Expo

  • 发布了 Expo SDK 22(使用 React Native 0.49),并为其更新了 CRNA
    • 包括改进的启动画面 API、基本的 ARKit 支持、“DeviceMotion” API、iOS11 上的 SFAuthenticationSession 支持以及更多
  • 您的 snacks 现在可以拥有多个 JavaScript 文件,您只需将图像和其他资产拖放到编辑器中即可上传。
  • 贡献 react-navigation 以添加对 iPhone X 的支持。
  • 在使用 Expo 构建大型应用时,将我们的注意力集中在遇到的难题上。例如:
    • 对部署到多个环境(包括预发布、生产和任意渠道)提供一流支持。渠道将支持回滚和为给定渠道设置活动发布版本。如果您想成为早期测试者,请告知我们,联系 @expo_io
    • 我们还在改进我们的独立应用构建基础设施,并添加了在独立应用构建中捆绑图像和其他非代码资产的支持,同时保留了通过空中更新资产的能力。

Facebook

  • 更好的 RTL 支持
    • 我们正在引入许多方向感知样式。
      • 位置
        • (左|右) → (开始|结束)
      • 外边距
        • 外边距(左|右) → 外边距(开始|结束)
      • 内边距
        • 内边距(左|右) → 内边距(开始|结束)
      • 边框
        • 边框顶部(左|右)半径 → 边框顶部(开始|结束)半径
        • 边框底部(左|右)半径 → 边框底部(开始|结束)半径
        • 边框(左|右)宽度 → 边框(开始|结束)宽度
        • 边框(左|右)颜色 → 边框(开始|结束)颜色
    • 在 RTL(从右到左书写)模式下,位置、外边距、内边距和边框样式中的“左”和“右”的含义被交换了。在几个月内,我们将移除这种行为,使“左”始终表示“左”,“右”始终表示“右”。这些重大更改隐藏在一个标志下。在您的 React Native 组件中使用 I18nManager.swapLeftAndRightInRTL(false) 以选择启用这些更改。
  • 正在为我们内部的原生模块进行 Flow 类型化,并使用这些类型生成 Java 接口和 ObjC 协议,原生实现必须遵循这些接口和协议。我们希望这个代码生成器最早能在明年开源。

Infinite Red

  • 帮助 React Native 和其他项目的新开源工具。更多内容请看这里
  • 正在改造 Ignite,以发布新的样板(代号:Bowser)。

Shoutem

  • 正在改进 Shoutem 上的开发流程。我们希望简化从创建应用到第一个自定义屏幕的过程,使其变得非常简单,从而降低新 React Native 开发人员的门槛。准备了一些研讨会来测试新功能。我们还改进了 Shoutem CLI 以支持新流程。
  • Shoutem UI 获得了一些组件改进和错误修复。我们还检查了与最新 React Native 版本的兼容性。
  • Shoutem 平台收到了一些显著更新,新的集成作为 开源扩展项目 的一部分提供。我们非常高兴看到其他开发人员积极开发 Shoutem 扩展。我们积极联系并提供有关其扩展的建议和指导。

下次会议

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

React Native 月刊 #4

·3 分钟阅读
Mike Grabowski
Mike Grabowski
Callstack 首席技术官兼联合创始人

React Native 月度会议继续进行!以下是各团队的笔记:

Callstack

  • React Native EU 已结束。来自 33 个国家的 300 多名参与者访问了弗罗茨瓦夫。演讲可在 YouTube 上找到。
  • 会议结束后,我们正在慢慢恢复开源计划。值得一提的是,我们正在开发 react-native-opentok 的下一个版本,它修复了大多数现有问题。

GeekyAnts

正在尝试通过以下方式降低开发人员学习 React Native 的门槛:

  • React Native EU 上宣布了 BuilderX.io。BuilderX 是一个设计工具,可以直接处理 JavaScript 文件(目前仅支持 React Native),以生成美观、可读且可编辑的代码。
  • 推出了 ReactNativeSeed.com,它为您的下一个 React Native 项目提供了一套样板。它提供了各种选项,包括用于数据类型的 TypeScript 和 Flow,用于状态管理的 MobX、Redux 和 mobx-state-tree,以及作为技术栈的 CRNA 和纯 React-Native。

Expo

  • 将很快发布 SDK 21,它增加了对 react-native 0.48.3 的支持,以及 Expo SDK 中的一系列错误修复/可靠性改进/新功能,包括视频录制、新的启动画面 API、对 react-native-gesture-handler 的支持以及改进的错误处理。
  • 关于 react-native-gesture-handlerSoftware MansionKrzysztof Magiera 仍在积极推进它,我们一直在帮助他进行测试并资助他部分开发时间。将其集成到 SDK21 的 Expo 中将使人们可以在 Snack 中轻松使用它,因此我们很高兴看到大家会创造出什么。
  • 关于:改进的错误日志记录/处理——有关日志记录的详细信息(特别是“问题 2”),请参阅此 Expo 内部 PR 的 gist,以及关于处理导入 npm 标准库模块失败尝试的更改,请参阅此提交。以这种方式改进 React Native 上游的错误消息有很多机会,我们将继续进行后续的上游 PR。社区也能参与进来就太好了。
  • native.directory 持续增长,您可以从 GitHub 仓库添加您的项目。
  • 参观北美各地的黑客马拉松,包括 PennAppsHack The NorthHackMIT,以及即将到来的 MHacks

Facebook

  • 正在改进 Android 上的 <Text><TextInput> 组件。(<TextInput> 的原生自动增长;深度嵌套的 <Text> 组件布局问题;更好的代码结构;性能优化)。
  • 我们仍在寻找其他愿意帮助分类问题和 pull request 的贡献者。

Microsoft

  • 发布了 CodePush 的代码签名功能。React Native 开发人员现在可以在 CodePush 中签署其应用程序包。公告可在这里找到。
  • 正在努力完成 CodePush 与 Mobile Center 的集成。也正在考虑测试/崩溃集成。

下次会议

下次会议定于 2017 年 10 月 10 日星期三举行。由于这只是我们的第四次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们如何改进会议成果有任何建议,请随时在 Twitter 上联系我。

React Native 月刊 #3

·5 分钟阅读
Mike Grabowski
Mike Grabowski
Callstack 首席技术官兼联合创始人

React Native 月度会议继续!本月会议略短,因为我们的大多数团队都在忙于交付。下个月,我们将在波兰弗罗茨瓦夫参加 React Native EU 会议。务必抢票,届时现场见!同时,让我们看看我们的团队都在忙些什么。

团队

在第三次会议上,有 5 个团队加入了我们:

笔记

以下是各团队的笔记:

Callstack

  • 最近开源了 react-native-material-palette。它从图像中提取主要颜色,帮助您创建视觉上引人入胜的应用程序。目前它仅支持 Android,但我们正在研究未来添加对 iOS 的支持。
  • 我们已将 HMR 支持引入 haul 以及许多其他很棒的功能!请查看最新版本。
  • React Native EU 2017 即将到来!下个月将是 React Native 和波兰的主场!务必抢购最后剩余的门票,链接在这里

Expo

  • 发布了在 Snack 上安装 npm 包的支持。通常的 Expo 限制适用——包不能依赖于 Expo 中尚未包含的自定义原生 API。我们还在努力支持 Snack 中的多文件和资产上传。Satyajit 将在 React Native Europe 上谈论 Snack。
  • 发布了 SDK20,其中包含相机、支付、安全存储、磁力计、暂停/恢复文件系统下载以及改进的启动/加载屏幕。
  • 继续与 Krzysztof 合作开发 react-native-gesture-handler。请尝试一下,使用 PanResponder 或原生手势识别器重建您之前构建的一些手势,并告诉我们您遇到的任何问题。
  • 正在试验 JSC 调试协议,并在 Canny 上处理一系列功能请求。

Facebook

  • 上个月我们讨论了 GitHub 问题跟踪器的管理,以及我们将尝试改进以解决项目的可维护性问题。
  • 目前,未解决问题的数量稳定在 600 左右,并且似乎会保持一段时间。在过去一个月中,我们因缺乏活动(定义为过去 60 天内没有评论)而关闭了 690 个问题。在这 690 个问题中,有 58 个因各种原因被重新打开(维护者承诺提供修复,或者贡献者有充分理由保持问题开放)。
  • 我们计划在可预见的未来继续自动关闭不活跃的问题。我们希望达到这样的状态:跟踪器中打开的每一个有影响的问题都能得到处理,但我们尚未实现。我们需要维护者全力协助分类问题,并确保我们不会遗漏引入回归或破坏性更改的问题,特别是那些影响新创建项目的问题。有兴趣提供帮助的人可以使用 Facebook GitHub Bot 来分类问题和 pull request。新的维护者指南包含有关分类和使用 GitHub Bot 的更多信息。请将您自己添加到问题工作组,并鼓励其他活跃社区成员也这样做!

Microsoft

  • 新的 Skype 应用基于 React Native 构建,旨在最大程度地实现跨平台代码共享。基于 React Native 的 Skype 应用目前已在 Android 和 iOS 应用商店中可用。
  • 在基于 React Native 构建 Skype 应用时,我们会向 React Native 发送 pull request,以解决我们遇到的 bug 和缺失的功能。到目前为止,我们已成功合并了大约 70 个 pull request
  • React Native 使我们能够从同一个代码库支持 Android 和 iOS Skype 应用。我们还希望使用该代码库来支持 Skype Web 应用。为了帮助我们实现这一目标,我们构建并开源了一个基于 React/React Native 的薄层,名为 ReactXP。ReactXP 提供了一组跨平台组件,在针对 iOS/Android 时映射到 React Native,在针对 Web 时映射到 react-dom。ReactXP 的目标与另一个名为 React Native for Web 的开源库相似。在 ReactXP FAQ 中简要描述了这些库方法的不同之处。

Shoutem

  • 我们正在继续努力改进和简化使用 Shoutem 构建应用时的开发体验。
  • 开始将我们所有的应用迁移到 react-navigation,但最终我们推迟了这一计划,直到发布更稳定的版本,或者其中一个原生导航解决方案变得稳定。
  • 正在将我们所有的扩展和大部分开源库(animationthemeui)更新到 React Native 0.47.1。

下次会议

下次会议定于 2017 年 9 月 13 日星期三举行。由于这只是我们的第三次会议,我们想知道这些笔记如何有益于 React Native 社区。如果您对我们如何改进会议成果有任何建议,请随时在 Twitter 上联系我。