跳至主要内容

26 篇包含“工程”标签的文章

查看所有标签

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

·阅读时长: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。子元素被 maskElement 遮罩。请注意,遮罩不需要是图像,它可以是任何任意视图。上面示例的行为是渲染蓝色视图,但它仅在 maskElement 中“基本遮罩”文字所在的位置可见。我们只是创建了复杂的蓝色文本。

我们要做的是渲染我们的蓝色层,然后在顶部使用 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.Value来配合Animated.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 则不够大 😀。不过,有趣的是,数字越大,它看起来增长的速度就越快,因为它必须在相同的时间内到达那里。这个数字需要一些反复试验才能使这个 logo 看起来不错。不同尺寸的 logo/设备将需要不同的最终缩放比例,以确保显示整个屏幕。

应用程序应该保持不透明一段时间,至少在 Twitter logo 缩小期间。根据官方动画,我希望在鸟的缩放比例达到一半时开始显示它,并在整个动画进行到 30% 时完全显示它。所以在 15% 时开始显示,在整个动画进行到 30% 时完全可见。

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

现在,在代码中。

我们在上面做的本质上是将动画进度百分比的值映射到各个部分的值。我们使用 Animated 的 .interpolate 来做到这一点。我们创建了三个不同的样式对象,每个动画部分一个,使用基于 this.state.loadingProgress 的插值值。

const loadingProgress = this.state.loadingProgress;

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

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

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

现在我们有了这些样式对象,我们可以在渲染帖子前面提到的视图片段时使用它们。请注意,只有 Animated.ViewAnimated.TextAnimated.Image 才能使用使用 Animated.Value 的样式对象。

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

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

耶!我们现在已经让动画片段看起来像我们想要的那样了。现在我们只需要清理我们永远不会再看到的蓝色和白色图层。

为了知道何时可以清理它们,我们需要知道动画何时完成。幸运的是,在我们调用 Animated.timing.start 时,它接受一个可选的回调函数,该函数在动画完成后运行。

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

现在我们在 state 中有一个值来知道我们是否完成了动画,我们可以修改我们的蓝色和白色图层来使用它。

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

瞧!我们的动画现在可以工作了,并且在动画完成后,我们清理了未使用的图层。我们已经构建了 Twitter 应用程序加载动画!

但是等等,我的不起作用!

别担心,亲爱的读者。我也讨厌指南只提供代码片段而不提供完整的源代码。

此组件已发布到 npm 并托管在 GitHub 上,地址为 react-native-mask-loader。要尝试在您的手机上使用它,可以在 Expo 上找到它。

更多阅读/额外练习

  1. 这本 GitBook 是一个学习 Animated 的好资源,在阅读 React Native 文档后可以参考它。
  2. 实际的 Twitter 动画似乎在结束时加快了遮罩揭示的速度。尝试修改加载器以使用不同的缓动函数(或弹簧!)以更好地匹配该行为。
  3. 遮罩的当前最终缩放比例是硬编码的,在平板电脑上可能无法显示整个应用程序。根据屏幕尺寸和图像尺寸计算最终缩放比例将是一个很棒的 PR。

React Native 每月简报 #6

