为 iOS 15 和 Android 12 准备你的应用
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
大家好!
随着今年晚些时候新版移动操作系统即将发布,我们建议您提前准备 React Native 应用,避免在正式版发布时出现功能退化问题。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
大家好!
随着今年晚些时候新版移动操作系统即将发布,我们建议您提前准备 React Native 应用,避免在正式版发布时出现功能退化问题。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
长期以来,Apple 一直建议开发者使用 WKWebView 替代 UIWebView。在即将发布的 iOS 12 中,UIWebView 将被正式弃用。由于 React Native 的 iOS WebView 实现重度依赖 UIWebView 类,基于这些变化,我们为 WebView React Native 组件开发了全新的 iOS 原生后端,采用 WKWebView 实现。
这些变更的最终代码已通过 此提交 合并,将在 0.57 版本中发布。
要启用这个新实现,请使用 useWebKit 属性:
<WebView
useWebKit={true}
source={{url: 'https://www.google.com'}}
/>
UIWebView 原本缺乏有效机制来实现 WebView 内运行的 JavaScript 与 React Native 之间的通信。当消息从 WebView 发送时,我们依赖一种变通方案进行传递:先将消息数据编码到特殊协议的 URL 中,再导航 WebView 至该 URL。在原生端,我们拦截并取消此导航,解析 URL 中的数据,最终调用 React Native。这种实现方式不仅容易出错且存在安全隐患。很高兴向大家宣布,我们现已利用 WKWebView 的特性完全重构了此机制。
WKWebView 相较于 UIWebView 的其他优势包括更快的 JavaScript 执行速度和多进程架构。更多细节请参考 2014 年 WWDC。
如果您的组件使用了以下属性,切换到 WKWebView 时可能会遇到问题。目前建议避免使用这些属性:
行为不一致:
automaticallyAdjustContentInsets 和 contentInsets (提交记录)
向 WKWebView 添加 contentInsets 时,WKWebView 的视口(viewport)尺寸不会改变,仍保持与框架相同的尺寸。而 UIWebView 的实际视口尺寸会发生变化(当内容边距为正数时视口会缩小)。
backgroundColor (提交记录)
使用新的 iOS WebView 实现时,此属性可能导致背景色出现闪烁现象。此外,WKWebView 与 UIWebview 渲染透明背景的方式存在差异,具体细节请查看提交说明。
不受支持:
scalesPageToFit (提交记录)
由于 WKWebView 本身不支持 scalesPageToFit 属性,我们无法在 React Native 的 WebView 组件中实现此功能。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
随着技术进步和移动应用在日常生活中日益重要,开发无障碍应用的需求也变得越来越迫切。
React Native 有限的无障碍 API 一直是开发者的痛点,因此我们对无障碍 API 进行了多项更新,让创建包容性移动应用更加便捷。
accessibilityComponentType 和 accessibilityTraits 用于告知 Android 的 TalkBack 和 iOS 的 VoiceOver 用户正在交互的 UI 元素类型。这两个属性存在两大问题:
功能相同却使用两套不同机制。旧 API 中这两个平台专属属性不仅使用不便,还给开发者带来困惑。iOS 的 accessibilityTraits 支持 17 种值,而 Android 的 accessibilityComponentType 仅支持 4 种值,且多数值不重叠。更麻烦的是输入格式也不同:accessibilityTraits 支持数组或单值,而 accessibilityComponentType 仅接受单值。
Android 功能极其有限。旧属性下 TalkBack 只能识别 "button"、"radiobutton_checked" 和 "radiobutton_unchecked" 三种 UI 元素。
当无障碍标签无法明确表达操作结果时,无障碍提示能帮助使用 TalkBack 或 VoiceOver 的用户理解操作效果。这些提示可在设置中开关,但 React Native 的旧 API 完全不支持此功能。
部分视障用户会开启手机颜色反转功能增强对比度。Apple 提供了允许开发者忽略特定视图的 iOS API,确保开启颜色反转时图片视频不变形,但 React Native 此前未支持该 API。
为解决 accessibilityComponentType 和 accessibilityTraits 之间的混淆问题,我们将其合并为单一属性。这符合逻辑——两者功能本质相同,合并后开发者无需再考虑平台差异。
技术背景
iOS 中,UIAccessibilityTraits 是可设置于任何 NSObject 的属性。通过 JavaScript 传到原生层的 17 种特性会映射到 Objective-C 的 UIAccessibilityTraits 元素。每个特性用长整型表示,多特性通过按位或(OR)组合。
Android 的 AccessibilityComponentType 则是 React Native 自创概念,不直接对应 Android 原生属性。Android 通过无障碍委托处理:每个视图有默认委托,如需定制需创建新委托并覆盖特定方法。当开发者设置 AccessibilityComponentType 时,原生代码会根据传入组件创建新委托。
具体变更
新属性旨在成为两属性的超集。我们决定新属性主要基于现有属性 accessibilityTraits 进行建模,因为 accessibilityTraits 的值要多得多。这些特性的功能将通过修改无障碍委托在 Android 上实现。
accessibilityTraits 在 iOS 支持 17 种值,但新属性未全采纳。因部分特性效果不明确,且许多值实际极少使用。
这些特性值通常体现两种用途:描述 UI 元素角色或状态。观察发现,开发者通常组合一个角色值(如 "button")与状态值(如 "selected" 或 "disabled")。因此我们拆分出两个新属性:accessibilityRole 和 accessibilityState。
accessibilityRole
新属性 accessibilityRole 用于向 TalkBack 或 VoiceOver 说明 UI 元素角色,支持以下值:
none
button
link
search
image
keyboardkey
text
adjustable
header
summary
imagebutton
此属性仅接受单值,因 UI 元素通常不承担多重角色。特例是图片(image)和按钮(button)的组合,故新增了 imagebutton 角色。
accessibilityStates
新属性 accessibilityStates 用于说明 UI 元素状态,接受包含以下一个或多个值的数组:
selected
disabled
我们新增 accessibilityHint 属性,设置后 TalkBack 或 VoiceOver 将向用户朗读提示内容。
accessibilityHint
此属性接受字符串形式的无障碍提示内容。
iOS 中设置该属性会同步原生视图的 AccessibilityHint 属性,当 iPhone 开启无障碍提示时 VoiceOver 将朗读。
Android 的实现是将提示内容追加到无障碍标签末尾。这样做模拟了 iOS 的行为,但缺点是 Android 无法像 iOS 那样在设置中单独关闭提示。
此设计源于无障碍提示通常对应特定操作(如点击),我们希望保持跨平台行为一致。
accessibilityIgnoresInvertColors
我们将 Apple 的 AccessibilityIgnoresInvertColors API 暴露给 JavaScript。现在当您有不希望颜色反转的视图(如图片)时,设置此属性为 true 即可避免反转。
这些新属性将在 React Native 0.57 版本中提供。
若您当前正在使用 accessibilityComponentType 和 accessibilityTraits,可按以下步骤迁移到新属性。
简单场景可通过运行 jscodeshift 脚本完成替换。
此脚本可自动替换以下情况:
accessibilityTraits=“trait”
accessibilityTraits={[“trait”]}
为
accessibilityRole= “trait”
该脚本还会移除 AccessibilityComponentType 实例(假设您每次设置 AccessibilityComponentType 时都会同时设置 AccessibilityTraits)。
对于 AccessibilityTraits 使用无对应 AccessibilityRole 值的情况,以及将多个特征值传递到 AccessibilityTraits 的情况,需要手动修改。
通常而言,
accessibilityTraits= {[“button”, “selected”]}
需手动替换为
accessibilityRole=“button”
accessibilityStates={[“selected”]}
这些属性已在 Facebook 代码库中实际应用。令人惊喜的是,Facebook 的代码迁移相当简单:jscodeshift 脚本处理了约半数实例,其余手动修改,整个过程仅耗时数小时。
希望新版 API 能为您带来便利!请持续打造无障碍应用!#包容性设计
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
距离我们上次发布 React Native 的现状更新已有一段时间。
在 Facebook,我们比以往任何时候都更广泛地在重要项目中使用 React Native。其中最受欢迎的产品是 Marketplace——这是我们应用中每月被 8 亿用户使用的顶级标签页之一。自 2015 年创建以来,Marketplace 完全采用 React Native 构建,包含遍布应用不同部分的数百个全屏视图。
我们也在应用的许多新模块中使用 React Native。如果您上个月观看了 F8 主题演讲,会注意到献血、危机应对、隐私快捷设置和健康检查等功能——这些都是近期采用 React Native 构建的特性。在 Facebook 主应用之外的项目同样在使用 React Native。新款 Oculus Go VR 头显包含完全使用 React Native 构建的配套移动应用,更不用说头显内部许多体验都由 React VR 驱动。
当然,我们也使用其他多种技术构建应用。Litho 和 ComponentKit 是我们在应用中广泛使用的两个库;它们都提供类似 React 的组件 API 来构建原生界面。React Native 的目标从来不是取代所有其他技术——我们专注于改进 React Native 本身,但也乐于看到其他团队借鉴 React Native 的理念,例如将即时重载功能引入非 JavaScript 代码。
2013 年启动 React Native 项目时,我们将其设计为在 JavaScript 和原生环境之间建立单一"桥接",该桥接具有异步、可序列化和批处理特性。正如 React DOM 将 React 状态更新转换为 document.createElement(attrs) 和 .appendChild() 等 DOM API 的命令式可变调用,React Native 被设计为返回列出待执行变更的单一 JSON 消息,例如 [["createView", attrs], ["manageChildren", ...]]。我们设计的整个系统从不依赖同步响应,并确保列表中所有内容都能完全序列化为 JSON 并还原。这样做是为了获得灵活性:基于此架构,我们得以构建诸如Chrome 调试等工具,该工具通过 WebSocket 连接异步运行所有 JavaScript 代码。
过去五年我们发现,这些初始原则使得某些功能的构建更加困难。异步桥接意味着无法将 JavaScript 逻辑直接集成到许多需要同步响应的原生 API 中。批处理桥接会对原生调用进行排队,导致 React Native 应用更难调用原生实现的函数。而可序列化桥接则意味着不必要的内存复制,而非直接在两个环境间共享内存。对于完全采用 React Native 构建的应用,这些限制通常可以接受。但对于 React Native 与现有应用代码需复杂集成的场景,这些限制令人沮丧。
我们正在进行 React Native 的大规模架构重构,以使框架更灵活,并与 JavaScript/原生混合应用中的原生基础设施更好集成。 通过该项目,我们将应用过去五年积累的经验,逐步将架构升级至更现代化的形态。我们正在重写 React Native 的许多内部实现,但大部分变更都在底层:现有 React Native 应用将继续运行,几乎不需要修改。
为了让 React Native 更轻量级并更好地融入现有原生应用,这次架构重构包含三大内部改进。首先,我们正在改变线程模型。用户界面更新不再需要在三个不同线程上执行工作,新架构允许在任何线程上同步调用 JavaScript 来处理高优先级更新,同时仍将低优先级任务移出主线程以保持响应能力。其次,我们将异步渲染能力整合到 React Native 中,以支持多级渲染优先级并简化异步数据处理。最后,我们正在简化桥接机制,使其更快速轻量:原生代码与 JavaScript 之间的直接调用将更高效,并便于构建跨语言堆栈追踪等调试工具。
这些改进完成后,更深入的集成将成为可能。当前若想整合原生导航/手势处理或 UICollectionView、RecyclerView 等原生组件,必须采用复杂技巧。线程模型改造后,构建此类功能将变得直接高效。
我们将在今年晚些时候这项工作接近完成时公布更多细节。
除 Facebook 内部社区外,我们欣喜地看到外部 React Native 用户和贡献者生态蓬勃发展。我们期待通过优化开发者体验和降低贡献门槛,为社区提供更完善的支持。
正如架构改进能提升 React Native 与其他原生设施的协同能力,我们也致力于精简 JavaScript 侧架构:包括支持可替换的虚拟机与打包工具,使其更契合 JavaScript 生态系统。我们理解重大变更的更新节奏可能带来适配压力,因此将探索减少大版本更新的方案。同时针对启动优化等尚未系统文档化的专业领域,我们将补充更详实的指南。这些改进将在未来一年逐步落地。
只要您在使用 React Native,就是社区的重要成员——请继续告诉我们如何为您打造更出色的开发体验。
虽然 React Native 只是移动开发者工具箱中的选项之一,但我们坚信其价值——去年 500+ 贡献者提交的 2500 多次代码更新,正推动它日臻完善。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
JavaScript!我们都爱它。但有些人也钟爱类型系统。幸运的是,有多种方案可以为 JavaScript 增加强类型支持。我最喜欢的是 TypeScript,不过 React Native 默认支持 Flow。选择哪种方案完全取决于个人偏好,它们各自有不同的方式为 JavaScript 添加类型的魔力。今天,我们将探讨如何在 React Native 应用中使用 TypeScript。
本文参考了微软的 TypeScript-React-Native-Starter 仓库指南。
更新:自本文撰写以来,配置过程变得更加简单。只需运行以下单个命令,即可替代本文描述的所有设置:
npx react-native init MyAwesomeProject --template react-native-template-typescript
不过 Babel 的 TypeScript 支持确实_存在_一些限制,上文提到的博文对此有详细说明。本文概述的步骤仍然有效,Artsy 在生产环境中仍在使用 react-native-typescript-transformer。但要在 React Native 和 TypeScript 中快速上手,使用上述命令是最便捷的方式。如有需要,之后可以随时切换。
无论如何,祝您使用愉快!原始博文继续如下。
由于您可能在不同平台上开发,面向多种设备类型,基础设置可能较为复杂。您应首先确保能在不使用 TypeScript 的情况下运行普通的 React Native 应用。请按照 React Native 官网的说明开始操作。当您成功部署到设备或模拟器后,就可以开始创建 TypeScript React Native 应用了。
当您尝试搭建好普通 React Native 项目后,就可以开始添加 TypeScript 了。让我们立即开始吧。
react-native init MyAwesomeProject
cd MyAwesomeProject
下一步是将 TypeScript 添加到项目中。以下命令将:
向项目添加 TypeScript
初始化空的 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'];
},
};
将生成的 App.js 和 __tests_/App.js 文件重命名为 App.tsx。index.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 应用了。
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',
},
});
哇!内容有点多,我们来分解说明:
我们不再渲染 div、span、h1 等 HTML 元素,而是渲染 View 和 Button 等组件。这些是跨平台工作的原生组件。
样式使用 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 开发环境中玩得开心!
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
三年前,有人提交了一个 GitHub issue,要求 React Native 支持 input accessory view。
在随后的几年里,这个问题收到了无数的 "+1" 和各种变通方案,但 RN 本身却没有任何实质改变——直到今天。我们从 iOS 平台开始,公开了访问原生 input accessory view 的接口,并很高兴分享我们的实现方案。
究竟什么是 input accessory view?阅读 Apple 开发者文档可知,它是一个自定义视图,当接收者成为第一响应者时,可以固定在系统键盘顶部。任何继承自 UIResponder 的对象都可重新声明 .inputAccessoryView 属性,并在此管理自定义视图。响应者基础架构会挂载该视图,并使其与系统键盘保持同步。用于关闭键盘的手势(如拖动或点击)会在框架层应用于 input accessory view。这让我们能实现交互式键盘收起功能——这是 iMessage 和 WhatsApp 等顶级通讯应用的核心特性。
将视图固定在键盘顶部有两种常见场景:第一种是创建键盘工具栏,例如 Facebook 发帖编辑器的背景选择器。
在此场景中,键盘聚焦于文本输入框,input accessory view 用于提供额外的键盘功能。这些功能与输入框类型相关:地图应用中可能是地址建议,文本编辑器中则可能是富文本格式工具。
在此场景中,拥有 <InputAccessoryView> 的 Objective-C UIResponder 很明确:当 <TextInput> 成为第一响应者时,底层会变成 UITextView 或 UITextField 实例。
第二种常见场景是粘性文本输入框:
此时文本输入框本身是 input accessory view 的组成部分。这常见于消息应用,用户可在滚动查看历史消息的同时编写新消息。
此例中谁拥有 <InputAccessoryView>?还能是 UITextView 或 UITextField 吗?文本输入框竟_位于_ input accessory view 内部,这形成了循环依赖。单独解决这个问题就值得另写一篇博客。剧透:拥有者是一个通用的 UIView 子类,我们会手动触发它的 becomeFirstResponder 方法。
现在我们知道 <InputAccessoryView> 是什么以及如何使用它。下一步是设计一个同时满足两种场景的接口,并与现有 React Native 组件(如 <TextInput>)良好协作。
对于键盘工具栏,我们需要考虑以下几点:
需要能将任意 React Native 视图结构提升到 <InputAccessoryView> 中
这个独立视图结构需能接收触摸事件并操作应用状态
需要将 <InputAccessoryView> 关联到特定 <TextInput>
需要能在多个文本输入框间共享 <InputAccessoryView> 而无需重复代码
我们可以使用类似于 React portals 的概念来实现 #1。在这种设计中,我们将 React Native 视图"传送"到由响应者基础设施管理的 UIView 层次结构中。由于 React Native 视图会渲染为 UIViews,这实际上相当直接——我们只需重写:
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
并将所有子视图传输到新的 UIView 层次结构中。对于 #2,我们为 <InputAccessoryView> 设置一个新的 RCTTouchHandler。状态更新通过常规事件回调实现。对于 #3 和 #4,我们在创建 <TextInput> 组件时,使用 nativeID 字段在原生代码中定位辅助视图的 UIView 层次结构。该函数利用底层原生文本输入的 .inputAccessoryView 属性,从而在 ObjC 实现中有效地将 <InputAccessoryView> 与 <TextInput> 关联起来。
支持粘性文本输入(场景二)带来额外约束。这种设计中输入辅助视图本身包含文本输入子元素,因此无法通过 nativeID 关联。我们改为将通用离屏 UIView 的 .inputAccessoryView 属性设置为我们原生的 <InputAccessoryView> 层次结构。通过手动使该通用 UIView 成为第一响应者,响应者基础设施便会挂载该层次结构。此概念在前述博客文章中有详细阐述。
当然,在构建此 API 的过程中并非一帆风顺。以下是我们遇到的一些陷阱及其解决方案。
最初的构建方案涉及监听 NSNotificationCenter 的 UIKeyboardWill(Show/Hide/ChangeFrame) 事件。该模式在某些开源库和 Facebook 应用内部模块中使用。遗憾的是,在滑动操作时,UIKeyboardDidChangeFrame 事件未能及时触发以更新 <InputAccessoryView> 的帧位置。此外,键盘高度变化也不会被这些事件捕获,导致如下缺陷:
在 iPhone X 上,文本键盘与表情键盘高度不同。依赖键盘事件操控文本输入框的应用都需修复此缺陷。我们的解决方案是坚持使用 .inputAccessoryView 属性,这意味着响应者基础设施会处理此类帧更新。
另一个棘手问题是避免与 iPhone X 的 Home 指示条重叠。您可能认为:"苹果为此专门开发了 safeAreaLayoutGuide,这很简单!" 我们最初也如此天真。首要问题是原生 <InputAccessoryView> 实现在即将显示前没有可锚定的窗口。虽然可通过重写 -(BOOL)becomeFirstResponder 强制执行布局约束,但遵循这些约束会将辅助视图上移后,新问题又会出现:
输入辅助视图成功避开 Home 指示条后,不安全区域后的内容却变得可见。解决方案来自这个 radar。我将原生 <InputAccessoryView> 层次结构包裹在不遵循 safeAreaLayoutGuide 约束的容器中,该容器覆盖不安全区域的内容,而 <InputAccessoryView> 保持在安全边界内。
以下示例构建了用于重置 <TextInput> 状态的键盘工具栏按钮:
class TextInputAccessoryViewExample extends React.Component<
{},
*,
> {
constructor(props) {
super(props);
this.state = {text: 'Placeholder Text'};
}
render() {
const inputAccessoryViewID = 'inputAccessoryView1';
return (
<View>
<TextInput
style={styles.default}
inputAccessoryViewID={inputAccessoryViewID}
onChangeText={text => this.setState({text})}
value={this.state.text}
/>
<InputAccessoryView nativeID={inputAccessoryViewID}>
<View style={{backgroundColor: 'white'}}>
<Button
onPress={() =>
this.setState({text: 'Placeholder Text'})
}
title="Reset Text"
/>
</View>
</InputAccessoryView>
</View>
);
}
}
该功能的完整实现提交位于此处。<InputAccessoryView> 将在即将发布的 v0.55.0 版本中提供。
祝键盘操作愉快 :)
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
AWS 作为云服务提供商在科技行业广为人知,其服务涵盖计算、存储和数据库技术,以及全托管的无服务器解决方案。AWS Mobile 团队一直与客户及 JavaScript 生态成员紧密合作,致力于让云端连接的移动和 Web 应用更安全、更易扩展,同时简化开发和部署流程。我们最初推出了完整的入门套件,近期还有更多新进展。
本篇博客将为 React 和 React Native 开发者介绍以下值得关注的技术:
AWS Amplify:面向云服务的 JavaScript 声明式开发库
AWS AppSync:具备离线和实时特性的全托管 GraphQL 服务
使用 Create React Native App 和 Expo 等工具可以轻松初始化 React Native 应用。但当您尝试将具体用例与基础设施服务匹配时,连接云端服务可能颇具挑战。例如,您的 React Native 应用可能需要上传照片:这些照片需要按用户隔离保护吗?这可能意味着您需要注册或登录流程。您希望使用自建用户目录还是社交媒体提供商?也许您的应用还需要在用户登录后调用包含定制业务逻辑的 API。
为帮助 JavaScript 开发者解决这些问题,我们发布了名为 AWS Amplify 的库。其设计按任务"类别"划分,而非基于 AWS 的具体实现。例如,若您需要用户注册登录后上传私有照片,只需在应用中引入 Auth 和 Storage 类别:
import { Auth } from 'aws-amplify';
Auth.signIn(username, password)
.then(user => console.log(user))
.catch(err => console.log(err));
Auth.confirmSignIn(user, code)
.then(data => console.log(data))
.catch(err => console.log(err));
在上方代码示例中,您可以看到 Amplify 协助处理的常见任务,例如通过邮箱或短信使用多因素认证(MFA)验证码。当前支持的类别包括:
Auth:提供凭证自动化功能。开箱即用的实现使用 AWS 凭证进行签名,并采用 Amazon Cognito 的 OIDC JWT 令牌。支持常见功能如 MFA 特性。
Analytics:单行代码即可在 Amazon Pinpoint 中追踪认证/未认证用户。可根据需要扩展自定义指标或属性。
API:通过 AWS 签名版本 4 安全地与 RESTful API 交互。该模块在配合 Amazon API Gateway 的无服务器架构中表现优异。
Storage:简化了 Amazon S3 内容的上传、下载和列表操作。还可轻松按用户分组公开或私有数据。
Caching:跨 Web 应用和 React Native 的 LRU 缓存接口,采用特定实现的持久化方案。
i18n and Logging:提供国际化与本地化能力,以及调试和日志功能。
Amplify 的亮点之一是在设计中对特定编程环境编码了"最佳实践"。例如,我们发现客户和 React Native 开发者常为快速实现功能而在开发阶段走捷径,这些方案最终进入生产环境后,可能影响扩展性或安全性,迫使重构基础设施和代码。
我们帮助开发者避免此类问题的一个典型案例是AWS Lambda无服务器参考架构。这些架构展示了在构建后端时联合使用Amazon API Gateway和AWS Lambda的最佳实践。该模式已被编码集成到Amplify的API类别中。您可以使用此模式与多个REST端点交互,并将自定义业务逻辑所需的标头一直传递到Lambda函数。我们还发布了AWS Mobile CLI工具,用于为新建或现有React Native项目快速配置这些功能。只需通过npm安装,然后按配置提示操作即可:
npm install --global awsmobile-cli
awsmobile configure
另一个针对移动生态的编码化最佳实践是密码安全。默认的Auth类别实现利用Amazon Cognito用户池处理用户注册和登录。该服务采用安全远程密码协议(SRP)来保护用户认证过程。如果您有兴趣研究该协议的数学原理,会注意到在原始根上计算密码验证器生成群组时必须使用大质数。在React Native环境中JIT被禁用,导致此类安全计算中的BigInteger运算性能较低。为此我们发布了Android和iOS原生桥接模块,可直接集成到项目中:
npm install --save aws-amplify-react-native
react-native link amazon-cognito-identity-js
令人振奋的是,Expo团队已将此功能纳入其最新SDK,您现在无需eject项目即可使用Amplify。
最后针对React Native(及React)开发,Amplify提供高阶组件(HOC)简化功能封装,例如实现应用注册登录功能:
import Amplify, { withAuthenticator } from 'aws-amplify-react-native';
import aws_exports from './aws-exports';
Amplify.configure(aws_exports);
class App extends React.Component {
...
}
export default withAuthenticator(App);
基础组件<Authenticator />也支持完全自定义UI。它提供用户状态管理属性(如登录状态/MFA验证等待状态)及状态变更回调函数。
同样地,您会发现适用于不同场景的通用React组件。例如在Storage模块中展示Amazon S3所有私有图片时可自定义组件:
<S3Album
level="private"
path={path}
filter={(item) => /jpg/i.test(item.path)}/>
如前所示,您可以通过props控制组件的多种功能(如公开/私有存储选项)。某些UI组件甚至支持自动收集用户交互数据:
return <S3Album track/>
AWS Amplify采用约定优于配置的开发范式,支持全局初始化或按类别初始化。最快捷的方式是使用aws-exports文件,同时也支持开发者独立使用库对接现有资源。
如需深入了解设计理念并查看完整演示,请观看AWS re:Invent大会视频。
在发布AWS Amplify后不久,我们还推出了AWS AppSync。这项全托管GraphQL服务同时具备离线和实时能力。尽管您可以在不同客户端编程语言(包括原生Android/iOS)中使用GraphQL,但它在React Native开发者中尤为流行——因为其数据模型完美契合单向数据流和组件层级结构。
AWS AppSync 让您能够连接到您自己 AWS 账户中的资源,这意味着您完全拥有并掌控自己的数据。这是通过数据源实现的,该服务支持 Amazon DynamoDB、Amazon Elasticsearch 和 AWS Lambda。您可以在单个 GraphQL API 中将多种功能(如 NoSQL 和全文搜索)组合成一个架构。AppSync 服务还能根据架构自动配置资源,因此即使您不熟悉 AWS 服务,只需编写 GraphQL SDL 并点击按钮,系统就能自动完成部署。
AWS AppSync 的实时功能通过基于事件的 GraphQL 订阅模式实现。由于订阅功能使用 GraphQL 指令在架构层进行控制,而架构可使用任意数据源,这意味着您可以通过 Amazon DynamoDB 和 Amazon Elasticsearch Service 的数据库操作触发通知,或通过 AWS Lambda 从基础设施其他部分触发。
与 AWS Amplify 类似,您可以在 AWS AppSync 的 GraphQL API 中使用企业级安全功能。该服务支持通过 API 密钥快速上手,但在生产环境中可无缝切换到 AWS IAM 或 Amazon Cognito 用户池的 OIDC 令牌。您可以在解析器层级通过类型策略控制访问权限,甚至能在运行时执行细粒度访问控制检查(例如验证用户是否为特定数据库资源所有者),还支持通过群组成员检查来执行解析器或数据库记录访问。
为帮助 React Native 开发者快速上手,AWS AppSync 控制台主页提供内置 GraphQL 示例架构。该示例会自动部署 GraphQL 架构、配置数据库表并连接查询/变更/订阅操作。我们还提供了可直接运行的 React Native 示例项目(以及 React 版本),帮助您在几分钟内同时启动客户端和云端组件。
使用 AWSAppSyncClient 非常简单,它直接集成 Apollo 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 的交互,还实现了数据分页(含加密令牌)以及 Events 和 Comments 的类型关联。由于应用配置了 AWSAppSyncClient,数据会自动持久化离线存储,并在设备重连时同步更新。
您可以通过此技术深度解析视频查看客户端技术细节和 React Native 演示。
这些库背后的团队渴望了解这些库和服务对您的工作效果如何。他们还想了解我们还能做些什么,让您在使用云服务开发 React 和 React Native 应用时更加轻松。请在 GitHub 上联系 AWS Mobile 团队:AWS Amplify 或 AWS AppSync。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
Twitter iOS 应用的加载动画让我非常喜欢。
当应用准备就绪时,Twitter 小鸟标志会优雅地展开,显露出应用界面。
我想探索如何在 React Native 中重现这个加载动画。
要理解构建方法,首先需要拆解加载动画的各个组成部分。观察细节最简单的方法是放慢速度观看。
这个动画包含几个关键部分需要我们实现:
小鸟标志的缩放
小鸟扩展时显示下方应用界面
最后阶段轻微缩小应用界面
我花了相当长时间才搞明白如何制作这个动画。
最初我有个错误假设:认为蓝色背景和小鸟是覆盖在应用上方的图层,随着小鸟放大逐渐变得透明从而显示下方应用。这种方法行不通,因为小鸟变透明后露出的会是蓝色背景,而不是应用界面!
幸运的是,亲爱的读者,您不必经历同样的挫折。本教程将带您直达关键实现部分!
在编写代码前,理解如何拆解这个效果很重要。为帮助可视化,我在 CodePen 上重建了这个效果(嵌入在下方段落中),您可以交互式查看不同图层。
这个效果包含三个主要图层:最底层是蓝色背景层。虽然它看起来像是在应用上方,实际位于最后方。
中间是纯白色图层。最前面则是我们的应用层。
这个动画的核心技巧是将 Twitter 标志作为 mask(遮罩),同时遮盖应用层和白色层。关于遮罩细节我不做深入探讨,网上有大量相关资源。
在此背景下,遮罩的基本原理是:遮罩图像的不透明像素会显示被遮盖内容,而透明像素则隐藏被遮盖内容。
我们将 Twitter 标志作为遮罩,用它遮盖两个图层:纯白色层和应用层。
通过将遮罩放大到超过整个屏幕的尺寸来显示应用界面。
在遮罩放大过程中,我们逐渐增加应用层的不透明度,从而显示应用界面并隐藏后方的纯白层。最后阶段,我们将应用层从大于1的初始比例缩小至1。动画结束后隐藏非应用图层。
常言道一图胜千言。那么交互式可视化值多少词呢?点击"Next Step"按钮逐步查看动画效果。图层展示提供了侧视角度,网格线帮助可视化透明图层。
好的。既然我们理解了构建目标和动画原理,现在可以进入代码环节——这才是您阅读的真正目的。
实现的关键组件是 MaskedViewIOS,这是 React Native 的核心组件。
import {MaskedViewIOS} from 'react-native';
<MaskedViewIOS maskElement={<Text>Basic Mask</Text>}>
<View style={{backgroundColor: 'blue'}} />
</MaskedViewIOS>;
MaskedViewIOS 接收 maskElement 和 children 两个 props。子元素会被 maskElement 遮罩。注意遮罩不必须是图片,它可以是任意视图。上述示例的效果是渲染蓝色视图,但仅通过 maskElement 的 "Basic Mask" 文字区域可见。我们刚刚创建了复杂的蓝色文字效果。
我们需要先渲染蓝色背景层,然后在顶部用 Twitter 标志遮罩来渲染应用层和白色层。
{
fullScreenBlueLayer;
}
<MaskedViewIOS
style={{flex: 1}}
maskElement={
<View style={styles.centeredFullScreen}>
<Image source={twitterLogo} />
</View>
}>
{fullScreenWhiteLayer}
<View style={{flex: 1}}>
<MyApp />
</View>
</MaskedViewIOS>;
这样就能得到下图所示的层级结构。
现在我们已经准备好所有组件,下一步是添加动画效果。为了让动画更流畅,我们将使用 React Native 的 Animated API。
Animated 允许我们在 JavaScript 中声明式地定义动画。默认情况下,这些动画在 JavaScript 线程运行,并逐帧通知原生层更新。但由于 JavaScript 无法保证每帧及时更新,可能导致掉帧(卡顿)—— 这绝非我们想要的效果!
Animated 提供了特殊机制避免卡顿:通过设置 useNativeDriver 标志,可以在动画开始时将定义从 JavaScript 发送到原生层,让原生端独立处理动画更新,无需每帧与 JavaScript 通信。useNativeDriver 的限制是只能修改特定属性(主要是 transform 和 opacity),目前无法用 useNativeDriver 实现背景色等属性的动画。当然,未来我们会扩展支持范围,也欢迎提交 PR 来增加项目所需属性,让整个社区受益 😀。
为确保动画流畅,我们将在这些约束条件下工作。深入了解 useNativeDriver 底层原理,请参阅相关博客文章。
我们的动画包含 4 个关键部分:
放大小鸟图标,逐渐显示应用和纯白层
淡入应用界面
轻微缩小应用界面
完成后隐藏白色层和蓝色层
Animated 提供两种主要动画定义方式:Animated.timing 可精确控制时长和缓动曲线;Animated.spring 则基于物理模型。使用 Animated.spring 时,你可以通过摩擦力和张力参数实现弹性效果。
由于多个动画需要同步进行(例如在遮罩展开中途开始淡入应用),且它们彼此关联,我们将使用 Animated.timing 配合单一 Animated.Value 实现。
Animated.Value 是封装动画状态值的容器。通常整个动画只需一个该值,大多数组件会将其存储在 state 中。
我们将动画视为随时间推进的连续过程:设置 Animated.Value 初始值为 0(0% 完成),最终值为 100(100% 完成)。
初始组件状态设置如下:
state = {
loadingProgress: new Animated.Value(0),
};
准备启动动画时,通知 Animated 将该值渐变至 100。
Animated.timing(this.state.loadingProgress, {
toValue: 100,
duration: 1000,
useNativeDriver: true, // This is important!
}).start();
接着我尝试估算动画各个部分在不同时间节点应有的参数值。下面列出了动画各组成部分的进度对应表,展示了随着时间推进各参数的理想变化值。

