跳到主要内容

发布 0.56

·5 分钟阅读
Lorenzo Sciandra
核心维护者 & Drivetribe 的 React Native 开发者

期待已久的 React Native 0.56 版本现已发布 🎉。这篇博文重点介绍了这个新版本中引入的一些更改。我们还想借此机会解释一下自三月份以来我们一直在忙什么。

破坏性更改困境,或者,“何时发布?”

贡献者指南解释了所有对 React Native 的更改所经历的集成过程。该项目由许多不同的工具组成,需要协调和持续的支持以保持一切正常运行。再加上活跃的开源社区为项目做出贡献,您将对这一切的规模有一个概念。

随着 React Native 令人印象深刻的采用,破坏性更改必须非常谨慎地进行,并且这个过程并不像我们希望的那样顺利。我们决定跳过四月和五月的发布,以便核心团队能够集成和测试一组新的破坏性更改。专门的社区沟通渠道在此过程中被使用,以确保 2018 年 6 月 (0.56.0) 版本尽可能顺利地被那些耐心等待稳定版本的人采用。

0.56.0 完美吗?不,就像所有软件一样:但是我们达到了一个平衡点,即“等待更多稳定性”与“测试带来了成功的结果,所以我们可以向前推进”之间的权衡,我们觉得可以发布它了。此外,我们意识到 一个 一些 问题 在最终的 0.56.0 版本中尚未解决。大多数开发者应该可以顺利升级到 0.56.0。对于那些因上述问题而受阻的开发者,我们希望在我们的讨论中见到您,并期待与您合作解决这些问题。

您可以将 0.56.0 视为构建更稳定框架的基础模块:可能需要一两周的广泛采用才能消除所有边缘情况,但这将带来更好的 2018 年 7 月 (0.57.0) 版本。

在本节的结尾,我们要感谢所有 67 位贡献者,他们总共完成了 818 次提交(!)这将帮助使您的应用变得更好 👏。

现在,无需赘言...

重大变更

Babel 7

您可能知道,使我们能够使用最新和最强大的 JavaScript 功能的转译器工具 Babel 正在迁移到 v7。由于这个新版本带来了一些重要的更改,我们认为现在是升级的好时机,让 Metro 能够 利用其改进

如果您在升级时遇到问题,请参考 与其相关的文档部分

Android 支持现代化

在 Android 上,许多周围的工具都发生了变化。我们已更新到 Gradle 3.5Android SDK 26Fresco 1.9.0 和 OkHttp 3.10.0,甚至 NDK API 目标也更新到 API 16。这些更改应该不会出现问题,并会加快构建速度。更重要的是,它将帮助开发者遵守下个月生效的 新的 Play 商店要求

与此相关,我们要特别感谢 Dulmandakh 提交的许多 PR,使之成为可能 👏。

在这个方向上还需要采取更多步骤,您可以关注 专门的问题(以及 JSC 的一个附带问题)中关于更新 Android 支持的未来规划和讨论。

新的 Node、Xcode、React 和 Flow – 我的天!

Node 8 现在是 React Native 的标准。实际上它已经在测试中了,但随着 Node 6 进入维护模式,我们已经全力推进。React 也已更新到 16.4,带来了大量的修复。

我们正在放弃对 iOS 8 的支持,使 iOS 9 成为可以定位的最旧 iOS 版本。我们不认为这会成为问题,因为任何可以运行 iOS 8 的设备都可以升级到 iOS 9。此更改使我们能够删除为运行 iOS 8 的旧设备实现解决方法而很少使用的代码。

持续集成工具链已更新使用 Xcode 9.4,确保所有 iOS 测试都在 Apple 提供的最新开发者工具上运行。

我们已升级到 Flow 0.75 以使用新的错误格式,许多开发者对此表示赞赏。我们还为更多组件创建了类型。如果您尚未在您的项目中强制执行静态类型,请考虑使用 Flow 在编码时而不是在运行时识别问题。

以及许多其他事情...

例如,YellowBox 已被替换为一个新的实现,这使得调试变得更好。

有关完整的发行说明,请参考完整的更新日志。并记住关注升级指南,以避免迁移到这个新版本时出现问题。


最后一点:从本周开始,React Native 核心团队将恢复每月会议。我们将确保让每个人都及时了解会议内容,并确保将您的反馈放在手边以供未来的会议参考。

祝大家编码愉快!

