
比较框架的任务是一项非常艰巨的任务,开发人员的偏好不同,技术变化非常快。 太快了 即使在单击“发布”按钮之前,本文也会过时。
例如,有人试图进行比较,例如大约五年前,这两个家伙(科林·埃伯哈特(Colin Eberhardt)和克里斯·普莱斯(Chris Price))启发了许多开发商,根据明确编制的TK提出了申请房地产的申请。 这个想法很酷,我们甚至参与了
DevExtreme并制作了该应用程序的一个版本。 但是就支持而言,这样的项目是地狱,现在,
Property Cross项目代表了一定的历史层次,引起了怀旧和温暖的感觉,但几乎没有实际用途。
如果仅使用js世界,那就是一个相当活跃的
todomvc项目,该项目仅比较js部分,而不打包在移动设备,台式机或任何应用程序中。 该项目非常活跃且受到支持。 最有可能的是,当我们准备本文时,仍然有一些非常酷的示例,我们在Google问题中没有注意到,但是我们对此不会感到沮丧。
我们的实验雄心勃勃,只展示了当前的技术部分,甚至只是其中的一小部分。 这是实验的
第一篇和
第二篇文章的链接。
进一步的阅读是关于如何根据TK在React Native上创建应用程序的第三篇文章。 它与关于如何在React Native上创建应用程序的其他成百上千的文档重述非常相似。 亲爱的读者,我警告过你,我的良心很清楚。
记住传统知识
通常,可以在第一篇文章中全面了解它。 但是,我将添加最终结果的图片。 图片很少。

少量设备。 什么是React Native?
React Native是一个用于从Facebook创建跨平台移动应用程序的框架。 就像在Web的“常规” React中一样,UI应用程序是由Bricks组装而成的-组件可以响应其状态(状态)和传递给它们的属性(props)的变化,但是与Web不同,它是在本机控件中呈现的。
理想情况下,应使用抗扰度和纯功能原理,以确保测试的简单性和隔离性。 这里值得一提的是,React本身非常简单,并且这种简单性进入了移动部分。
如果可能,本机和JS代码中的其他附加组件可消除平台之间的差异。 实际上,React Native提供了每个操作系统中组件属性的一些统一。
例如,ScrollView和HorizontalScrollView是Android中的两个不同组件。 在iOS中,UIScrollView支持水平和垂直滚动。 在React Native中,我们将使用以下跨平台代码:
<ScrollView horizontal={true}/>
通过有效的方法,我们得到了一个在iOS和Android上运行的“诚实”的本机应用程序。
在理想的世界中,使用React Native开发时,您不必使用Java或Objective-C进行编写。 但是,当您需要实现超越React Native功能的组件时,就有这样的机会。
Airbnb的开发人员在此方面发挥了很多作用,我们可以看到社区中许多有价值的实现都在其存储库中做出了反应。 例如,
Lottie是一个用于从Adobe After Effects或
跨平台地图导入动画的库。
应用程序中的JS代码在
JavaScriptCore引擎上执行。 本机代码和JS之间的通信是使用异步桥(桥)完成的,该桥允许您传输属性(属性),触发事件(事件)和回调。

图片来自出色的
React Made Native Easy文档。 (强烈建议阅读。)
在构建过程中,使用了新型的babel来转换JS代码,这使您可以使用新的ES6语法以及某些ES8功能(例如async-await)。 如果您(我亲爱的读者)是js开发人员,那么您将了解在存在散布运算符时它的优劣,而在没有散布符时它的优劣。
对于页面布局,使用flexbox技术,该技术由跨平台的Yoga引擎实现。 它与浏览器flexbox有所不同,但它们无关紧要,并且主要与默认值有关。 当然,有细微差别,但您会很幸运,所有内容仅根据文档进行。
准备和部署堆栈。 管端子
要使用RN,我们需要
Node.js和捆绑的npm软件包管理器。 不必要,但非常需要在设备上安装
Expo应用程序。 它可以让您在手机上启动我们的项目,以及在没有macOS的情况下构建和运行iOS应用程序。
让我们创建一个新的应用程序。 为此,请使用
create-react-native-app软件包。
在终端中,执行:
npm install -g create-react-native-app create-react-native-app notes cd notes npm run start
使用Expo扫描QR码,或从终端输入链接,甚至直接从终端将链接发送到手机。
我通常会怀疑,用于开发native的cli开发人员有一个白发苍苍的老人,当只有一个终端时才发现没有ui的流氓玩具,而没有高端显卡,只有您的想象力。