·阅读时间: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 组件和诸如 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 运行时和 UIManagers(考虑 JavaScriptCore、Xamarin.Mac、Linux Gtk# 和 Samsung Tizen 选项)扩展桥接。

Wix

  • Detox
    • 为了让我们能够扩展 E2E 测试,我们希望最大程度地减少 CI 上花费的时间,我们正在研究对 Detox 的并行化支持。
    • 提交了一个 pull request 以启用对自定义风味构建的支持,以便更好地支持 E2E 模拟。
  • DetoxInstruments
    • 事实证明,DetoxInstruments 的杀手级功能是一个非常具有挑战性的任务,在任何给定时间获取 JavaScript 回溯都需要自定义 JSCore 实现来支持 JS 线程挂起。在 Wix 的应用程序内部测试分析器揭示了关于 JS 线程的有趣见解。
    • 该项目尚未稳定到可以普遍使用,但正在积极开发中,我们希望很快宣布它。
  • React Native Navigation
    • V2 的开发速度已大幅提高,到目前为止,我们只有一名开发人员将其 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、iOS 11 上的 SFAuthenticationSession 支持,以及 更多 功能。
  • 您的 Snack 现在可以拥有多个 JavaScript 文件,并且您可以通过将图像和其他资源拖放到编辑器中来上传它们。
  • react-navigation 做出贡献以添加对 iPhone X 的支持。
  • 将我们的注意力集中在使用 Expo 构建大型应用程序时的粗糙边缘上。例如
    • 对部署到多个环境(暂存、生产和任意渠道)提供一流的支持。渠道将支持回滚并为给定渠道设置活动版本。如果您想成为早期测试者,请告诉我们,@expo_io
    • 我们还在努力改进我们的独立应用程序构建基础设施,并在独立应用程序构建中添加对捆绑图像和其他非代码资源的支持,同时保留通过无线方式更新资源的功能。

Facebook

  • 更好的 RTL 支持
    • 我们正在引入许多方向感知样式。
      • 位置
        • (左|右)→(开始|结束)
      • 边距
        • margin(Left|Right) → margin(Start|End)
      • 填充
        • padding(Left|Right) → padding(Start|End)
      • 边框
        • borderTop(Left|Right)Radius → borderTop(Start|End)Radius
        • borderBottom(Left|Right)Radius → borderBottom(Start|End)Radius
        • border(Left|Right)Width → border(Start|End)Width
        • border(Left|Right)Color → border(Start|End)Color
    • 在 RTL 中,位置、边距、填充和边框样式的“左”和“右”含义已互换。在几个月内,我们将删除此行为,并使“左”始终表示“左”,“右”始终表示“右”。中断性更改隐藏在标志下。在您的 React Native 组件中使用 I18nManager.swapLeftAndRightInRTL(false) 来选择加入它们。
  • 正在使用 Flow 为我们的内部原生模块编写类型,并使用这些类型在 Java 中生成接口以及 ObjC 中的协议,原生实现必须实现这些接口和协议。我们希望此代码生成最早明年开源。

Infinite Red

  • 用于帮助 React Native 和其他项目的新的开源工具。更多信息请访问 此处
  • 为新的样板版本(代号:Bowser)重新设计 Ignite

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

Facebook

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

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 个左右,并且看起来可能会持续一段时间。在过去的一个月里,我们关闭了 690 个由于缺乏活动而产生的问题(定义为在过去 60 天内没有评论)。在这 690 个问题中,有 58 个由于各种原因被重新打开(维护人员承诺提供修复,或贡献者提出了保持问题打开的充分理由)。
  • 在可预见的未来,我们计划继续自动关闭陈旧的问题。我们希望能够处理跟踪器中打开的每个有影响的问题,但我们还没有达到那个状态。我们需要维护人员提供一切可能的帮助来分类问题,并确保我们不会错过导致回归或引入重大更改的问题,尤其是那些影响新创建项目的问题。有兴趣提供帮助的人可以使用 Facebook GitHub 机器人对问题和拉取请求进行分类。新的维护人员指南包含更多关于分类和使用 GitHub 机器人的信息。请将自己添加到问题工作组并鼓励其他活跃的社区成员也这样做!

Microsoft

  • 新的 Skype 应用构建在 React Native 之上,以便尽可能地在平台之间共享代码。基于 React Native 的 Skype 应用目前已在 Android 和 iOS 应用商店中提供。
  • 在使用 React Native 构建 Skype 应用时,我们会向 React Native 发送拉取请求以解决我们遇到的错误和缺失的功能。到目前为止,我们已经获得了大约70 个合并的拉取请求
  • React Native 使我们能够使用相同的代码库为 Android 和 iOS Skype 应用提供支持。我们还想使用该代码库为 Skype 网页应用提供支持。为了帮助我们实现这一目标,我们在 React/React Native 之上构建并开源了一个薄层,称为ReactXP。ReactXP 提供了一组跨平台组件,在针对 iOS/Android 时映射到 React Native,在针对 Web 时映射到 react-dom。ReactXP 的目标与另一个名为 React Native for Web 的开源库类似。ReactXP 常见问题解答中简要描述了这些库方法的不同之处。

Shoutem

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

下次会议

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

Marketplace 中的 React Native 性能

·阅读时间:5 分钟
Facebook 软件工程师

React Native 在 Facebook 家族的多个应用中多个地方使用,包括 Facebook 主应用中的顶级标签。我们这篇博文的重点是一个高度可见的产品,Marketplace。它在十几个国家/地区可用,使用户能够发现其他用户提供的产品和服务。

在 2017 年上半年,通过 Relay 团队、Marketplace 团队、移动 JS 平台团队和 React Native 团队的共同努力,我们将 Android2010-11 年产品线的设备的 Marketplace 交互时间 (TTI) 缩短了一半。Facebook 从历史上将这些设备视为低端 Android 设备,并且它们在任何平台或设备类型上的 TTI 速度都最慢。

一个典型的 React Native 启动过程如下所示

免责声明:比率不具有代表性,并且会根据 React Native 的配置和使用方式而有所不同。

在运行产品特定的 JavaScript 之前,我们首先初始化 React Native 核心(又名“桥”),该 JavaScript 确定 React Native 将在原生处理时间内呈现哪些原生视图。

不同的方法

我们早期犯的一个错误是让Systrace 和 CTScan来驱动我们的性能工作。这些工具帮助我们在 2016 年找到了许多唾手可得的成果,但我们发现 Systrace 和 CTScan **不能代表生产场景**,也无法模拟实际情况。时间在各个部分的占比通常是不正确的,有时甚至完全偏离。在极端情况下,我们预计需要几毫秒才能完成的一些操作实际上需要数百或数千毫秒。也就是说,CTScan 很有用,我们发现它在生产之前捕获了三分之一的回归。

在 Android 上,我们将这些工具的缺点归因于以下事实:1) React Native 是一个多线程框架,2) Marketplace 与众多复杂的视图(如信息流和其他顶级标签)共存,以及 3) 计算时间差异很大。因此,在本学期,我们让生产测量和分解来驱动我们几乎所有的决策和优先级排序。

