跳到主要内容

开源路线图

·5 分钟阅读
Héctor Ramos
Facebook 工程师

今年,React Native 团队专注于大规模的 React Native 重新架构。正如 Sophie 在她的 React Native 2018 状态 文章中提到的那样,我们已经草拟了一个计划,以便更好地支持 Facebook 以外蓬勃发展的 React Native 用户和协作者群体。现在是时候分享更多关于我们一直在做的工作的细节了。在此之前,我想阐述一下我们对 React Native 在开源领域的长期愿景。

我们对 React Native 的愿景是...

  • 一个健康的 GitHub 仓库。 问题和拉取请求在合理的时间段内得到处理。
    • 增加测试覆盖率。
    • 从 Facebook 代码仓库同步出来的提交不应破坏开源测试。
    • 更高规模的有意义的社区贡献。
  • 稳定的 API, 使其更容易与开源依赖项接口。
    • Facebook 使用与开源相同的公共 API
    • 遵循语义版本控制的 React Native 发布版本。
  • 一个充满活力的生态系统。 由社区维护的高质量 ViewManager、原生模块和多平台支持。
  • 优秀的文档。 专注于帮助用户创造高质量的体验,以及最新的 API 参考文档。

我们已经确定了以下重点领域,以帮助我们实现这一愿景。

✂️ 精简核心

我们的目标是 通过移除非核心和未使用的组件来减少 React Native 的表面积。我们将把非核心组件转移到社区,以使其能够更快地发展。减少的表面积将使管理对 React Native 的贡献变得更容易。

WebView 是我们转移到社区的组件的一个例子。我们正在开发一个工作流程,允许内部团队在我们将这些组件从仓库中移除后继续使用它们。我们已经确定了 数十个更多组件,我们将把所有权交给社区。

🎁 开源内部工具和 🛠 更新的工具

Facebook 产品团队的 React Native 开发体验可能与开源社区非常不同。在开源社区中可能流行的工具在 Facebook 中不使用。可能有一个内部工具可以达到相同的目的。在某些情况下,Facebook 团队已经习惯了 Facebook 外部不存在的工具。当我们开源我们即将到来的架构工作时,这些差异可能会带来挑战。

我们将致力于发布一些内部工具。我们还将改进对开源社区流行的工具的支持。以下是我们将要解决的非详尽的项目列表

  • 开源 JSI 并使社区能够引入他们自己的 JavaScript VM,替换 RN 初始版本中的现有 JavaScriptCore。我们将在未来的文章中介绍 JSI 是什么,同时您可以从 Parashuram 在 React Conf 上的演讲 中了解更多关于 JSI 的信息。
  • 在 Android 上支持 64 位库。
  • 在新架构下启用调试。
  • 改进对 CocoaPods、Gradle、Maven 和新 Xcode 构建系统的支持。

✅ 测试基础设施

当 Facebook 工程师发布代码时,如果它通过所有测试,则认为可以安全地发布。这些测试确定更改是否可能破坏我们自己的 React Native 表面。然而,Facebook 使用 React Native 的方式存在差异。这使得我们不知不觉地在开源中破坏了 React Native。

我们将加强我们的内部测试,以确保它们在尽可能接近开源的环境中运行。这将有助于防止破坏这些测试的代码进入开源。我们还将致力于基础设施,以支持在 GitHub 上更好地测试核心仓库,使未来的拉取请求能够轻松地包含测试。

结合减少的表面积,这将使贡献者能够更快、更有信心地合并拉取请求。

📜 公共 API

Facebook 将通过公共 API 使用 React Native,就像开源社区一样,以减少意外的破坏性更改。我们已经开始转换内部调用站点来解决这个问题。我们的目标是收敛到一个稳定的公共 API,从而在 1.0 版本中采用语义版本控制。

📣 沟通

React Native 是 GitHub 上按贡献者数量计算的 顶级开源项目之一。这让我们非常高兴,我们希望继续保持下去。我们将继续致力于促进贡献者参与的倡议,例如提高透明度和公开讨论。文档是新接触 React Native 的人首先遇到的事情之一,但它一直不是优先事项。我们希望解决这个问题,首先是恢复自动生成的 API 参考文档,创建更多专注于创建 高质量用户体验 的内容,并改进我们的 发布说明