但是,与此同时,我们刚刚创建并启动了“ Hello World”应用程序。
整个“ Hello World” -a是不够的。 分析传统知识
根据工作说明,应用程序的数据结构将是
Note: { userName: string, avatar: string, editTime: string, text: string } Project: { name: string, notes: Array<Note> } Projects: Array<Project>
为了处理此类数据,我将采用一些基于CQRS的非常时尚的解决方案。 这将允许保留数据完整性,提供高读取速度并具有重新排列投影的能力,以及通过单个命令即可快速部署到云中。 就像由我们的同事开发的
Resolve一样。
但我不会接受,我们有一个简单的实验,没有后端。 为了简单起见,我将使用
磁通架构,尤其是其实现
-redux 。 来自应用程序状态的数据作为道具到达组件。 组件可以调用操作以更新数据。
该应用程序将具有3个屏幕,所有屏幕均取决于ToR:
- 项目清单-项目,
- 详细的项目页面,带有注释列表-项目,
- 详细的笔记页面-笔记
对于屏幕之间的导航,我将使用标准的
反应导航库。 库页面上图形附近的数字显示每周下载多少次。 现在每周大约有10万。 不仅如此,我不是唯一选择这样的图书馆进行导航的人。 是的,您可以看到我在本文中指出的其他npm软件包的数字,以大致了解给定时间使用该技术的用户数量。
创建一个应用程序
对于React Native,来自App.js文件的App组件是应用程序的入口点。
export default class App extends Component { render() { return ( <Provider store={store}> <Navigator /> </Provider> ) } }
带有数据的存储和应用程序状态由react-redux库中的Provider组件连接。 这为嵌套组件提供了数据转发。
为应用程序中的屏幕之间的转换创建导航器。 它清楚地反映了实验中声明的应用程序的结构,并为每个平台绘制了屏幕之间的动画过渡。
const Navigator = createStackNavigator({ Projects: { screen: Projects }, Project: { screen: Project }, Note: { screen: Note } })
导航器屏幕是组件-容器。 他们从应用程序状态获取数据。
项目清单-项目
在带有项目列表的屏幕上,右侧窗口的标题中将有一个列表和一个用于添加项目的按钮。 我们将在“项目”屏幕上创建一个新项目。
对于导航,我们使用导航对象,该对象传递给props的父组件-导航器。
export class Projects extends PureComponent { static navigationOptions = ({ navigation }) => ({ headerRight: ( <AddButton onPress={() => navigation.navigate('Project')} /> ) }) navigateProject = project => { this.props.navigation.navigate('Project', { projectId: project.id, name: project.name }) } render() { return ( <ProjectList projects={this.props.projects} onPressProject={this.navigateProject} /> ) } }
为了显示项目列表,我们将使用FlatList-具有虚拟化功能的跨平台列表:
export class ProjectList extends PureComponent { static propTypes = { projects: ProjectsType, onPressProject: PropTypes.func } renderItem = ({ item }) => ( <ProjectListItem project={item} onPressProject={this.props.onPressProject} /> ) render() { return ( <FlatList data={this.props.projects} keyExtractor={item => item.id} renderItem={this.renderItem} /> ) } }
对于每个元素,我们都设置一个唯一的键-我们具有该元素的ID。 这是必需的,以便反应可以区分列表中的元素并仅更新已更改的元素。
将组件添加到列表项。
export class ProjectListItem extends PureComponent { static propTypes = { project: ProjectType, onPressProject: PropTypes.func } onPressProject = () => { const { project, onPressProject } = this.props onPressProject(project) } render() { return ( <TouchableOpacity onPress={this.onPressProject}> <View style={styles.project}> <Text style={styles.name}>{this.props.project.name}</Text> </View> </TouchableOpacity> ) } }
TouchableOpactity-响应点击的包装器。 单击后,嵌套的组件将变得更加透明。
视图-用于网络的div模拟-标记的基本组件。
文本-文本的容器。
添加样式:
const styles = StyleSheet.create({ project: { paddingVertical: 30, paddingHorizontal: 15, backgroundColor: 'white', borderBottomWidth: StyleSheet.hairlineWidth, borderColor: 'gray' }, name: { fontSize: 16 } })
样式语法类似于css,主要区别在于您只能对组件本身进行样式化(例如,不能为整个应用程序设置字体大小,只能为特定的Text组件设置字体大小)