LorenzoRyan 和整个 React Native 核心团队

PS: 与往常一样,我们要提醒大家,React Native 仍然处于 0.x 版本控制中,因为仍然有许多更改正在进行中 - 所以请记住,在升级时,是的,可能仍然有东西会崩溃或损坏。在问题和提交 PR 时互相帮助 - 并记住遵守强制执行的 CoC:屏幕的另一端始终是真人。

2018 年 React Native 状况

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

自从我们上次发布关于 React Native 的状态更新以来已经有一段时间了。

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

我们还在为应用的许多新部分使用 React Native。如果您观看了上个月的 F8 主题演讲,您会认出 Blood Donations、Crisis Response、Privacy Shortcuts 和 Wellness Checks – 所有这些都是最近使用 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 之间的直接调用更有效,并将使构建跨语言堆栈跟踪等调试工具更容易。

一旦这些更改完成,更紧密的集成将成为可能。如今,如果不进行复杂的 hack,就不可能集成原生导航和手势处理或原生组件,如 UICollectionView 和 RecyclerView。在我们更改线程模型后,构建这样的功能将变得很简单。

随着这项工作接近完成,我们将在今年晚些时候发布有关这项工作的更多详细信息。

社区

除了 Facebook 内部的社区之外,我们很高兴拥有 Facebook 外部蓬勃发展的 React Native 用户和协作者群体。我们希望更多地支持 React Native 社区,既要更好地服务于 React Native 用户,也要使该项目更容易贡献。

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

如果您正在使用 React Native,您就是我们社区的一份子;请继续告诉我们如何才能使 React Native 对您更好。

React Native 只是移动开发者工具箱中的一个工具,但我们坚信它——我们每天都在使其变得更好,在过去一年中,有 500 多位贡献者提交了 2500 多个 commit。

将 TypeScript 与 React Native 结合使用

·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,因此为了使用 TypeScript 测试 React Native 应用,我们将需要将 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 构建 - Build.com 应用

·5 分钟阅读
Garrett McCullough
高级移动工程师

Build.com 总部位于加利福尼亚州奇科,是最大的家居装修用品在线零售商之一。该团队拥有 18 年以网络为中心的强大业务,并于 2015 年开始考虑移动应用程序。由于我们团队规模小且原生经验有限,构建独特的 Android 和 iOS 应用程序是不切实际的。相反,我们决定冒险尝试非常新的 React Native 框架。我们的初始提交是在 2015 年 8 月 12 日,使用的是 React Native v0.8.0!我们于 2016 年 10 月 15 日在两个应用商店上线。在过去的两年中,我们不断升级和扩展该应用程序。我们目前使用的是 React Native 版本 0.53.0。

您可以在 https://www.build.com/app 查看该应用程序。

功能特点

我们的应用程序功能齐全,包含您对电子商务应用程序所期望的一切:产品列表、搜索和排序、配置复杂产品的能力、收藏夹等。我们接受标准信用卡付款方式以及 PayPal 和 iOS 用户的 Apple Pay。

您可能意想不到的一些突出特点包括

  1. 约 40 种产品提供 3D 模型,并有 90 种饰面
  2. 增强现实 (AR) 技术,允许用户以 98% 的尺寸精度查看灯具和水龙头在家中的外观。Build.com React Native 应用程序在 Apple App Store 中被推荐用于 AR 购物!AR 现在可用于 Android 和 iOS!
  3. 协作项目管理功能,允许人们为项目的不同阶段整理购物清单,并围绕选择进行协作

我们正在开发许多新的令人兴奋的功能,这些功能将继续改善我们的应用程序体验,包括下一阶段的沉浸式购物与 AR。

我们的开发工作流程

Build.com 允许每位开发人员选择最适合他们的工具。

  • IDE 包括 Atom、IntelliJ、VS Code、Sublime、Eclipse 等。
  • 对于单元测试,开发人员负责为任何新组件创建 Jest 单元测试,我们正在努力使用 jest-coverage-ratchet 提高应用程序旧部分的覆盖率。
  • 我们使用 Jenkins 来构建我们的 beta 版和候选发布版。此过程对我们来说效果很好,但仍然需要大量工作来创建发行说明和其他工件。
  • 集成测试包括一个共享的测试人员池,他们在桌面、移动和 Web 平台上工作。我们的自动化工程师正在使用 Java 和 Appium 构建我们的自动化集成测试套件。
  • 工作流程的其他部分包括详细的 eslint 配置、强制执行测试所需属性的自定义规则以及阻止违规更改的 pre-push 钩子。