生产检测之路

在表面上,生产检测听起来可能很简单,但事实证明这是一个相当复杂的过程。它经历了多个 2-3 周的迭代周期;由于将提交合并到主分支的延迟,以及将应用推送到 Play 商店以及收集足够的生产样本以对我们的工作充满信心。每个迭代周期都涉及发现我们的分解是否准确,它们是否具有合适的粒度,以及它们是否正确地加起来构成整个时间段。我们不能依赖 Alpha 和 Beta 版本,因为它们不能代表普通用户。从本质上讲,我们非常细致地根据数百万个样本的聚合构建了一个非常精确的生产跟踪。

我们一丝不苟地验证每个分解中的毫秒是否都正确地加起来构成其父指标的原因之一是,我们很早就意识到我们的检测中存在差距。事实证明,我们最初的分解没有考虑线程跳转引起的停顿。线程跳转本身并不昂贵,但线程跳转到已经忙于工作的繁忙线程是非常昂贵的。我们最终通过在正确的时间点散布 Thread.sleep() 调用在本地复制了这些阻塞,并且我们设法通过以下方法修复了它们

  1. 删除我们对 AsyncTask 的依赖关系,
  2. 撤消在 UI 线程上强制初始化 ReactContext 和 NativeModules 的操作,以及
  3. 删除在初始化时测量 ReactRootView 的依赖关系。

总的来说,删除这些线程阻塞问题将启动时间缩短了 25% 以上。

生产指标也挑战了我们之前的一些假设。例如,我们过去习惯在启动路径上预加载许多 JavaScript 模块,假设将模块放在一个包中可以降低它们的初始化成本。但是,预加载和将这些模块放在一个包中的成本远远超过了收益。通过重新配置我们的内联 require 黑名单并从启动路径中删除 JavaScript 模块,我们能够避免加载不必要的模块,例如 Relay Classic(当只需要Relay Modern时)。如今,我们的 RUN_JS_BUNDLE 分解速度提高了 75% 以上。

我们还在调查特定于产品的原生模块时发现了成果。例如,通过延迟注入原生模块的依赖项,我们将该原生模块的成本降低了 98%。通过消除 Marketplace 启动与其他产品的争用,我们将启动时间缩短了相同的时间间隔。

最棒的是,许多这些改进广泛适用于使用 React Native 构建的所有屏幕。

结论

人们认为 React Native 启动性能问题是由 JavaScript 速度慢或网络时间过长造成的。虽然加快 JavaScript 等内容的速度会使 TTI 下降一个不小的数字,但这些内容对 TTI 的贡献比例远低于之前的预期。

到目前为止,教训是**测量、测量、测量!**一些成果来自将运行时成本转移到构建时间,例如 Relay Modern 和延迟 NativeModules。其他成果来自通过更智能地并行化代码或删除死代码来避免工作。还有一些成果来自对 React Native 进行的大规模架构更改,例如清理线程阻塞。性能没有万能的解决方案,长期性能提升将来自增量的检测和改进。不要让认知偏差影响你的决策。相反,要仔细收集和解读生产数据来指导未来的工作。