时间线

我们计划在接下来的一年左右的时间里完成这些项目。其中一些工作已经在进行中,例如 已经登陆开源的 JSI。其他一些项目将需要更长的时间才能完成,例如减少表面积。我们将尽最大努力让社区及时了解我们的进展。请加入我们在 讨论和提案 仓库中的讨论,这是 React Native 社区的一项倡议,促成了本路线图中讨论的几项倡议的创建。

引入新的 iOS WebViews

·2 分钟阅读
Facebook 软件工程师

长期以来,Apple 一直不鼓励使用 UIWebView,而推荐使用 WKWebView。在即将发布的 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 发送时,我们依赖于一种 hack 方法将其传递给 React Native。简而言之,我们将消息数据编码到一个具有特殊方案的 url 中,并将 WebView 导航到该 url。在原生端,我们拦截并取消了此导航,从 url 解析数据,最后调用到 React Native。此实现容易出错且不安全。我很高兴地宣布,我们已经利用 WKWebView 功能完全替换了它。

WKWebView 相对于 UIWebView 的其他优势包括更快的 JavaScript 执行和多进程架构。请参阅此 2014 年 WWDC 了解更多详情。

注意事项

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

行为不一致

automaticallyAdjustContentInsetscontentInsets (提交)

当您向 WKWebView 添加 contentInsets 时,它不会更改 WKWebView 的视口。视口保持与帧相同的大小。对于 UIWebView,视口大小实际上会改变(如果内容插页为正数,则会变小)。

backgroundColor (提交)

使用新的 iOS WebView 实现,如果您使用此属性,您的背景颜色可能会闪烁到视图中。此外,WKWebView 渲染透明背景的方式与 UIWebView 不同。请查看提交描述了解更多详情。

不支持

scalesPageToFit (提交)

WKWebView 不支持 scalesPageToFit 属性,因此我们无法在 WebView React Native 组件上实现此功能。

无障碍功能 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 允许传入 traits 数组或单个 trait,而 accessibilityComponentType 仅允许单个值。
  2. Android 上的功能非常有限。 使用旧属性,Talkback 能够识别的唯一 UI 元素是“button”、“radiobutton_checked”和“radiobutton_unchecked”。

问题二:不存在的无障碍功能提示:

无障碍功能提示帮助使用 TalkBack 或 VoiceOver 的用户了解当他们对无障碍功能元素执行操作时会发生什么,而这些操作仅通过无障碍功能标签是不明显的。这些提示可以在设置面板中打开和关闭。以前,React Native 的 API 根本不支持无障碍功能提示。

问题三:忽略反转颜色:

一些视力障碍用户在其手机上使用反转颜色以获得更高的屏幕对比度。Apple 为 iOS 提供了一个 API,允许开发人员忽略某些视图。这样,当用户启用反转颜色设置时,图像和视频不会失真。React Native 当前不支持此 API。

新 API 的设计

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

为了解决 accessibilityComponentTypeaccessibilityTraits 之间的混淆,我们决定将它们合并为一个属性。这是有道理的,因为它们在技术上具有相同的预期功能,并且通过合并它们,开发人员在构建无障碍功能时不再需要担心平台特定的复杂性。

背景

在 iOS 上,UIAccessibilityTraits 是可以在任何 NSObject 上设置的属性。通过 javascript 属性传递到原生的 17 个 traits 中的每一个都映射到 Objective-C 中的 UIAccessibilityTraits 元素。Traits 每个都由一个长整型表示,并且设置的每个 trait 都被 OR 在一起。

然而,在 Android 上,AccessibilityComponentType 是 React Native 提出的概念,并不直接映射到 Android 中的任何属性。无障碍功能由无障碍功能委托处理。每个视图都有一个默认的无障碍功能委托。如果您想自定义任何无障碍功能操作,您必须创建一个新的无障碍功能委托,覆盖您想要自定义的特定方法,然后设置您正在处理的视图的无障碍功能委托以与新的委托关联。当开发人员设置 AccessibilityComponentType 时,原生代码会基于传入的组件创建一个新的委托,并将视图设置为具有该无障碍功能委托。

