react native 学习指南
1.运行环境搭建
1.安装nodejs
nodejs >=12
2.安装yarn
npm i -g yarn
3.安装react native脚手架
npm i -g react-native-cli
安卓环境搭建(按react-native官网操作)
1.安装JDK (确保能执行javac -version 查的到)
2.安装Android Studio
3.安装Android SDK
4.配置环境变量
//新建项目
npx react-native@latest init project1
yarn android
2.模拟器调试
模拟器上
ctrl/cmd+m debug
3.RN中的样式与css的不同
//没有继承性
//RN中的继承只发生在Text组件上
样式名采用小驼峰
font-size=>fontSize
所有尺寸没有单位
width:100
有些特殊的样式名
marginHorizontal(水平外边距)
marginVertical(垂直外边距)
样式声明方式:
① 通过style属性声明
<组件 style={{样式}} />
<组件 style={[{样式1},...{样式N}]} />
② 在style属性中调用StyleSheet写声明的样式
引入:import {StyleSheet,View} from 'react-native'
声明:const styles=StyleSheet.create({
foo:{样式1},
bar:{样式2}
})
使用:
<View style={[styles.foo],styles.bar]}>内容</View>
import React, { Component } from 'react'
import { Text, View, StyleSheet } from 'react-native'
const styles = StyleSheet.create({
h1: {
fontSize: 50,
color: 'yellowgreen',
fontWeight: 700
}
})
const Home = () => {
return (
<View>
<Text style={[{
color: 'red',
fontSize: 20
}, { color: 'green' }]}> textInComponent </Text>
<Text style={[{
color: 'red',
fontSize: 40
}, { color: 'red' }]}> textInComponent </Text>
<Text style={[styles.h1]}> textInComponent </Text>
</View>
)
}
export default Home
4.flex布局
flex布局:
在RN中 //flexDirection主轴默认为竖向column 与web布局相反
flexShrink 默认值为0 表示元素在容器中不会被缩小 //web布局默认为1 表示元素可以缩小到其原始尺寸的任何比例
alignContent默认值为 flex-start(而不是 stretch)
5.标签
View 等同于 无滚动的 div
ScrollView 等同于 可滚动的 div
Text 等同于 P标签
Image 等同于 img
6.组件
概述
核心组件:RN组件
原生组件:安卓或ios组件
View 视图组件
Text 文本组件
Alert 警告框组件
Button 按钮组件
Switch 开关组件
StatusBar 状态栏组件
ActivityIndicator 加载指示器组件
Image 图片组件
TextInput 输入框组件
Touchable 触碰组件(共三个)
ScrollView 滚动视图组件
SectionList 分组列表组件
FlatList 高性能列表组件
Animated 动画组件
Button组件
Button组件
//属性:
//onPress 类似click事件
//title 按钮文字
//color 文本的颜色(iOS),或是按钮的背景色(Android)
//disabled 设置为 true 时此按钮将不可点击。
//accessibilityLabel 用于给残障人士显示的文本(比如读屏应用可能会读取这一内容)
//示例:
<Button
title="Press me"
color="#f194ff"
onPress={() => Alert.alert('Button with adjusted color pressed')}
/>
Alert组件
Alert组件
//示例:
Alert.alert(
'更新版本',
'发现新版本,是否现在更新?',
[
{
text:'稍后再试',
onPress:()=>console.log('稍后提醒我')
},
{
text: '取消',
onPress: () => {
console.log('cancel')
},
style: 'cancel'
},
{
text: '确认',
onPress: () => {
console.log('Ok')
},
style: 'default'
},
]
)
StatusBar组件与Switch组件
StatusBar
//状态栏组件
//animated 指定状态栏的变化是否应以动画形式呈现。目前支持这几种样式:backgroundColor, barStyle 和 hidden。
//hidden 是否隐藏状态栏
//backgroundColor 状态栏的背景色
//barStyle 在 Android 上此属性仅对 6.0 (API 23)及以上版本生效。
//'default', 'light-content', 'dark-content'
<StatusBar
animated={true}
hidden={false}
backgroundColor="red"// 仅在安卓应用下有效
barStyle={'dark-content'}
/>
示例:
import React, { Component, useState } from 'react'
import { Text, StyleSheet, View, StatusBar, Switch } from 'react-native'
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
}
})
export default () => {
const [hideStatusBar, setHideStatusBar] = useState(false)
// 切换
const toggleStatusBar = () => {
setHideStatusBar(!hideStatusBar)
}
return (
<View style={[styles.container]}>
<StatusBar
animated={true}
hidden={hideStatusBar}
backgroundColor="yellow"// 仅在安卓应用下有效
barStyle={'dark-content'}
networkActivityIndicatorVisible={true}
translucent={true}
/>
<Text> textInComponent </Text>
<Switch
trackColor={{ false: '#999', true: '#666' }}
thumbColor={hideStatusBar?'red':'white'}
value={hideStatusBar}
onValueChange={
toggleStatusBar
}
/>
</View>
)
}
Image组件
//支持base64和png格式
//请注意对于网络和 base64 数据的图片需要手动指定尺寸!
import { StyleSheet, Text, View, Image, Dimensions } from 'react-native'
import React from 'react'
export default () => {
return (
<View style={[styles.container]}>
{/* 本地图片 */}
<Image
style={[styles.banner]}
source={require('../../images/1.png')}
/>
{/* 网络图片 */}
<Image
style={styles.tinyLogo}
source={{
uri: 'https://reactnative.dev/img/tiny_logo.png',
}}
/>
{/* base64格式 */}
<Image
style={styles.logo}
source={{
uri: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADMAAAAzCAYAAAA6oTAqAAAAEXRFWHRTb2Z0d2FyZQBwbmdjcnVzaEB1SfMAAABQSURBVGje7dSxCQBACARB+2/ab8BEeQNhFi6WSYzYLYudDQYGBgYGBgYGBgYGBgYGBgZmcvDqYGBgmhivGQYGBgYGBgYGBgYGBgYGBgbmQw+P/eMrC5UTVAAAAABJRU5ErkJggg==',
}}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
banner: {
width: Dimensions.get('window').width,
height: 300,
marginVertical: 20
},
tinyLogo:{
width:66,
height:58
},
logo:{
width:66,
height:58
}
})
TextInput(输入框)组件
/*
唯一做法:
onChangeText中用setState把用户的输入写入到 state 中,然后在需要取值的地方从 this.state 中取出值
*/
//keyboardType 弹出何种键盘
/*
default
number-pad
decimal-pad
numeric
email-address
phone-pad
*/
import { StyleSheet, Text, View, Button, TextInput, Dimensions, Alert } from 'react-native'
import React, { useState } from 'react'
export default () => {
const [userName, setUserName] = useState('')
const [password, setPassword] = useState('')
const [phone, setPhone] = useState('')
const toLogin = () => {
Alert.alert(userName, password)
}
return (
<View style={[styles.container]}>
<TextInput
style={[
styles.input
]}
value={userName}
onChangeText={(text) => setUserName(text)}
placeholder="请输入用户名"
/>
<TextInput
secureTextEntry={true}
style={[
styles.input
]}
value={password}
onChangeText={(text) => setPassword(text)}
placeholder="请输入密码"
/>
<TextInput
keyboardType='number-pad'
style={[
styles.input
]}
value={phone}
onChangeText={(text) => setPhone(text)}
placeholder="手机号"
/>
<TextInput
autoFocus
placeholder='请输入自我介绍'
style={[
styles.input
]}
multiline={true}
numberOfLines={5}
textAlignVertical='top'
/>
<View style={[styles.btn]}>
<Button title="登录" onPress={toLogin} />
</View>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
input: {
width: Dimensions.get('window').width-20,
margin: 10,
borderWidth: 1,
borderColor: '#333',
paddingHorizontal: 5,
},
btn: {
margin: 10
}
})
Touchable(触碰组件 三种)
//TouchableHighlight
//TouchableOpacity(常用)
//TouchableWithoutFeedback
import { StyleSheet, Text, View, Touchable, TouchableHighlight, TouchableOpacity, TouchableWithoutFeedback } from 'react-native'
import React from 'react'
export default () => {
return (
<View style={[styles.container]}>
<TouchableHighlight
onPress={() => {
console.log('触碰高亮');
}}
>
<View style={[styles.item]}>
<Text >触碰高亮</Text>
</View>
</TouchableHighlight>
<TouchableOpacity
onPress={() => {
console.log('触碰高亮');
}}
>
<View style={[styles.item]}>
<Text >触碰透明度变化</Text>
</View>
</TouchableOpacity>
<TouchableWithoutFeedback
onPress={() => {
console.log('触碰高亮');
}}
>
<View style={[styles.item]}>
<Text >触碰无响应</Text>
</View>
</TouchableWithoutFeedback>
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
item: {
margin: 20,
padding: 10,
borderWidth: 1,
borderColor: 'red'
}
})
ScrollView与SafeAreaView
//SafeView 安全举例 针对ios刘海屏
//滚动容器
import { StyleSheet, Text, View, ScrollView, SafeAreaView, Platform } from 'react-native'
import React from 'react'
export default function index() {
return (
<SafeAreaView>
<ScrollView
showsHorizontalScrollIndicator={false}
horizontal={true}
style={{
backgroundColor: '#dfb'
}}>
<Text style={[styles.nav]}>新闻</Text>
<Text style={[styles.nav]}>娱乐</Text>
<Text style={[styles.nav]}>财经</Text>
<Text style={[styles.nav]}>军事</Text>
<Text style={[styles.nav]}>体育</Text>
<Text style={[styles.nav]}>新闻</Text>
<Text style={[styles.nav]}>时尚</Text>
<Text style={[styles.nav]}>科技</Text>
</ScrollView>
<ScrollView style={[styles.scrollView]} contentContainerStyle={{ margin: 10 }} showsVerticalScrollIndicator={false}>
<Text style={[styles.text]}> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat. Duis aute irure dolor in
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim id est laborum.</Text>
{/* 解决ScrollView在Android下滚动不到底的问题 */}
<View style={{ height: Platform.OS === 'ios' ? 0 : 70 }}></View>
</ScrollView>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
scrollView: {
// flex:1,
backgroundColor: '#ddd',
marginVertical: 20
},
nav: {
margin: 10,
height: 50,
fontSize: 30
},
text: {
fontSize: 50
}
})
SectionList分组组件
//sections 绑定的数据源
//keyExtractor 生成唯一key
//renderItem 渲染每一个列表项 必须返回一个 react 组件
import { StyleSheet, Text, View, SectionList, StatusBar, SafeAreaView } from 'react-native'
import React, { useState } from 'react'
const DATA = [
{
title: 'Main dishes',
data: ['Pizza', 'Burger', 'Risotto'],
},
{
title: 'Sides',
data: ['French Fries', 'Onion Rings', 'Fried Shrimps'],
},
{
title: 'Drinks',
data: ['Water', 'Coke', 'Beer'],
},
{
title: 'Desserts',
data: ['Cheese Cake', 'Ice Cream'],
},
];
export default function index() {
const [isRefresh, setIsRefresh] = useState(false)
const loadData = () => {
setIsRefresh(true)
setTimeout(() => {
setIsRefresh(false)
alert('下拉刷新完毕')
}, 2000);
}
return (
<SafeAreaView>
<SectionList
sections={DATA}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => (
<View style={styles.item}>
<Text style={styles.title}>{item}</Text>
</View>
)}
renderSectionHeader={({ section: { title } }) => (
<Text style={styles.header}>{title}</Text>
)}
// 声明项目之间的分隔线
ItemSeparatorComponent={() => {
return <View style={{
borderBottomWidth: 1,
borderColor: 'red'
}}></View>
}}
// 数据源为空时候展示的组件
ListEmptyComponent={() => {
return <Text style={{
fontSize: 50,
textAlign: 'center',
}}>暂无数据</Text>
}}
// 下拉刷新
refreshing={isRefresh}
onRefresh={loadData}
// 上拉加载
onEndReachedThreshold={0.1} //声明触底的比例 0.1代表10%
onEndReached={() => {
alert('到底了')
}}
//声明列表头部
ListHeaderComponent={() => {
return <Text style={{ fontSize: 40 }}>三国英雄榜</Text>
}}
// 声明列表尾部
ListFooterComponent={()=>{
return <Text>没有更多了</Text>
}}
/>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: StatusBar.currentHeight,
marginHorizontal: 16,
},
item: {
backgroundColor: '#f9c2ff',
padding: 20,
marginVertical: 8,
},
header: {
fontSize: 32,
backgroundColor: '#fff',
},
title: {
fontSize: 24,
},
})
FlatList(高性能的简单列表组件)
import React, { useState } from 'react';
import { SafeAreaView, View, FlatList, StyleSheet, Text, StatusBar, TouchableOpacity } from 'react-native';
const DATA = [
{
id: '1',
title: '头条',
},
{
id: '2',
title: '军事',
},
{
id: '3',
title: '科技',
},
{
id: '4',
title: '娱乐',
},
{
id: '5',
title: '时尚',
},
{
id: '6',
title: '社会',
},
{
id: '7',
title: '财经',
},
{
id: '8',
title: '体育',
},
{
id: '9',
title: '明星',
}
];
const Item = ({ title }) => {
return (
<View style={styles.item}>
<Text style={styles.title}>{title}</Text>
</View>
);
}
export default () => {
const [isRefresh, setIsRefresh] = useState(false)
const [selectedId, setSelectedId] = useState(null)
const loadData = () => {
setIsRefresh(true)
setTimeout(() => {
setIsRefresh(false)
alert('下拉刷新完毕')
}, 1000);
}
const renderItem = ({ item }) => {
console.log(item)
const backgroundColor = item.id === selectedId ? 'yellow' : '#f9c2ff'
return (
<TouchableOpacity style={[styles.item,{backgroundColor}]} onPress={()=>{
setSelectedId(item.id)
}}>
<Item title={item.title} />
</TouchableOpacity>
)
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={DATA}
//从data中挨个取出数据并渲染到列表中
renderItem={renderItem}
keyExtractor={item => item.id}
horizontal={false} //水平布局模式
initialScrollIndex={0} //初始滚动索引
initialNumToRender={5}//指定初始渲染数据的数量 一般数量要填满一屏 懒加载
// numColumns={1} //指定列数 数据项必须等高(无法支持瀑布流
inverted={false} //列表反转
//extraData={selectedId}
//如果有除data以外的数据用在列表中(不论是用在renderItem还是头部或者尾部组件中),请在此属性中指定。同时此数据在修改时也需要先修改其引用地址(比如先复制到一个新的 Object 或者数组中),然后再修改其值,否则界面很可能不会刷新。
// 声明项目之间的分隔符
ItemSeparatorComponent={() => <View style={[styles.itemSeprator]}></View>}
// 列表数据为空时 展示的组件
ListEmptyComponent={() => <View style={{ fontSize: 30 }}>暂无数据</View>}
// 下拉刷新
refreshing={isRefresh}
onRefresh={loadData}
// 上拉加载
onEndReachedThreshold={0.1}
onEndReached={() => {
alert('到底了')
}}
// 声明列表头部
ListHeaderComponent={() => <Text>列表头部</Text>}
// 声明列表尾部
ListFooterComponent={() => <Text>没有更多了</Text>}
/>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: StatusBar.currentHeight || 0,
},
item: {
marginVertical: 20,
marginHorizontal: 20,
},
title: {
fontSize: 32,
},
itemSeprator: {
backgroundColor: 'red',
height: 1,
marginHorizontal: 16,
}
});
Animated(动画)
//组件必须经过特殊处理才能用于动画
RN中可以直接使用的动画组件
Animated.View
Animated.Text
Animated.ScrollView
Animated.Image
如何创建动画(步骤)
创建初始值
⭕Animated.Value() 单个值
⭕Animated.ValueXY() 向量值
将初始值绑定到动画组件上
⭕一般将其绑定到某个样式属性下,例如:opacity、translate、
通过动画类型API 一帧一帧地更改初始值
⭕Animated.decay() 加速效果
⭕Animated.spring() 弹跳效果
⭕Animated.timing() 时间渐变效果
示例:
import React, {useRef} from 'react';
import {
Animated,
Text,
View,
StyleSheet,
Button,
SafeAreaView,
} from 'react-native';
const App = () => {
//1. fadeAnim 创建初始值 将不透明度设置为 0
const fadeAnim =new Animated.Value(0);
const fadeIn = () => {
// 3.通过动画类型API 一帧一帧地更改初始值 (此处使用的是时间渐变效果 Animated.timing)
Animated.timing(fadeAnim, {
toValue: 1,
duration: 5000,
useNativeDriver: true,
}).start();
};
const fadeOut = () => {
// 3.通过动画类型API 一帧一帧地更改初始值 (此处使用的是时间渐变效果 Animated.timing)
Animated.timing(fadeAnim, {
toValue: 0,
duration: 3000,
useNativeDriver: true,
}).start();
};
return (
<SafeAreaView style={styles.container}>
{/* 使用的动画组件 */}
<Animated.View
style={[
styles.fadingContainer,
{
// 2.将不透明度值绑定到属性上
opacity: fadeAnim,
},
]}>
<Text style={styles.fadingText}>Fading View!</Text>
</Animated.View>
<View style={styles.buttonRow}>
<Button title="Fade In View" onPress={fadeIn} />
<Button title="Fade Out View" onPress={fadeOut} />
</View>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
fadingContainer: {
padding: 20,
backgroundColor: 'powderblue',
},
fadingText: {
fontSize: 28,
},
buttonRow: {
flexBasis: 100,
justifyContent: 'space-evenly',
marginVertical: 16,
},
});
export default App;
第三方组件
webView
//安装
yarn add react-native-webview
//配置
https://github.com/react-native-webview/react-native-webview
//使用
指定url地址
直接渲染html代码
//cd到ios目录
pod install
7. API
Platform
//环境判断
Platform.OS //enum('android', 'ios')
Platform.Version; //版本号 安卓端为number ios端为string
Dimensions
本模块用于获取设备屏幕的宽高。
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
//示例:
import React, { useState, useEffect } from "react";
import { View, StyleSheet, Text, Dimensions } from "react-native";
const window = Dimensions.get("window");
const screen = Dimensions.get("screen");
const App = () => {
const [dimensions, setDimensions] = useState({ window, screen });
const onChange = ({ window, screen }) => {
setDimensions({ window, screen });
};
useEffect(() => {
Dimensions.addEventListener("change", onChange);
return () => {
Dimensions.removeEventListener("change", onChange);
};
});
return (
<View style={styles.container}>
<Text>{`Window Dimensions: height - ${dimensions.window.height}, width - ${dimensions.window.width}`}</Text>
<Text>{`Screen Dimensions: height - ${dimensions.screen.height}, width - ${dimensions.screen.width}`}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center"
}
});
export default App;
8.打包(此方式打包的是 JavaScript 包和资源非APK, 适合与原生混合开发,原生层加载打出来的资源即可)
原生加载资源
// 在 Activity 中加载 JS 包
ReactInstanceManager manager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("localInformation.android.bundle") // 指定 JS 包文件名
.setJSMainModulePath("index")
.addPackage(new MainReactPackage())
.build();
ReactRootView rootView = new ReactRootView(this);
rootView.startReactApplication(manager, "YourAppName", null);
注意事项
- 文件位置:确保 JS 包和资源文件放在原生项目的正确目录下(
assets/和res/)。 - 版本匹配:React Native 版本需要与原生应用中的
react-native依赖一致。 - 增量更新:如果需要动态更新 JS 包,可以将文件下载到外部存储(如
sdcard),然后通过路径加载。
这种方式的优势是原生代码与 JS 代码解耦,前端团队可以独立开发和测试,然后将生成的 bundle 文件交付给原生团队集成。
打包命令:
"bundle:ios": "node node_modules/react-native/local-cli/cli.js bundle --platform ios --dev false --entry-file index.js --bundle-output ios/bundle/localInformation.ios.jsbundle --assets-dest ios/bundle/release_ios/",
"bundle:android": "react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/localInformation.android.bundle --assets-dest android/app/src/main/res/",
"bundle:all": "npm run bundle:ios && npm run bundle:android"
打包命令参数解释(以bundle:android示例:
--platform android:指定打包目标平台为 Android。--dev false:表示生成生产环境(非开发环境)的优化代码。--entry-file index.js:指定入口文件为项目根目录下的index.js。--bundle-output .../localInformation.android.bundle:指定 JS 打包文件的输出路径,该文件包含所有 JavaScript 代码。(注:localInformation是打包后的名称,可以修改)--assets-dest .../res/:指定资源文件(如图片、字体等)的输出目录,会生成对应的 Android 资源目录结构。
- JS 打包文件
路径:android/app/src/main/assets/localInformation.android.bundle
说明:包含所有 JavaScript 代码(React 组件、逻辑等),是一个单一的二进制文件。 - 资源文件目录(如果使用网络图片则不需要提供资源给客户端)
路径:android/app/src/main/res/
****结构示例:
res/
├── drawable-mdpi/ # 中等密度图片
│ └── image.png
├── drawable-hdpi/ # 高密度图片
│ └── image.png
├── drawable-xhdpi/ # 超高密度图片
│ └── image.png
├── raw/ # 其他资源(如字体、音频)
│ └── font.ttf
└── ...
- 原生项目整体结构
执行命令后,android/ 目录结构大致如下:
android/
├── app/
│ └── src/
│ └── main/
│ ├── assets/
│ │ └── localInformation.android.bundle # JS 打包文件
│ └── res/
│ ├── drawable-*/ # 图片资源
│ ├── raw/ # 其他资源
│ └── ...
└── ...
补充说明
为何需要这些文件?
原生 Android 应用会在运行时加载 localInformation.android.bundle 和资源文件,将 React Native 组件渲染为原生视图。
文件用途:
localInformation.android.bundle:包含应用的所有逻辑代码。
res/ 目录:包含图片、字体等静态资源,与原生 Android 项目的资源结构完全兼容。
如果需要将这些文件提供给原生团队,只需交付 assets/ 和 res/ 目录下的内容即可。