未来计划

从长远来看,我们希望 Marketplace 的 TTI 能与使用原生构建的类似产品相媲美,并且总体而言,React Native 的性能能够与原生性能相媲美。此外,虽然在本学期我们通过诸如Prepack之类的项目以及更多构建时间处理,将桥接启动成本大幅降低了约 80%,但我们计划将 React Native 桥接的成本降至接近零。

React Native 每月简报 #2

·阅读时长:8 分钟
Tomislav Tenodi
Shoutem 产品经理

React Native 每月会议继续进行!在本届会议上,我们邀请了Infinite Red,他们是Chain React(React Native 大会)背后的天才。由于这里的大多数人都将在 Chain React 上发表演讲,因此我们将会议推迟了一周。会议演讲已在线发布,我鼓励您查看它们。那么,让我们看看我们的团队在做什么。

团队

在第二次会议上,有 9 个团队加入了我们

会议记录

以下是每个团队的会议记录

Airbnb

Callstack

  • Mike Grabowski 一直像往常一样管理着 React Native 的每月发布,包括发布了一些 Beta 版本。特别是,正在努力将 v0.43.5 版本发布到 npm,因为它可以解除 Windows 用户的限制!
  • Haul上正在进行缓慢但持续的工作。有一个拉取请求添加了 HMR,并且已发布了其他改进。最近让一些行业领导者采用了它。可能计划在该领域开始全职付费工作。

  • 来自 Jest 团队的 Michał Pierzchała 本月加入了 Callstack。他将帮助维护 Haul,并可能参与 Metro BundlerJest 的开发。
  • Satyajit Sahoo 现在也加入我们了,耶!
  • 我们的开源部门即将推出一些很酷的东西。特别是,正在努力将 Material Palette API 引入 React Native。计划最终发布我们的原生 iOS 工具包,旨在提供与原生组件 1:1 的外观和感觉。

Expo

  • 最近发布了 Native Directory,以帮助提高 React Native 生态系统中库的发现性和评估性。问题是:库太多,难以测试,需要手动应用启发式方法,而且并不立即清楚哪些库是最佳的、应该使用的。也很难知道某个库是否与 CRNA/Expo 兼容。因此,Native Directory 试图解决这些问题。请查看它并 添加您的库。库的列表 在这里。这只是我们第一次尝试,我们希望它由社区拥有和运营,而不仅仅是 Expo 团队。因此,如果您认为这很有价值并希望改进它,请参与进来!
  • Snack 中添加了对安装 npm 包的初步支持(使用 Expo SDK 19)。如果您遇到任何问题,请告知我们,我们仍在解决一些 bug。与 Native Directory 结合使用,这应该可以轻松测试仅具有 JS 依赖项或包含在 Expo SDK 中的依赖项的库。试试看。
  • 发布了 Expo SDK19,其中包含大量改进,并且我们现在使用的是 更新后的 Android JSC
  • Alexander Kotliarskyi 合作,正在编写一份文档指南,其中包含一系列关于如何提升应用程序用户体验的技巧。请加入我们,添加或帮助撰写其中的一些内容!
  • 继续开发:音频/视频、相机、手势(与 Software Mansion 合作,react-native-gesture-handler)、GL 相机集成,并希望在 SDK20(8 月)中首次推出其中一些功能,并在那时对其他功能进行重大改进。我们才刚刚开始在 Expo 客户端中构建用于后台工作的基础设施(地理位置、音频、处理通知等)。
  • Adam Miskiewiczreact-navigation 中模拟 UINavigationController 的过渡方面取得了一些不错的进展。请查看他 推文 中的早期版本 - 即将发布。还可以查看 MaskedViewIOS,他已将其 上游提交。如果您有能力和愿望为 Android 实现 MaskedView,那就太棒了!

Facebook

  • Facebook 内部正在探索能否在 React Native 中嵌入原生 ComponentKitLitho 组件。
  • 非常欢迎您为 React Native 做出贡献!如果您想知道如何贡献,“如何贡献”指南 描述了我们的开发流程并概述了发送您的第一个拉取请求的步骤。还有其他不需要编写代码的贡献方式,例如对问题进行分类或更新文档。
    • 在撰写本文时,React Native 有 635未解决的问题249未合并的拉取请求。这对我们的维护人员来说不堪重负,并且当内部修复问题时,很难确保相关任务得到更新。
    • 我们不确定在保持社区满意度的同时处理此问题的最佳方法是什么。一些(但并非所有!)选项包括关闭陈旧的问题、授予更多人员管理问题的权限以及自动关闭不符合问题模板的问题。我们编写了一份“对维护人员的期望”指南,以设定期望并避免意外。如果您对如何改善维护人员的体验以及如何确保提出问题和拉取请求的人员感到被倾听和重视有任何想法,请告诉我们!