所做的更改

对于我们的新属性,我们想要创建这两个属性的超集。我们决定保持新属性主要模仿现有属性 accessibilityTraits,因为 accessibilityTraits 具有明显更多的值。Android 上这些 traits 的功能将通过修改无障碍功能委托来 polyfill。

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

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 上提示的行为,但这种实现的缺点是这些提示无法像在 iOS 上那样在 Android 的设置中关闭。

我们在 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. 使用手动 codemod

对于使用 AccessibilityTraits 但没有 AccessibilityRole 对应值的情况,以及将多个 traits 传入 AccessibilityTraits 的情况,必须手动完成 codemod。

通常,

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

将手动替换为

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

这些属性已经在 Facebook 的代码库中使用。Facebook 的 codemod 非常简单。jscodeshift 脚本修复了我们大约一半的实例,另一半是手动修复的。总的来说,整个过程花费了不到几个小时。

希望您会发现更新后的 API 很有用!请继续制作无障碍应用!#inclusion

发布 0.56

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

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

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

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

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

0.56.0 完美吗?不,就像所有软件一样:但是我们已经达到了一个平衡点,即“等待更多稳定性”与“测试导致成功结果,因此我们可以向前推进”之间的权衡,我们感觉可以发布它了。此外,我们意识到 of a few issues 在最终的 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 版本控制中,因为仍然有很多更改正在进行中 - 因此请记住,升级时,可能,某些东西仍然可能崩溃或损坏。在 issues 中以及提交 PR 时,请互相帮助 - 并记住遵守强制执行的CoC:屏幕的另一端始终是一个人。

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 之间的直接调用更有效,并将使构建跨语言堆栈跟踪等调试工具更容易。

一旦这些更改完成,更紧密的集成将成为可能。如今,如果不进行复杂的 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 多次提交。

将 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 年强大的以 Web 为中心的业务,并于 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 都会修复一些错误,但会引入更多错误。例如,Remote JS Debugging 在 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”、各种解决方法以及对 RN 的零具体更改——直到今天。从 iOS 开始,我们正在公开一个 API 以访问原生输入配件视图,我们很高兴分享我们是如何构建它的。

背景

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

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

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


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

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

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


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

API 设计

我们现在知道 <InputAccessoryView> 是什么,以及我们想如何使用它。下一步是设计一个对两种用例都有意义的 API,并且可以与现有的 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 属性。这样做有效地将 <InputAccessoryView> 链接到 <TextInput> 在它们的 ObjC 实现中。

支持粘性文本输入(场景 2)添加了一些更多约束。对于此设计,输入配件视图有一个文本输入作为子项,因此通过 nativeID 链接不是一种选择。相反,我们将通用屏幕外 UIView.inputAccessoryView 设置为我们的原生 <InputAccessoryView> 层次结构。通过手动告诉这个通用的 UIView 成为第一响应者,层次结构由响应者基础设施挂载。上述博文中彻底解释了这一概念。

陷阱

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

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

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


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

输入附件视图成功避开了 Home Pill,但现在不安全区域后面的内容是可见的。解决方案就在这个 雷达 中。我将原生 <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 中,以便您可以在不弹出的情况下使用 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 示例模式,您可以启动它。此示例部署 GraphQL 模式,预置数据库表,并自动为您连接查询、mutation 和订阅。还有一个功能齐全的 React Native 示例用于 AWS AppSync,它利用了这个内置模式(以及一个 React 示例),使您可以在几分钟内启动并运行客户端和云组件。

当您使用 AWSAppSyncClient 时,入门非常简单,它插入到 Apollo Client 中。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 预置的示例应用程序模式的查询。它不仅展示了与 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 接受 props 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.ValueAnimated.timing

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

由于我将此动画视为在完整动画的不同时间点发生的步骤,因此我们将 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 徽标变小的过程中保持不透明。根据官方动画,我希望在小鸟放大一半时开始显示它,并在整体动画的 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。