应用程序中使用的库

Build.com 应用程序依赖于许多常见的开源库,包括:Redux、Moment、Numeral、Enzyme 和一堆 React Native 桥接模块。我们还使用了许多 fork 的开源库;fork 的原因要么是因为它们被放弃了,要么是因为我们需要自定义功能。快速计数显示大约有 115 个 JavaScript 和原生依赖项。我们想探索删除未使用库的工具。

我们正在通过 TypeScript 添加静态类型,并研究可选链。这些功能可以帮助我们解决我们仍然看到的一些类型的错误

  • 类型错误的数据
  • 由于对象不包含我们期望的内容而导致的未定义数据

开源贡献

由于我们如此严重地依赖开源,我们的团队致力于回馈社区。Build.com 允许团队开源我们构建的库,并鼓励我们回馈我们使用的库。

我们已经发布并维护了许多 React Native 库

  • react-native-polyfill
  • react-native-simple-store
  • react-native-contact-picker

我们还为包括 React 和 React Native 在内的长长的库列表做出了贡献,react-native-schemes-managerreact-native-swipeablereact-native-galleryreact-native-view-transformerreact-native-navigation

我们的历程

在过去的几年里,我们看到了 React Native 和生态系统的巨大发展。早期,似乎每个版本的 React Native 都会修复一些错误,但会引入更多错误。例如,远程 JS 调试在 Android 上中断了几个月。值得庆幸的是,在 2017 年,情况变得稳定得多。

我们面临的一个反复出现的大挑战是导航库。长期以来,我们一直使用 Expo 的 ex-nav 库。它对我们来说效果很好,但最终被弃用了。但是,当时我们正处于繁重的功能开发阶段,因此花时间更换导航库是不可行的。这意味着我们不得不 fork 该库并对其进行修补以支持 React 16 和 iPhone X。最终,我们能够迁移到 react-native-navigation,并希望它能得到持续的支持。

桥接模块

另一个巨大的挑战是桥接模块。当我们刚开始时,缺少许多关键的桥接。我的一个队友编写了 react-native-contact-picker,因为我们需要在我们的应用程序中访问 Android 联系人选择器。我们还看到许多桥接被 React Native 内的更改破坏。例如,React Native v40 中存在一个重大更改,当我们升级我们的应用程序时,我不得不提交 PR 来修复 3 或 4 个尚未更新的库。

展望未来

随着 React Native 的持续发展,我们对社区的愿望清单包括

  • 稳定和改进导航库
  • 维护对 React Native 生态系统中库的支持
  • 改善向项目中添加原生库和桥接模块的体验

React Native 社区的公司和个人一直非常乐于奉献他们的时间和精力来改进我们都在使用的工具。如果您还没有参与开源,我希望您能考虑改进您使用的一些库的代码或文档。有很多文章可以帮助您入门,而且它可能比您想象的要容易得多!

为 React Native 构建 <InputAccessoryView>

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

动机

三年前,有人在 GitHub 上提出了一个 issue,要求支持来自 React Native 的输入配件视图。

在随后的几年中,出现了无数的 “+1s”、各种解决方法,以及关于此 issue 的 RN 的零实际更改 - 直到今天。从 iOS 开始,我们正在公开一个 API 以访问原生输入配件视图,我们很高兴分享我们是如何构建它的。

背景

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

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

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


在这种情况下,拥有 <InputAccessoryView> 的 Objective-C UIResponder 应该是明确的。<TextInput> 已成为第一响应者,在幕后,这变成了 UITextViewUITextField 的实例。

第二种常见情况是粘性文本输入

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


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

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 属性。这样做有效地将 ObjC 实现中的 <InputAccessoryView> 链接到 <TextInput>

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

陷阱

当然,在构建此 API 时并非一切都一帆风顺。以下是我们遇到的一些陷阱,以及我们如何修复它们。

构建此 API 的最初想法涉及监听 NSNotificationCenter 以获取 UIKeyboardWill(Show/Hide/ChangeFrame) 事件。这种模式在一些开源库中使用,并在 Facebook 应用程序的某些内部部分中使用。不幸的是,UIKeyboardDidChangeFrame 事件没有及时调用以在滑动时更新 <InputAccessoryView> 框架。此外,键盘高度的变化不会被这些事件捕获。这会产生一类像这样的错误

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


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