GeekyAnts

  • 我们在 Chain React 上演示了与 React Native 文件一起使用的设计器工具。许多与会者报名参加了候补名单。
  • 我们还在研究其他跨平台解决方案,例如 Google Flutter(即将进行重大比较)、Kotlin NativeApache Weex,以了解架构差异以及我们可以从中学习什么来提高 React Native 的整体性能。
  • 我们的大多数应用程序都切换到 react-navigation,这提高了整体性能。
  • 此外,还宣布了 NativeBase Market - 一个面向 React Native 组件和应用程序的市场(由开发者为开发者创建)。

Infinite Red

Microsoft

  • CodePush 现已集成到 Mobile Center 中。现有用户的工作流程不会发生任何变化。
    • 一些用户报告了重复应用程序的问题 - 他们已经在 Mobile Center 上拥有了一个应用程序。我们正在努力解决这些问题,但如果您有两个应用程序,请告知我们,我们可以为您合并它们。
  • Mobile Center 现在支持 CodePush 的推送通知。我们还展示了如何将通知和 CodePush 结合起来用于应用程序的 A/B 测试 - 这是 ReactNative 架构独有的功能。
  • VS Code 在 ReactNative 中存在一个已知的调试问题 - 几天后发布的扩展程序的下一个版本将解决此问题。
  • 由于微软内部还有许多其他团队也在开发 React Native,因此我们将努力让所有团队在下次会议中获得更好的代表权。

Shoutem

  • 完成了在 Shoutem 上简化 React Native 开发流程的过程。在 Shoutem 上开发应用程序时,您可以使用所有标准的 react-native 命令。
  • 我们做了很多工作试图弄清楚如何在 React Native 上最好地进行分析。很大一部分 文档 已过时,我们将尽最大努力在官方文档上创建一个拉取请求,或者至少在博文中写下我们的一些结论。
  • 我们正在将导航解决方案切换到 react-navigation,因此我们可能很快就会收到一些反馈。
  • 我们在我们的工具包中发布了一个 新的 HTML 组件,它将原始 HTML 转换为 React Native 组件树。

Wix

  • 我们开始与 react-native-repackager 功能一起为 Metro Bundler 提交拉取请求。我们更新了 react-native-repackager 以支持 RN 44(我们在生产中使用)。我们将其用于 detox 的模拟基础设施。
  • 在过去的三个星期里,我们一直在用 detox 测试覆盖 Wix 应用程序。这是一个关于如何在如此规模的应用程序(超过 40 位工程师)中减少手动 QA 的绝佳学习体验。因此我们解决了一些 detox 的问题,一个新版本刚刚发布。我很高兴地报告,我们正在践行“零不稳定性政策”,到目前为止,测试一直稳定地通过。
  • Android 版 Detox 发展顺利。我们得到了社区的大力帮助。我们预计大约两周后发布初始版本。
  • DetoxInstruments,我们的性能测试工具,正在变得比我们最初预期的更大。我们现在计划将其变成一个独立的工具,不会与 detox 密切耦合。它将允许调查 iOS 应用程序的整体性能。它也将与 detox 集成,以便我们可以对性能指标运行自动化测试。

下次会议

下一届会议定于 2017 年 8 月 16 日举行。由于这只是我们的第二次会议,我们想知道这些记录如何使 React Native 社区受益。如果您对我们如何改进会议输出有任何建议,请随时在 Twitter 上联系我

React Native 每月简报 #1

·阅读时长 6 分钟
Tomislav Tenodi
Shoutem 产品经理

Shoutem,我们很幸运能从 React Native 的早期就开始使用它。我们决定从第一天起就成为这个很棒社区的一部分。很快,我们意识到几乎不可能跟上社区发展和改进的速度。因此,我们决定组织一个每月会议,让所有主要的 React Native 贡献者能够简要介绍他们的工作成果和计划。

每月会议

我们在2017年6月14日举行了 React Native 每月会议的第一次会议。React Native 每月会议的目标简单明了:**提升 React Native 社区**。通过展示团队的工作成果,方便团队之间进行离线协作。