详细的项目页面,带有注释列表-项目
同样,创建一个详细页面。 区别仅在于导航器中有标题和附加输入。 在导航器中,设置标题-项目的名称。 如果未指定项目ID,我们将提供输入项目名称并创建一个新项目的名称。
export class Project extends PureComponent { static navigationOptions = ({ navigation }) => { const projectId = navigation.getParam('projectId') return { title: navigation.getParam('name', ''), headerRight: ( <AddButton onPress={() => navigation.navigate('Note', { projectId })} /> ) } } removeNote = noteId => { const { projectId, removeNote } = this.props removeNote(projectId, noteId) } navigateNote = noteId => { const { projectId, navigation } = this.props navigation.navigate('Note', { noteId, projectId }) } createProject = name => { const newProjectId = shortid.generate() this.props.navigation.setParams({ projectId: newProjectId, name }) this.props.addProject(newProjectId, name) } render() { const { projectId, project } = this.props if (!projectId) { return ( <ProjectNameInput onSubmitEditing={this.createProject} /> ) } return ( <NoteList notes={project.notes} onNavigateNote={this.navigateNote} onRemoveNote={this.removeNote} /> ) } }
项目页面是注释列表。 根据每个音符的ToR,有一个带有编辑和删除功能的上下文菜单。 您还可以通过滑动删除注释。 React Native中有一个单独的列表,可以滑动-SwipeableFlatList。
<SwipeableFlatList data={this.props.notes} bounceFirstRowOnMount={false} keyExtractor={item => item.id} maxSwipeDistance={MAX_SWIPE_DISTANCE} renderQuickActions={this.renderQuickActions} renderItem={this.renderItem} />
删除记事时,我们会要求您进行确认,为此,我们将其称为标准系统Alert
onRemoveNote = noteId => { Alert.alert( 'Remove Note', 'Do you want to remove note ?', [ { text: 'Cancel', onPress: () => {}}, { text: 'Remove', onPress: () => this.props.onRemoveNote(noteId) } ] ) }

上下文菜单有一个有趣的地方。 与警报不同,其在RN中针对Android和iOS的实现方式有所不同。
对于android,请使用弹出菜单
showPopupMenu = () => { const button = findNodeHandle(this._buttonRef) UIManager.showPopupMenu( button, [ 'Edit', 'Delete' ], e => console.error(e), (e, i) => this.onPressMenu(i) ) }
对于iOS-actionSheet
showActionSheet = () => { ActionSheetIOS.showActionSheetWithOptions({ options: [ 'Edit', 'Delete', 'Cancel' ], destructiveButtonIndex: 1, cancelButtonIndex: 2 }, this.onPressMenu ) }
有几种方法可以拆分特定于平台的代码。 我们将使用Platform对象。
onOpenMenu = Platform.select({ android: this.showPopupMenu, ios: this.showActionSheet })

详细注释页面-注释
注释页面也很原始。 但是,与以前的不同,我们使用状态来存储中间用户输入结果。
export class Note extends PureComponent { static navigationOptions = ({ navigation }) => ({ headerRight: ( <SaveButton onPress={navigation.getParam('onSaveNote')} /> ) }) state = { noteText: '' } componentDidMount() { this.props.navigation.setParams({ onSaveNote: this.onSaveNote }) } onSaveNote = () => { Keyboard.dismiss() const { projectId, noteId, note, navigation, addNote, editNote } = this.props const { noteText } = this.state if (!noteId) { const newNoteId = shortId.generate() navigation.setParams({ noteId: newNoteId }) addNote(projectId, newNoteId, noteText) } else if (noteText && noteText !== note.text) { editNote(projectId, noteId, noteText) } } onChangeNote = noteText => { this.setState({ noteText }) } render() { const initialTextValue = this.props.note ? this.props.note.text : '' const noteText = this.state.noteText || initialTextValue return ( <NoteDetail noteText={noteText} onChangeNoteText={this.onChangeNote} /> ) } }
记事的详细屏幕-经典的“愚蠢”组件-向上报告文本更改并显示父级传递给它的文本
export class NoteDetail extends PureComponent { static propTypes = { noteText: PropTypes.string, onChangeNoteText: PropTypes.func } render() { const { noteText, onChangeNoteText } = this.props return ( <View style={styles.note}> <TextInput multiline style={styles.noteText} value={noteText} placeholder="Type note text here ..." underlineColorAndroid="transparent" onChangeText={onChangeNoteText} /> </View> ) } }

总的来说,我们收到的申请与传统知识一样。 实验完成。 可以在常规
存储库中查看应用程序代码
React Native的总体,优缺点
优点:
对于熟悉React和Node.js和npm框架的开发人员来说,React Native是熟悉且可以理解的。 与通常的React一样,可以使用所有方法和库。
来自npm的大量js软件包。 最有可能的是,大多数标准任务已经解决,并且可能已获得MIT许可。
大型社区。 个体开发人员和大型公司都已使用RN进行开发,并继续使用它。
许多现成的UI组件集,例如
NativeBase ,
React Native Elements以及来自Facebook,Airbnb和Wix.com等大型公司的库。
清晰的工具包,用于从Hello World到
Instagram的便捷应用程序开发。
缺点:
该应用程序的启动速度比本地应用程序慢,并且调试存在一些困难。 带有和不带有调试器的JS代码都在不同的引擎上运行。 Airbnb在
一系列为什么他们在开发中放弃React Native
的文章中对此问题写得很好。
由于该工具包包含许多单独开发的软件包,因此可能会发生版本冲突和中断。
没有本机代码,并非无法完成所有事情。 而且,当您更改本机代码时,您将失去使用Expo的能力,并迫使自己使用标准的本机开发工具来构建应用程序。
非常感谢
Mirimon和
HeaTTheatR邀请
我参加此实验。 真令人兴奋。 最后,添加一个投票。