输入配件视图成功避免了 home pill,但现在不安全区域后面的内容是可见的。解决方案在于这个 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 版本中提供。

键盘操作愉快:)

将 AWS 与 React Native 结合使用

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

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) 代码与电子邮件或 SMS 一起使用。今天支持的类别有

  • 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 已禁用。这使得用于此类安全操作的 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 和全文搜索)组合为 schema。这使您可以混合和匹配数据源。AppSync 服务还可以从 schema 进行配置,因此如果您不熟悉 AWS 服务,您可以编写 GraphQL SDL,单击一个按钮,即可自动启动并运行。

AWS AppSync 中的实时功能通过 GraphQL 订阅以及众所周知的、基于事件的模式 进行控制。由于 AWS AppSync 中的订阅 在 schema 上使用 GraphQL 指令进行控制,并且 schema 可以使用任何数据源,这意味着您可以从数据库操作(使用 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,配置了数据库表,并自动为您连接了查询、mutation 和订阅。还有一个功能齐全的 AWS AppSync 的 React Native 示例,它利用了这个内置 schema(以及一个 React 示例),这使您可以在几分钟内运行您的客户端和云组件。

当您使用插入 Apollo ClientAWSAppSyncClient 时,入门很简单。AWSAppSyncClient 处理 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 配置的示例应用程序 schema。它不仅展示了与 DynamoDB 的交互,还包括数据分页(包括加密令牌)以及 EventsComments 之间的类型关系。由于该应用程序配置了 AWSAppSyncClient,因此数据会自动离线持久化,并在设备重新连接时同步。

您可以在此视频中深入了解此背后的客户端技术和一个 React Native 演示。

反馈

这些库背后的团队渴望听到这些库和服务如何为您工作。他们还想听取我们还可以做些什么来让您更轻松地使用云服务进行 React 和 React Native 开发。请在 GitHub 上联系 AWS Mobile 团队,了解 AWS AmplifyAWS AppSync

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

·11 分钟阅读
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 属性。children 会被 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 具有特殊的行为,允许你获得没有卡顿的动画。Animated 有一个名为 useNativeDriver 的标志,它会在动画开始时将你的动画定义从 JavaScript 发送到原生层,从而允许原生端处理动画的更新,而无需每帧都与 JavaScript 进行来回通信。useNativeDriver 的缺点是你只能更新一组特定的属性,主要是 transformopacity。你不能使用 useNativeDriver 来动画背景颜色之类的东西,至少目前还不行——我们会在稍后添加更多,当然,你始终可以为你项目所需的属性提交 PR,从而使整个社区受益 😀。

由于我们希望这个动画是流畅的,我们将在这些约束条件下工作。要更深入地了解 useNativeDriver 的底层工作原理,请查看我们宣布它的博客文章

分解我们的动画

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

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

使用 Animated,有两种主要方法来定义动画。第一种是使用 Animated.timing,它可以让你精确地指定动画运行的时间长度,以及一个缓动曲线来平滑运动。另一种方法是使用基于物理的 API,例如 Animated.spring。使用 Animated.spring,你可以指定弹簧中的摩擦力和张力等参数,并让物理规律运行你的动画。

我们有多个动画要同时运行,这些动画都彼此密切相关。例如,我们希望在遮罩正在中间显示时,应用程序开始淡入。因为这些动画密切相关,我们将使用 Animated.timing 和单个 Animated.Value

Animated.Value 是对原生值的包装,Animated 使用它来了解动画的状态。对于一个完整的动画,你通常只想拥有一个这样的值。大多数使用 Animated 的组件会将该值存储在 state 中。

由于我将此动画视为在整个动画过程中不同时间点发生的步骤,我们将从 0 开始我们的 Animated.Value,表示 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。

React Native 月刊 #6

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

React Native 每月例会仍在顺利进行!请务必查看本文底部的注释,了解下一次会议的信息。

Expo

  • 祝贺 Devin AbbottHoussein Djirdeh 预发布了《Full Stack React Native》一书!本书指导你通过构建几个小型应用程序来学习 React Native。你可以在购买本书之前在 https://www.fullstackreact.com/react-native/ 上试用这些应用程序。
  • 发布了 reason-react-native-scripts 的第一个(实验性)版本,以帮助人们轻松试用 ReasonML
  • Expo SDK 24 已发布!它使用 React Native 0.51,并包含许多新功能和改进:在独立应用程序中捆绑图像(无需在首次加载时缓存!)、图像处理 API(裁剪、调整大小、旋转、翻转)、人脸检测 API、新的发布渠道功能(为给定渠道设置活动发布和回滚)、用于跟踪独立应用程序构建的 Web 仪表板,以及修复了 OpenGL Android 实现和 Android 多任务处理器的长期存在的错误,仅举几例。
  • 从今年 1 月开始,我们将为 React Navigation 分配更多资源。我们坚信,仅使用 React 组件和诸如 Animatedreact-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# 和 Samsung Tizen 选项)来扩展该桥。

Wix

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

下次会议

已经有一些关于重新调整本次会议用途的讨论,以讨论一个单一且特定的主题(例如,导航、将 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。我们的路线图在 issue tracker 上公开。如果你想提出改进建议或提供反馈,请告诉我们!

Expo

  • 发布了 Expo SDK 22(使用 React Native 0.49)并为此更新了 CRNA
    • 包括改进的启动屏幕 API、基本的 ARKit 支持、“DeviceMotion” API、iOS11 上的 SFAuthenticationSession 支持以及 更多
  • 你的 snacks 现在可以有多个 JavaScript 文件,你可以通过将图像和其他资源拖动到编辑器中来上传它们。
  • react-navigation 做出贡献,以添加对 iPhone X 的支持。
  • 在用 Expo 构建大型应用程序时,我们将注意力集中在粗糙的边缘上。例如
    • 一流地支持部署到多个环境:暂存、生产和任意渠道。渠道将支持回滚和为给定渠道设置活动发布。如果你想成为早期测试人员,请告知我们 @expo_io
    • 我们还在努力改进我们的独立应用程序构建基础设施,并添加对在独立应用程序构建中捆绑图像和其他非代码资源的支持,同时保持通过 OTA 更新资源的能力。

Facebook

  • 更好的 RTL 支持
    • 我们正在引入许多方向感知样式。
      • 位置
        • (左|右) → (开始|结束)
      • 外边距
        • margin(左|右) → margin(开始|结束)
      • 内边距
        • padding(左|右) → padding(开始|结束)
      • 边框
        • borderTop(左|右)Radius → borderTop(开始|结束)Radius
        • borderBottom(左|右)Radius → borderBottom(开始|结束)Radius
        • border(左|右)Width → border(开始|结束)Width
        • border(左|右)Color → border(开始|结束)Color
    • 在 RTL 中,“左”和“右”的含义对于位置、外边距、内边距和边框样式进行了交换。在几个月内,我们将删除此行为,并使“左”始终意味着“左”,而“右”始终意味着“右”。破坏性更改隐藏在一个标志下。在你的 React Native 组件中使用 I18nManager.swapLeftAndRightInRTL(false) 来选择启用它们。
  • 正在开发 Flow 类型化我们的内部原生模块,并使用这些模块在 Java 中生成接口,在 ObjC 中生成原生实现必须实现的协议。我们希望这个代码生成器最早在明年开源。

Infinite Red

  • 用于帮助 React Native 和其他项目的新 OSS 工具。更多信息 here
  • 正在改进 Ignite,以发布新的样板代码(代号:Bowser)

Shoutem

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

下次会议

下次会议定于 2017 年 12 月 6 日星期三举行。如果你对我们应该如何改进会议的输出有任何建议,请随时在 Twitter 上 ping 我。

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 Mansion 的 Krzysztof Magiera 继续推进这项工作,我们一直在帮助他进行测试并资助他的一部分开发时间。在 SDK21 中将其集成到 Expo 中将使人们能够在 Snack 中轻松地使用它,因此我们很高兴看到人们会想出什么。
  • 关于改进的错误日志记录/处理 - 有关日志记录的详细信息,请参阅 此内部 Expo PR 的 gist(特别是“问题 2”),以及 此提交,了解处理导入 npm 标准库模块失败的尝试的更改。在这种方式下,有很多机会改进 React Native 中上游的错误消息,我们将致力于后续的上游 PR。社区参与进来也很棒。
  • native.directory 继续增长,你可以从 GitHub repo 添加你的项目。
  • 访问北美各地的黑客马拉松,包括 PennAppsHack The NorthHackMIT,以及即将到来的 MHacks

Facebook

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

Microsoft

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

下次会议

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