Twitter小鸟遮罩的初始缩放值应为1,在迅速放大前会先略微缩小。在动画进度10%时,其缩放值应为0.8,最终则放大至70。选择70这个值其实相当随意——它必须足够大才能完全显露下方内容,而60显然不够😀。有趣的是,这个最终值越大,缩放速度看起来就越快,因为必须在相同时间内达到更大倍数。经过反复调试才找到适合当前Logo的数值。不同尺寸的Logo/设备需要不同的最终缩放值,以确保完整显示屏幕内容。
应用界面需要保持不透明状态一段时间,至少要覆盖Logo缩小阶段。根据官方动画效果,我计划在小鸟放大到一半时开始显示应用界面,并快速完成过渡。因此在动画进度15%时开始显现,到30%时实现完全可见。
应用界面初始缩放值为1.1,随动画推进逐渐缩小至正常尺寸。
上述过程本质是将整体动画进度映射到各独立元素的参数值。我们通过Animated的.interpolate方法实现这种映射。基于this.state.loadingProgress的插值计算,我们为动画的三个组成部分分别创建了对应的样式对象。
const loadingProgress = this.state.loadingProgress;
const opacityClearToVisible = {
opacity: loadingProgress.interpolate({
inputRange: [0, 15, 30],
outputRange: [0, 0, 1],
extrapolate: 'clamp',
// clamp means when the input is 30-100, output should stay at 1
}),
};
const imageScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 10, 100],
outputRange: [1, 0.8, 70],
}),
},
],
};
const appScale = {
transform: [
{
scale: loadingProgress.interpolate({
inputRange: [0, 100],
outputRange: [1.1, 1],
}),
},
],
};
获得这些样式对象后,就可以在渲染前文提到的视图片段时应用它们。注意只有Animated.View、Animated.Text和Animated.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在手机上直接体验:
这本Gitbook是学习Animated的绝佳资源,尤其适合已掌握React Native基础文档的开发者
实际Twitter动画在最后阶段会加速遮罩展开过程。尝试修改加载器,使用不同的缓动函数(或弹性动画)来模拟这种效果
当前遮罩的最终缩放值是硬编码的,在平板设备上可能无法完整显示内容。根据屏幕尺寸和图像大小动态计算最终缩放值会是很棒的PR改进
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
React Native 月度会议仍在火热进行中!请务必查看本文底部的通知以了解下一期会议安排。
恭喜 Devin Abbott 和 Houssein Djirdeh 预发布了《Full Stack React Native》一书!该书通过构建多个小型应用带你系统学习 React Native。
发布了首个(实验性)版本的 reason-react-native-scripts,帮助开发者轻松尝试 ReasonML。
Expo SDK 24 已发布!该版本基于 React Native 0.51,包含多项新特性和改进:独立应用内置图片(无需首次加载缓存!)、图片处理 API(裁剪/缩放/旋转/翻转)、人脸识别 API、新版发布渠道功能(设置指定渠道的活跃版本并支持回滚)、用于追踪独立应用构建的网页仪表盘,以及修复了 OpenGL Android 实现与 Android 多任务处理的长期 bug 等。
今年一月起,我们将向 React Navigation 投入更多资源。我们坚信仅使用 React 组件和 Animated、react-native-gesture-handler 等基础模块构建 React Native 导航完全可行且值得期待,并对规划中的改进充满期待。如果你想为社区贡献力量,请关注需要协助的 react-native-maps 和 react-native-svg 项目!
已确定 Chain React 大会 主题演讲嘉宾:Kent C. Dodds 和 Tracy Lee,即将开放议题征集(CFP)。
社区聊天群 现有 1600 名成员。
React Native 简讯 订阅量已达 8500。
正在研究增强 RN 崩溃抵抗能力的最佳实践,报告即将发布。
新增从 Solidarity 生成报告的功能。
发布了 React Native 与 Android 的应用发布指南。
Android 性能优化
我们正讨论调整会议形式:未来可能聚焦单一特定主题(例如导航架构、React Native 模块独立仓库迁移、文档优化等)。这种方式有望最大化对 React Native 社区的贡献。该调整可能于下次会议实施,欢迎通过推特留言提出您希望探讨的主题。
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
新一期 React Native 月度会议来了!让我们看看各个团队的最新进展。
我们持续优化 React Native 的持续集成系统。最重要的进展是将 CI 系统从 Travis 迁移到 CircleCI,为 React Native 建立了统一高效的 CI 流水线。
我们举办了 Hacktoberfest - React Native 特别版活动,与参与者共同向开源项目提交了大量 Pull Request。
我们持续推进 Haul 项目。上月发布的两个新版本已支持 webpack 3。未来计划增加 CRNA 和 Expo 支持,并优化热模块替换功能。路线图已在 issue 跟踪器公开。欢迎提出改进建议或反馈意见!
发布 Expo SDK 22(基于 React Native 0.49)并同步更新 CRNA
Snack 编辑器现支持多 JavaScript 文件,并可通过拖拽直接上传图片等资源
为 react-navigation 贡献代码,新增 iPhone X 支持
重点优化大型 Expo 应用的开发体验:
RTL 布局优化:
I18nManager.swapLeftAndRightInRTL(false) 提前启用此变更我们正在为内部原生模块添加 Flow 类型注解,并利用这些类型生成 Java 接口和 ObjC 协议,原生实现必须遵循这些规范。我们希望该代码生成工具最早能在明年开源。
优化 Shoutem 平台的开发流程。我们致力于简化从创建应用到实现首个自定义屏幕的全过程,大幅降低 React Native 新手的入门门槛。通过多次工作坊测试了新功能,同时改进了 Shoutem CLI 以支持新流程。
Shoutem UI 进行了多项组件优化和错误修复,并验证了与最新 React Native 版本的兼容性。
Shoutem 平台迎来多项重要更新,新集成功能已作为开源扩展项目开放使用。看到其他开发者积极参与扩展开发令人振奋,我们主动联系并提供技术指导支持。
下次会议定于 2017年12月6日(星期三)举行。如果您对会议成果改进有任何建议,欢迎随时在 Twitter 上联系我。