团队

在第一次会议上,有8个团队加入了我们。

我们希望更多核心贡献者加入接下来的会议!

会议记录

由于团队的计划可能对更广泛的受众感兴趣,我们将在 React Native 博客上分享这些计划。以下是计划内容:

Airbnb

  • 计划为 `View` 和 `AccessibilityInfo` 原生模块添加一些 A11y(辅助功能)API。
  • 将研究在 Android 上的原生模块中添加一些 API,以便指定它们运行的线程。
  • 一直在研究潜在的初始化性能改进。
  • 一直在研究一些更复杂的打包策略,用于在“unbundle”的基础上使用。

Callstack

  • 正在研究通过使用 Detox 进行端到端测试来改进发布流程。Pull Request 很快就会提交。
  • 他们一直在开发的 Blob Pull Request 已合并,后续 Pull Request 将陆续提交。
  • 在内部项目中增加 Haul 的采用率,以了解其与 Metro Bundler 相比的性能表现。与 Webpack 团队一起改进多线程性能。
  • 在内部,他们已经实现了一个更好的基础架构来管理开源项目。计划在接下来的几周内发布更多内容。
  • React Native Europe 大会正在筹备中,目前还没有什么有趣的消息,但欢迎大家参加!
  • 暂时停止了对 react-navigation 的开发,转而研究替代方案(尤其是原生导航)。

Expo

Facebook

  • React Native 的打包器现在是 Metro Bundler,位于一个独立的仓库中。伦敦的 Metro Bundler 团队很高兴能够满足社区的需求,提高模块化以支持 React Native 之外的其他用例,并提高对问题和 PR 的响应速度。
  • 在未来几个月,React Native 团队将致力于改进基本组件的 API。期待在布局问题、辅助功能和 Flow 类型方面有所改进。
  • React Native 团队还计划今年改进核心模块化,通过重构来完全支持 Windows 和 macOS 等第三方平台。

GeekyAnts

  • 团队正在开发一个 UI/UX 设计应用(代号:Builder),该应用直接使用 `.js` 文件。目前,它只支持 React Native。它类似于 Adobe XD 和 Sketch。
  • 团队正在努力,以便您可以在编辑器中加载现有的 React Native 应用,进行更改(以设计师的方式进行视觉更改),并将更改直接保存到 JS 文件中。
  • 大家正在努力缩小设计师和开发者之间的差距,并将他们带到同一个仓库中。
  • 此外,NativeBase 最近在 GitHub 上获得了 5000 颗星。

Microsoft

  • CodePush 现已集成到 Mobile Center 中。这是迈向提供与分发、分析和其他服务更集成体验的第一步。请查看他们的公告 此处
  • VS Code 在调试方面存在一个 bug,他们正在努力修复它,并很快发布新的版本。
  • 正在研究 Detox 用于集成测试,并查看 JSC Context 以获取崩溃报告中的变量。

Shoutem

  • 使使用 React Native 社区的工具处理 Shoutem 应用更容易。您将能够使用所有 React Native 命令来运行在 Shoutem 上创建的应用。
  • 正在研究 React Native 的性能分析工具。他们在设置方面遇到很多问题,他们将记录他们在过程中发现的一些见解。
  • Shoutem 正在努力使 React Native 与现有原生应用的集成更容易。他们将记录他们在公司内部开发的概念,以便从社区获得反馈。

Wix

  • 在内部努力采用 Detox,将 Wix 应用的重要部分迁移到“零人工 QA”。因此,Detox 正在生产环境中被数十名开发人员大量使用,并迅速发展成熟。
  • 正在努力为 Metro Bundler 添加支持,以便在构建过程中覆盖任何文件扩展名。它不仅支持“ios”和“android”,还支持任何自定义扩展名,如“e2e”或“detox”。计划将其用于 E2E 模拟。已经有一个名为 react-native-repackager 的库,现在正在开发一个 PR。
  • 正在研究性能测试的自动化。这是一个名为 DetoxInstruments 的新仓库。您可以查看一下,它正在开源开发。
  • 与来自 KPN 的贡献者一起开发 Android 版 Detox 并支持真实设备。
  • 思考“Detox 作为平台”的概念,以允许构建其他需要自动控制模拟器/设备的工具。例如,React Native 的 Storybook 或 Ram 关于集成测试的想法。

下次会议

