先看demo
写在最前面
从开始学RN到现在大概有2个星期天左右了,这里先记录一下,也算个小阶段总结。就目前感觉,RN的优势和劣势都很明显;
- 优势
- RN是混合开发一份代码多端使用
- 代码与前端相似,Web转RN比较轻松
- 劣势
- RN组件由多个第三方维护,更新不可控,会有停更不兼容的风险
- 会由于RN版本,组件版本,Xcode版本的不同而随机组合成各种坑(这点很令人烦躁,大部分时间都浪费在这)
- 多端兼容适配成本大,而且会随着项目规模而增大难度,到一定程度开发成本会比原生的高,如Airbnb宣布放弃使用RN,回归原生技术
正文
一 环境安装
官网说的很详细,按照官网的步骤基本没问题,就不多赘述
官网地址: https://reactnative.cn/docs/getting-started.html
二 熟悉RN
创建Q项目,并用iOS模拟器运行起来
react-native init q
cd q
react-native run-ios
项目内容如下:
android:Android工程文件
ios: iOS工程文件
node_modules: React-native组件依赖存放的文件夹
package.json: 依赖配置json, 类似于cocoPods中的“Podfile”
index.js: 项目入口 ...
先看index.js
, import
是引入文件,AppRegistry.registerComponent(appName, () => App);
整个的意思就是将工程目录的App.js
注册成组件并引入,所以一开始显示的即App.js
里面的内容
import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => App);
App.js
文件里面大致可以分成三部分
- 引入组件
- 搭建UI
- 样式
有过前端开发经验的朋友对View
,Text
,ScrollView
这些并不陌生,在React-native中,所有组件都要单独引入,所有组件及作用可看官方文档
import React from 'react';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
} from 'react-native';
import {
Header,
LearnMoreLinks,
Colors,
DebugInstructions,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';
这里部分内容是画UI,基本上和html
没差多少,都是用各种组件的堆砌。学过web或者小程序之类的看起来会很简单,没学过的话,建议选去学学最基本的html + css
const App: () => React$Node = () => {
return (
<StatusBar barStyle="dark-content" />
<SafeAreaView>
<ScrollView
.....
.....
</SafeAreaView>
</>
);
};
export default App;
这里是各种样式,大部分都是沿用css
的,看到这里大概感觉到React-native其实就是前段代码换个壳,对于有前段知识的人学起来应该会很轻松,没有相关知识的话建议还是先去学学基础的再来搞React-native
const styles = StyleSheet.create({
scrollView: {
backgroundColor: Colors.lighter,
},
.....
.....
});
三 尝试Demo
有这些了解后,可以试着做一个简单的列表页面
1.导入组件,需要的组件大概有这些,重点是FlatList
列表组件
import React, { Component } from "react";
import { Image, FlatList, StyleSheet, Text, View } from "react-native";
2.导出默认类App扩展组件,包括住下面的其他代码
export default class App extends Component {
}
3.创建个长度是8的data数组,后面可给FlatList
赋值用
constructor(props) {
super(props);
this.state = {
data: [{}, {}, {}, {}, {}, {}, {}, {}],
};
}
4.RN的render()函数实际上跟iOS的ViewDidLoad()函数相似,返回的就是具体的内容,内容很固定,只能是一个组件,这里我返回的是FlatList
,字段说明如下
- data:需要给予一个数组,数组长度与列表item对应
- style:列表样式
- renderItem:Item渲染,这里是直接调用
renderMovie
渲染 - keyExtractor:设置每个item唯一的key,类似于索引
render() {
return (
<FlatList
data={this.state.data}
style={styles.list}
renderItem={this.renderMovie.bind(this)}
keyExtractor={item => item.id}
/>
);
}
5.通过renderMovie
函数返回item的内容,这里可以任意发挥
renderMovie({ item }) {
return (
<View style={styles.container}>
<Image
source={{ uri: 'https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign=e4d6ea2325dda3cc0be4bf2639d25e3c/b64543a98226cffcb1f7cc0eb2014a90f703eaa9.jpg' }}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<View style={styles.titleWithout}>
<Text style={styles.title}>罗小黑战记</Text>
<Text style={styles.tip}>中国巨屏</Text>
</View>
<Text style={styles.score}>猫眼评分<Text style={styles.grade}> 9.8 </Text></Text>
<Text style={styles.starring}>主演:罗小黑,罗小白</Text>
<Text style={styles.cinema}>今天129加音乐反映124场</Text>
</View>
<Text style={styles.buy}>购买</Text>
</View>
);
}
6.最后是样式,其实这些随意发挥即可
这样简单的一个页面就完成了,完整代码如下,可以直接copy替代原有内容运行即可看到效果
import React, { Component } from "react";
import { Image, FlatList, StyleSheet, Text, View, TouchableOpacity } from "react-native";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
data: [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],
};
}
render() {
return (
<FlatList
data={this.state.data}
style={styles.list}
renderItem={this.renderMovie.bind(this)}
keyExtractor={item => item.id}
/>
);
}
renderMovie({ item }) {
return (
<View style={styles.container}>
<Image
source={{ uri: 'https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/w%3D268%3Bg%3D0/sign=e4d6ea2325dda3cc0be4bf2639d25e3c/b64543a98226cffcb1f7cc0eb2014a90f703eaa9.jpg' }}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<View style={styles.titleWithout}>
<Text style={styles.title}>罗小黑战记</Text>
<Text style={styles.tip}>中国巨屏</Text>
</View>
<Text style={styles.score}>猫眼评分<Text style={styles.grade}> 9.8 </Text></Text>
<Text style={styles.starring}>主演:罗小黑,罗小白</Text>
<Text style={styles.cinema}>今天129加音乐反映124场</Text>
</View>
<Text style={styles.buy}>购买</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
},
header: {
height: 20,
marginTop: 44,
},
rightContainer: {
flex: 1,
paddingLeft: 18,
},
titleWithout: {
flexDirection: "row",
fontWeight: "bold",
alignItems: "center",
},
title: {
fontSize: 14,
marginTop: 4,
lineHeight: 0,
},
tip: {
backgroundColor: "#999",
fontSize: 8,
textAlign: "center",
color: "#fff",
height: 14,
width: 40,
lineHeight: 14,
borderRadius: 2,
marginLeft: 4,
marginTop: 4,
},
score: {
paddingTop: 8,
fontSize: 12,
color: "#666",
},
starring: {
paddingTop: 4,
fontSize: 12,
color: "#666",
},
cinema: {
paddingTop: 4,
fontSize: 12,
color: "#666",
},
buy: {
fontSize: 12,
// color:'#333',
width: 46,
height: 24,
lineHeight: 24,
textAlign: "center",
backgroundColor: "#D44145",
color: "#fff",
borderRadius: 12,
marginRight: 20,
},
grade: {
color: "#DF8D7A",
},
year: {
textAlign: "center"
},
thumbnail: {
width: 68,
height: 94,
marginLeft: 20,
marginTop: 10,
marginBottom: 10,
},
list: {
paddingTop:40,
backgroundColor: "#F5FCFF"
},
headerOutline: {
backgroundColor: "#fff",
marginTop: 44,
},
headerInside: {
backgroundColor: "#f5f5f5",
flexDirection: "row",
justifyContent: 'space-between',
marginLeft: 20,
marginRight: 20,
marginBottom: 6,
paddingTop: 10,
paddingBottom: 4,
paddingLeft: 10,
paddingRight: 10,
},
trendIcon: {
width: 10,
height: 14,
marginLeft: 10,
marginTop: -1,
},
trendText: {
height: 22,
color: '#333',
fontWeight: "bold",
},
trendR: {
color: '#333',
fontSize: 10,
fontWeight: "bold",
height: 22,
},
trendRText: {
},
trendMoney: {
color: '#D24349',
},
});
二 Navigation与Tabbar
如图,最终目的是创建一个带Navigation
,Tabbar
的demo,可分为三个步骤
- 安装组件
- 创建tabbar上的两个跟根页面和一个详情页面
- 修改
index.js
入口
1.先安装react-navigation组件
注:这里有个天坑,react-navigation4与3差距很大,现在网上的教程基本使用的都是react-navigation 3,这里我也是兜兜转转搞了许久才意识到的,大家都是初学者,建议安装版本3
yarn add react-navigation@3.5.1
yarn add react-native-gesture-handler
这里可能会报这个错
Error: Requiring unknown module "447". If you are sure the module exists, try restarting Metro. You may also want to run `yarn` or `npm install`.
这个错原因很多,可以尝试
npm install
react-native run-ios
或者
cd ios
pod install
cd ..
react-native run-ios
2.创建detailsScreen.js
,settingScreen.js
,navigation.js
文件
detailsScreen.js
内容
import React from 'react';
import {
View,
Text,
Button,
Image,
} from 'react-native';
export default class detailsScreen extends React.Component {
render() {
return (
<View style={{flex:1, alignItems:'center',justifyContent: 'center'}}><Text>详情页</Text></View>
);
}
}
settingScreen.js
内容
import React from 'react';
import {
View,
Text,
Button,
Image,
} from 'react-native';
export default class settingScreen extends React.Component {
render() {
return (
<View style={{flex:1, alignItems:'center',justifyContent: 'center'}}><Text>设置页</Text></View>
);
}
}
navigation.js
内容需要分步讲解一下,首先引入所有需要的组件与页面
import React from 'react';
import { Text } from 'react-native'
import HomeScreen from "./App";
import DetailsScreen from "./detailScreen";
import SettingScreen from "./settingScreen";
import {
createStackNavigator,
createAppContainer,
createBottomTabNavigator
} from 'react-navigation';
这里是声明HomeStack
,SettingsStack
两个组件;
createStackNavigator 提供APP屏幕之间切换的能力,它是以栈的形式还管理屏幕之间的切换,新切换到的屏幕会放在栈的顶部。
const HomeStack = createStackNavigator({
Home: { screen: HomeScreen, }
})
const SettingsStack = createStackNavigator({
Settings: { screen: SettingScreen },
})
这里声明TabNavigator
createBottomTabNavigator(RouteConfigs, BottomTabNavigatorConfig)相当于iOS里面的TabBarController,屏幕下方的标签栏
- RouteConfigs(必选):路由配置对象是从路由名称到路由配置的映射,告诉导航器该路由呈现什么。
- BottomTabNavigatorConfig(可选):配置导航器的路由(如:默认首屏,navigationOptions,paths等)样式(如,转场模式mode、头部模式等)。
const TabNavigator = createBottomTabNavigator(
{
Home: { screen: HomeStack },
Settings: { screen: SettingsStack }
},
{
navigationOptions: () => ({
header: null
}),
defaultNavigationOptions: ({ navigation }) => ({
tabBarLabel: ({ tintColor}) => {
const { routeName } = navigation.state
switch (routeName) {
case 'Home':
return <Text style={{ color: tintColor, fontSize: 12 }}>{'首页'}</Text>
case 'Settings':
return <Text style={{ color: tintColor, fontSize: 12 }}>{'设置'}</Text>
}
},
tabBarIcon: ({ focused, tintColor }) => {
let urld
const { routeName } = navigation.state
switch (routeName) {
case 'Home':
return <Image source={{ uri: 'https://static.easyicon.net/preview/119/1191814.gif' }} style={[{height: 20, width: 20}]}/>
case 'Settings':
return <Image source={{ uri: 'https://static.easyicon.net/preview/121/1215319.gif' }} style={[{height: 20, width: 20}]}/>
}
}
}),
tabBarOptions: {
inactiveTintColor: 'gray',
}
}
)
最后设置路由并返回
const AppStack = createStackNavigator({
Tabs: TabNavigator,
Details: { screen: DetailsScreen },
}, {
defaultNavigationOptions: () => ({
})
})
export default createAppContainer(AppStack)
完整代码如下
import React from 'react';
import { Text,Image} from 'react-native'
import HomeScreen from "./App";
import DetailsScreen from "./detailScreen";
import SettingScreen from "./settingScreen";
import {
createStackNavigator,
createAppContainer,
createBottomTabNavigator
} from 'react-navigation';
const HomeStack = createStackNavigator({
Home: { screen: HomeScreen, }
})
const SettingsStack = createStackNavigator({
Settings: { screen: SettingScreen },
})
const TabNavigator = createBottomTabNavigator(
{
Home: { screen: HomeStack },
Settings: { screen: SettingsStack }
},
{
navigationOptions: () => ({
header: null
}),
defaultNavigationOptions: ({ navigation }) => ({
tabBarLabel: ({ tintColor}) => {
const { routeName } = navigation.state
switch (routeName) {
case 'Home':
return <Text style={{ color: tintColor, fontSize: 12 }}>{'首页'}</Text>
case 'Settings':
return <Text style={{ color: tintColor, fontSize: 12 }}>{'设置'}</Text>
}
},
tabBarIcon: ({ focused, tintColor }) => {
let urld
const { routeName } = navigation.state
switch (routeName) {
case 'Home':
return <Image source={{ uri: 'https://static.easyicon.net/preview/119/1191814.gif' }} style={[{height: 20, width: 20}]}/>
case 'Settings':
return <Image source={{ uri: 'https://static.easyicon.net/preview/121/1215319.gif' }} style={[{height: 20, width: 20}]}/>
}
}
}),
tabBarOptions: {
inactiveTintColor: 'gray',
}
}
)
const AppStack = createStackNavigator({
Tabs: TabNavigator,
Details:DetailsScreen,
}, {
defaultNavigationOptions: () => ({
})
})
export default createAppContainer(AppStack)
3.修改index.js
入口
这里仅仅只是把入口改为navigation.js
import {AppRegistry} from 'react-native';
import Nav from './navigation.js';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => Nav);
保存基本就能看到App的架子大概形成了
接下来要设置一下点击事件,让demo可以跳转
先回到App.js
页面 设置首页导航栏标题
static navigationOptions = ({ navigation }) => {
const { params = {} } = navigation.state
const onPressRightButtonFunc = params.openPublisher || function () { }
return {
title: '首页',
}
}
引入TouchableOpacity
设置点击事件
import { TouchableOpacity } from "react-native";
...
...
renderMovie({ item }) {
const navigate = this.props.navigation;
return (
<TouchableOpacity activeOpacity={0.5} onPress={() => navigate.navigate('Details')} > //'Details'是之前在navigation.js声明好的了
... //这里是之前item的UI代码
</TouchableOpacity>
到这里基本已经完成了这个demo,其他的都是一些重复的UI工作也不赘述了,这是稍微优化过的代码和详情页,看不懂的可以根据根据这源码来。
这里我的源码是将基本组件都下好,下载运行即可,因为比较大先上传到百度云。
链接: https://pan.baidu.com/s/1854tyx1R_Bjz4A57xvxn1g 提取码: kgmb
网上的其他demo对新人都很不友好,需要安装各个组件再运行起来,各种报错容易劝退新人
后记
初衷是想让新手快速的入门制作一个demo,后面发现还是需要一点web经验的,内容有点多,说得没那么细致的地方请见谅。后续会一直持续更新这个demo;