React Native(RN)采用JavaScript编写应用逻辑,利用React框架构建UI。在运行时,RN通过桥接机制将JavaScript代码与原生平台代码进行通信。对于iOS和Android,RN分别有对应的原生渲染引擎。在iOS上,它利用iOS的原生UI组件和API;在Android上,则使用Android的原生UI组件和API。这样,RN代码在不同平台上可以调用各自平台的原生能力,实现界面渲染和功能交互。当开发者编写一个按钮组件时,RN会根据当前运行的平台,将这个按钮渲染为iOS风格或Android风格的原生按钮。这一过程中,RN的桥接机制起到关键作用,它负责将JavaScript层的指令传递给原生层,同时将原生层的事件和数据反馈给JavaScript层。
| React Native UI 组件 | Android 原生视图 | iOS 原生视图 | Web 标签 | 说明 |
|---|---|---|---|---|
<View> |
<ViewGroup> |
<UIView> |
A non-scrolling <div> |
不支持滚动,支持flexbox、触摸处理和无障碍性控件的容器 |
<Text> |
<TextView> |
<UITextView> |
<p> |
显示、样式和嵌套文本字符串,甚至处理触摸事件 |
<Image> |
<ImageView> |
<UIImageView> |
<img> |
显示不同类型的图片 |
<ScrollView> |
<ScrollView> |
<UIScrollView> |
<div> |
一个通用的滚动容器,可以包含多个组件和视图,适合用来显示数量不多的滚动元素,立即渲染所有元素 |
<TextInput> |
<EditText> |
<UITextField> |
<input type="text"> |
使用户可以输入文本 |
| <FlatList> | 显示较长的滚动列表,性能更好,垂直的滚动列表,其中的元素之间结构近似而仅数据不同,FlatList并不立即渲染所有元素,而是优先渲染屏幕上可见的元素 |
|||
| <SectionList> | 可以分组的list |
React基础:components 组件、JSX、props 属性、state 状态
Function Compoments
import React from 'react';
import { Text } from 'react-native';
const Cat = () => {
// 这个函数的
返回值就会被渲染为一个 React 元素return (
<Text>Hello, I am your cat!</Text>
);
}
// 使用了export default语句来导出这个组件,以使其可以在其他地方引入使用
export default Cat;
Class Compoments
import React, { Component } from 'react';
import { Text } from 'react-native';
// 定义组件首先要继承(extends)自
Componentclass Cat extends Component {
// Class 组件必须有一个
render()函数,它的返回值会被渲染为一个 React 元素:render() {
return (
<Text>Hello, I am your cat!</Text>
);
}
}
export default Cat;
JSX: JavaScript XML
一种在React组件内部构建标签的类XML语法。JSX为react.js开发的一套语法糖,也是react.js的使用基础。React在不使用JSX的情况下一样可以工作,然而使用JSX可以提高组件的可读性
// 使用JS
render:function (){
return React.createElement('div', {className: "divider"},
"Label Text",
React.createElement('hr')
);
}
// 使用JSX
render:function (){
return <div className="divider">
Labbel Text<hr/>
</div>
}
在 React Native 0.71 版本之前,JSX 语法糖的实质是调用React.createElement方法,所以你必须在文件头部引用import React from 'react'。但在 React Native 0.71 版本之后,官方引入了新的 JSX 转换,可以不用 再在文件头部写import React from 'react'。
Props 是"properties"(属性)的简写。Props 使得我们可以定制组件
import React from 'react';
import { Text, View } from 'react-native';
const Cat = (props) => {
return (
<View>
<Text>Hello, I am {props.name}!</Text>
</View>
);
}
const Cafe = () => {
return (
<View>
<Cat name="Maru" />
<Cat name="Jellylorum" />
<Cat name="Spot" />
</View>
);
}
export default Cafe;
State 状态:如果把 props 理解为定制组件渲染的参数, 那么state就像是组件的私人数据记录。状态用于记录那些随时间或者用户交互而变化的数据。
Function Components
import React, { useState } from "react";
import { Button, Text, View } from "react-native";
const Cat = (props) => {
const [isHungry, setIsHungry] = useState(true);
return (
<View>
<Text>
I am {props.name}, and I am {isHungry ? "hungry" : "full"}!
</Text>
<Button
onPress={() => {
setIsHungry(false);
}}
disabled={!isHungry}
title={isHungry ? "Pour me some milk, please!" : "Thank you!"}
/>
</View>
);
}
const Cafe = () => {
return (
<>
<Cat name="Munkustrap" />
<Cat name="Spot" />
</>
);
}
export default Cafe;
Class Componentsimport React, { Component } from "react";
import { Button, Text, View } from "react-native";
class Cat extends Component {
state = { isHungry: true };
render() {
return (
<View>
<Text>
I am {this.props.name}, and I am
{this.state.isHungry ? " hungry" : " full"}!
</Text>
<Button
onPress={() => {
this.setState({ isHungry: false });
}}
disabled={!this.state.isHungry}
title={
this.state.isHungry ? "Pour me some milk, please!" : "Thank you!"
}
/>
</View>
);
}
}
class Cafe extends Component {
render() {
return (
<>
<Cat name="Munkustrap" />
<Cat name="Spot" />
</>
);
}
}
export default Cafe;
Platform 模块
React Native 提供了一个检测当前运行平台的模块,Platform.OS在 iOS 上会返回ios,而在 Android 设备或模拟器上则会返回android,Platform.select(),它可以以 Platform.OS 为 key,从传入的对象中返回对应平台的值
import {Platform, StyleSheet} from 'react-native';
const styles = StyleSheet.create({
height: Platform.OS === 'ios' ? 200 : 100,
});
Platform.Version // Android 版本、iOS 版本
特定平台后缀
当不同平台的代码逻辑较为复杂时,最好是放到不同的文件里,这时候我们可以使用特定平台后缀。React Native 会检测某个文件是否具有.ios.或是.android.的后缀,然后根据当前运行的平台自动加载正确对应的文件。
BigButton.ios.js
BigButton.android.js
import BigButton from './BigButton'; // 去掉平台后缀直接引用:
Style:要求使用了驼峰命名法,后声明的属性会覆盖先声明的同名属性
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
const LotsOfStyles = () => {
return (
<View style={styles.container}>
<Text style={styles.red}>just red</Text>
<Text style={styles.bigBlue}>just bigBlue</Text>
<Text style={[styles.bigBlue, styles.red]}>bigBlue, then red</Text>
<Text style={[styles.red, styles.bigBlue]}>red, then bigBlue</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
marginTop: 50,
},
bigBlue: {
color: 'blue',
fontWeight: 'bold',
fontSize: 30,
},
red: {
color: 'red',
},
});
export default LotsOfStyles;
React Native 中的尺寸都是无单位的,表示的是与设备像素密度无关的逻辑像素点,在不同尺寸的屏幕上都显示成一样的大小;百分比宽高要求父容器有一个明确的尺寸。
Image标签:src属性改为source,不接受字符串,是一个带有uri属性的对象
<Image source={{uri: 'https://facebook.github.io/react/logo-og.png'}}
style={{width: 400, height: 400}} />
URI 是统一资源标识符,URL 是统一资源定位符。URL 是 URI 的子集,所有 URL 都是 URI,但不是所有 URI 都是 URL。
URI = 身份证号(标识你是谁) URL = 家庭住址(告诉你去哪找你)
需要手动指定图片的尺寸、需要使用 https 以满足 iOS App Transport Security 的要求
在浏览器中,不给图片指定尺寸,浏览器会先渲染一个 0x0 大小的元素占位,然后下载图片,在下载完成后再基于正确的尺寸来渲染图片(抖动)。
在React Native中我们有意避免了这一行为。开发者需要做更多工作来提前知晓远程图片的尺寸(或宽高比),读取本地静态图片(使用require('./my-icon.png')语法)则无需指定尺寸,因为它们的尺寸在加载时就可以立刻知道。比如
require('./my-icon.png')的实际输出结果可能是:
{"__packager_asset":true,"uri":"my-icon.png","width":591,"height":573}
Hooks:是 React 16.8 引入的新特性,让你在函数组件中使用状态和生命周期等 React 特性
1.为什么需要hooks
// ❌ 类组件- 以前的方式
class Counter extends React.Component {
state = { count: 0 };
componentDidMount() {
document.title = `点击了 ${this.state.count} 次`;
}
render() {
return (
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
点击次数: {this.state.count}
</button>
);
}
}
// ✅ 函数组件 + Hooks - 现在的方式
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `点击了 ${count} 次`;
}, [count]);
return (
<button onClick={() => setCount(count + 1)}>
点击次数: {count}
</button>
);
};
2.hooks解决的问题
问题 说明 类组件复杂 this 指向混乱、生命周期方法分散 逻辑复用难 以前需要高阶组件、render props 代码冗余 相关逻辑被迫拆分到不同生命周期 难以理解 类组件的学习曲线较陡 3.常见的hooks
基础
Hook 作用 示例 useState 状态管理 const [count, setCount] = useState(0)useEffect 副作用处理 useEffect(() => { ... }, [])useContext 跨组件共享状态 const value = useContext(MyContext)额外
Hook 作用 useRef 引用 DOM 或保存值 useMemo 缓存计算结果 useCallback 缓存函数引用 useReducer 复杂状态管理 useLayoutEffect DOM 更新后同步执行 1.useState - 状态管理
const [count, setCount] = useState(0);
setCount(1); // 直接设置
2. useEffect - 副作用处理
// 挂载时执行一次
useEffect(() => {
fetchData();
}, []);
// 依赖变化时执行
useEffect(() => {
fetchUser(userId);
}, [userId]);
3. useRef - 引用
// 引用 DOM
const inputRef = useRef<HTMLInputElement>(null);
<input ref={inputRef} />
inputRef.current?.focus();
// 保存值(不触发渲染)
const countRef = useRef(0);
countRef.current = countRef.current + 1;
4.useMemo - 缓存计算结果
/ 仅当依赖变化时重新计算
const expensiveValue = useMemo(() => { return computeExpensiveValue(a, b); }, [a, b]);
5.useCallback - 缓存函数// 仅当依赖变化时重新创建函数 const handleClick = useCallback(() => { doSomething(a, b); }, [a, b]);
fetch: React Native 提供了和 web 标准一致的Fetch API,用于满足开发者访问网络的需求, Chrome 调试目前无法观测到 React Native 中的网络请求,可使用第三方的react-native-debugger来进行观测。默认情况下,iOS 会阻止所有 http 的请求, Android9 开始,也会默认阻止 http 请求以督促开发者使用 https。
fetch('https://mywebsite.com/mydata.json');
// Fetch 还有可选的第二个参数,可以用来定制 HTTP 请求一些参数
fetch('https://mywebsite.com/endpoint/', {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json', // 数据的格式(取决于服务器端)
},
body: JSON.stringify({
firstParam: 'yourValue',
secondParam: 'yourOtherValue',
}),
});