会议将每四周举行一次。下一届会议定于 2017年7月12日举行。由于我们刚刚开始举行此会议,我们想知道这些记录如何使 React Native 社区受益。如果您对我们应该在后续会议中涵盖哪些内容或如何改进会议成果有任何建议,请随时在 Twitter 上联系我。

改进 React Native 中的列表视图

2017年3月13日 ·阅读时长 6 分钟
Spencer Ahrens
Facebook 软件工程师

在我们在 社区群组中发布的预告公告 之后,你们中的许多人已经开始使用我们的一些新的列表组件了,但我们今天正式发布它们!不再有 `ListView` 或 `DataSource`、过时的行、被忽略的 bug 或过多的内存消耗 - 使用最新的 React Native 2017年3月发布候选版本 ( `0.43-rc.1` ),您可以从新的组件套件中选择最适合您用例的组件,并获得出色的性能和开箱即用的功能集。

<FlatList>

这是用于简单、高性能列表的主力组件。提供一个数据数组和一个 `renderItem` 函数,就可以开始使用了。

<FlatList
data={[{title: 'Title Text', key: 'item1'}, ...]}
renderItem={({item}) => <ListItem title={item.title} />}
/>

<SectionList>

如果您想渲染一组分成逻辑部分的数据,也许还有节标题(例如,在按字母顺序排列的地址簿中),并且可能包含异构数据和渲染(例如,包含一些按钮的个人资料视图,然后是撰写器,然后是照片网格,然后是朋友网格,最后是故事列表),这就是方法。

<SectionList
renderItem={({item}) => <ListItem title={item.title} />}
renderSectionHeader={({section}) => <H1 title={section.key} />}
sections={[ // homogeneous rendering between sections
{data: [...], key: ...},
{data: [...], key: ...},
{data: [...], key: ...},
]}
/>

<SectionList
sections={[ // heterogeneous rendering between sections
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
{data: [...], key: ..., renderItem: ...},
]}
/>

<VirtualizedList>

幕后实现具有更灵活的 API。如果您的数据不在普通数组中(例如,不可变列表),则尤其方便。

功能

列表在许多上下文中使用,因此我们将新组件打包了丰富的功能,以便开箱即用地处理大多数用例。

  • 滚动加载 ( `onEndReached` )。
  • 下拉刷新 ( `onRefresh` / `refreshing` )。
  • 可配置 的可见性 (VPV) 回调 ( `onViewableItemsChanged` / `viewabilityConfig` )。
  • 水平模式 ( `horizontal` )。
  • 智能项目和分隔符。
  • 多列支持 ( `numColumns` )
  • `scrollToEnd` 、 `scrollToIndex` 和 `scrollToItem`
  • 更好的 Flow 类型。

一些注意事项

  • 当内容滚动到渲染窗口之外时,不会保留项目子树的内部状态。确保所有数据都捕获在项目数据或外部存储(如 Flux、Redux 或 Relay)中。

  • 这些组件基于 `PureComponent`,这意味着如果 `props` 保持浅层相等,它们将不会重新渲染。确保 `renderItem` 函数直接依赖的所有内容都作为 prop 传递,并且在更新后不为 `===`,否则您的 UI 在更改时可能不会更新。这包括 `data` prop 和父组件状态。例如

    <FlatList
    data={this.state.data}
    renderItem={({item}) => (
    <MyItem
    item={item}
    onPress={() =>
    this.setState(oldState => ({
    selected: {
    // New instance breaks `===`
    ...oldState.selected, // copy old data
    [item.key]: !oldState.selected[item.key], // toggle
    },
    }))
    }
    selected={
    !!this.state.selected[item.key] // renderItem depends on state
    }
    />
    )}
    selected={
    // Can be any prop that doesn't collide with existing props
    this.state.selected // A change to selected should re-render FlatList
    }
    />
  • 为了限制内存并实现流畅滚动,内容会异步地渲染到屏幕外。这意味着滚动速度可能快于填充速度,并短暂地看到空白内容。这是一个可以根据每个应用程序的需求进行调整的权衡,我们正在幕后努力改进它。

  • 默认情况下,这些新列表会在每个项目上查找 `key` prop 并将其用作 React key。或者,您可以提供自定义 `keyExtractor` prop。

性能

除了简化 API 之外,新的列表组件还具有显著的性能增强,主要体现在无论有多少行,内存使用量几乎都是恒定的。这是通过“虚拟化”渲染窗口之外的元素来实现的,方法是完全将它们从组件层次结构中卸载并从 React 组件中回收 JS 内存,以及从阴影树和 UI 视图中回收原生内存。这有一个缺点,即不会保留内部组件状态,因此**请确保在组件本身之外跟踪任何重要状态,例如在 Relay 或 Redux 或 Flux 存储中**。

限制渲染窗口还可以减少 React 和原生平台需要执行的工作量,例如视图遍历。即使您正在渲染一百万个元素中的最后一个,使用这些新列表也无需遍历所有这些元素才能进行渲染。您甚至可以跳到中间位置,使用 `scrollToIndex` 而不进行过多的渲染。

我们还对调度进行了一些改进,这应该有助于提高应用程序的响应能力。渲染窗口边缘的项目很少渲染,并且在任何活动手势、动画或其他交互完成后,才会以较低的优先级渲染。

高级用法

ListView 不同,渲染窗口中的所有项目在任何 prop 发生变化时都会重新渲染。通常情况下,这没有问题,因为窗口化将项目数量减少到一个常数,但如果你的项目比较复杂,你应该确保遵循 React 的性能最佳实践,并在你的组件中适当地使用 React.PureComponent 和/或 shouldComponentUpdate 来限制递归子树的重新渲染。

如果你可以在不渲染的情况下计算行高,可以通过提供 getItemLayout prop 来改善用户体验。这使得使用例如 scrollToIndex 滚动到特定项目变得更加流畅,并且会改进滚动指示器 UI,因为可以在不渲染的情况下确定内容的高度。

如果你有其他数据类型,例如不可变列表,<VirtualizedList> 是最佳选择。它接受一个 getItem prop,允许你返回任何给定索引的项目数据,并且具有更宽松的流类型。

如果你有特殊的用例,还可以调整许多参数。例如,你可以使用 windowSize 在内存使用量和用户体验之间进行权衡,使用 maxToRenderPerBatch 调整填充率和响应速度之间的关系,使用 onEndReachedThreshold 控制滚动加载何时发生,等等。

未来工作

  • 迁移现有的界面(最终弃用 ListView)。
  • 根据需要添加更多功能(请告知我们!)。
  • 支持粘性节标题。
  • 更多性能优化。
  • 支持带有状态的函数式项目组件。

idx:存在函数

2017年3月13日 ·阅读时间:2 分钟
Timothy Yung
Facebook 工程经理

在 Facebook,我们经常需要访问使用 GraphQL 获取的数据结构中的深度嵌套值。在访问这些深度嵌套值的途中,一个或多个中间字段通常是可空的。这些中间字段可能是由于各种原因而为 null,从隐私检查失败到仅仅是 null 恰好是表示非致命错误的最灵活方式。

不幸的是,访问这些深度嵌套值目前既繁琐又冗长。

props.user &&
props.user.friends &&
props.user.friends[0] &&
props.user.friends[0].friends;

有一个 ECMAScript 提案引入存在运算符,这将使操作更加便捷。但在该提案最终确定之前,我们希望找到一个能够改善我们生活质量、维护现有语言语义并鼓励 Flow 类型安全的解决方案。

我们想出了一个名为 idx 的存在函数

idx(props, _ => _.user.friends[0].friends);

此代码段中的调用行为与上面代码段中的布尔表达式类似,但重复性大大减少。idx 函数恰好接受两个参数

  • 任何值,通常是对象或数组,你希望在其中访问嵌套值。
  • 一个接收第一个参数并在其上访问嵌套值的函数。

理论上,idx 函数将尝试捕获访问 null 或 undefined 上的属性导致的错误。如果捕获到此类错误,它将返回 null 或 undefined。(并且你可以看到这如何在 idx.js 中实现)。

在实践中,捕获每个嵌套属性访问的错误速度很慢,并且区分特定类型的 TypeError 很脆弱。为了解决这些缺点,我们创建了一个 Babel 插件,将上述 idx 调用转换为以下表达式

props.user == null
? props.user
: props.user.friends == null
? props.user.friends
: props.user.friends[0] == null
? props.user.friends[0]
: props.user.friends[0].friends;

最后,我们为 idx 添加了一个自定义 Flow 类型声明,允许在第二个参数中正确地进行类型检查,同时允许在可空属性上进行嵌套访问。

该函数、Babel 插件和 Flow 声明现在 可在 GitHub 上获取。它们通过安装 idxbabel-plugin-idx npm 包,并将“idx”添加到 .babelrc 文件中的插件列表中来使用。