文章目录
- 引子
- React Native适用场景
- React基础
- JSX
- 组件的定义
- 基础API
- Flex弹性布局
- 例子: Flex布局实现多行多列
- 常用UI组件
- 几个核心钩子函数
- useState用法
- useEffect典型用法
- 和原生模块交互
- 调用原生模块方法
- 调试
- 其它工具
- UI框架
- 参考资源
引子
软件开发,移动优先;移动开发,RN优先。为什么?RN的性能胜任大部分应用场景,开发效率奇高(前提当然是熟悉web和javascript),热更新,快速见效,出活快,画一个界面三言两语,比原生简洁多了。Flutter? Xamarin? NativeScript? Ionic? Cordova? 我还是选RN。有人担心RN不流行了,会被替代,大可不必,哪种技术能长命百岁?一个产品有三五年的周期就算长寿了。RN短期内死不了。新的JS引擎 Hermes也极大地改进了性能。
关键词:JSX、Flex布局、UI组件
React Native适用场景
大部分应用都可以采用RN,新闻资讯类自不必说,电商类,教育类,视频播放类,也不在话下。CPU密集型?游戏类?这个我没有尝试过。但就像Java和C++/C的关系一样,80~90%都可以由Java搞掂,剩下的C来搞掂。你能吃下90%的业务部分,你还担心什么?
React基础
JSX
JSX 是 React 的核心组成部分,它使用 XML 标记的方式去直接声明界面。JSX貌似丑陋,和JS代码耦合,但其背后的哲学是每个HTML都是由有生命的片段组合而成,有了对片段的良好编程,就能由简到繁,积小成大,去构建更庞大的应用。
组件的定义
组件有两种写法:类组件和函数式组件。
类的写法:
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
函数写法:
function Welcome(props) {
const data = [
{ id: 1, name: "John Doe" },
{ id: 2, name: "Victor Wayne" },
{ id: 3, name: "Jane Doe" },
];
return (
<View>
<Text>Hello, {props.name}</Text>
<View className="users">
{data.map((user) => (
<Text className="user">{user}</Text>
))}
</View>
</View>
);
}
函数组件就是可以返回一个ReactElement的函数。
基础API
ReactNode是一个联合类型:
type ReactNode =
| ReactChild
| ReactFragment
| ReactPortal
| boolean
| null
| undefined;
type ReactText = string | number;
type ReactChild = ReactElement | ReactText;
Flex弹性布局
定理:采用Flex加上嵌套能实现任意布局
-
react 宽度基于pt为单位, 可以通过Dimensions 来获取宽高,PixelRatio 获取密度。
-
基于flex的布局
- view默认宽度为100%
- 水平居中用alignItems, 垂直居中用justifyContent
- 基于flex能够实现现有的网格系统需求,且网格能够各种嵌套无bug
-
图片布局
- 通过Image.resizeMode来适配图片布局,包括contain, cover, stretch
- 默认不设置模式等于cover模式
- contain模式自适应宽高,给出高度值即可
- cover铺满容器,但是会做截取
- stretch铺满容器,拉伸
-
定位
- 定位相对于父元素,父元素不用设置position也行
- padding 设置在Text元素上的时候会存在bug。所有padding变成了marginBottom
-
文本元素
- 文字必须放在Text元素里边
- Text元素可以相互嵌套,且存在样式继承关系
- numberOfLines 需要放在最外层的Text元素上,且虽然截取了文字但是还是会占用空间
例子: Flex布局实现多行多列
容器: flex-wrap: wrap; 允许折行
元素有三种方式:
- flex: 1 0 33.3%;
- flex-basis: 33.3%;
- width: 33.3%;
常用UI组件
View是最核心、最常用的UI组件。
VirtualizedList是FlatList和SectionList的底层实现。FlatList和SectionList都依赖一个数组,每个元素是个字典,SectionList的字典必须有data、title、key。
- 不要在ScrollView里使用FlatList
<View style={{flex: 1}} >
<ScollView style={{flexGrow: 1}}
nestedScrollEnabled={true}>
<View>
<SafeAreaView>
<ScrollView horizontal
style={{width: '100%', height: 'your_height'}}>
{data.map((item, index) => (
<View key={index}>
// Your component
</View>
))}
</ScrollView> -- Horizontal Scroll --
</SafeAreaView>
</View>
</ScrollView>
</View>
几个核心钩子函数
Hooks 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。Hooks 是一种在函数式组件中使用有状态函数的方法。Hooks不支持在class中使用,比如在class中使用useState和useEffect都是不允许的。
- useEffect()函数:作用就是指定一个副效应函数,组件每渲染一次,该函数就自动执行一次。组件首次在网页 DOM 加载后,副效应函数也会执行。它的第二个参数,使用一个数组指定副效应函数的依赖项,只有依赖项发生变化,才会重新渲染。
- useState()函数:用于为函数组件引入状态。
- useContext():用于在组件之间共享状态。
- useReducer():React 本身不提供状态管理功能,通常需要使用外部库。这方面最常用的库是 Redux。Redux 的核心概念是,组件发出 action 与状态管理器通信。状态管理器收到 action 以后,使用 Reducer 函数算出新的状态,Reducer 函数的形式是(state, action) => newState。
Hook函数 | 说明 |
---|---|
useState | [<取值>, <设值>] = useState(<初始值>) |
useEffect | 在组件加载和监听的对象值发生变化时调用 |
useContext | |
useLayoutEffect | |
useReducer | |
useCallback | |
useMemo | |
useRef | 返回一个mutable ref对象,用.current获得其值 |
useTransition | |
useDeferredValue | |
useId |
useState用法
- useState的更新是异步的,数据不会立即刷新的。其背后是队列实现的。
- 仅顶层调用,不能在循环,条件,嵌套函数等中调用useState()。
useEffect典型用法
const [data, setData] = useState()
useEffect(() => {
// declare the async data fetching function
const fetchData = async () => {
// get the data from the api
const data = await fetch(`https://yourapi.com?param=${param}`);
// convert the data to json
const json = await response.json();
// set state with the result
setData(json);
}
// call the function
fetchData()
// make sure to catch any error
.catch(console.error);;
}, [param])
再举一个例子:
import { useState, useEffect } from 'react';
const HackerNewsStories = () => {
const [stories, setStories] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchStories = async () => {
try {
const data = await (await fetch('https://hn.algolia.com/api/v1/search_by_date?tags=front_page&hitsPerPage=20')).json();
setStories(
data.hits.sort((story, nextStory) => (story.points < nextStory.points ? 1 : -1))
);
setError(null);
} catch (err) {
setError(err.message);
setStories(null);
} finally {
setLoading(false);
}
};
fetchStories();
}, []);
return (
<div className="wrapper">
<h2>Latest HN Stories</h2>
{loading && <div>HackerNews frontpage stories loading...</div>}
{error && <div>{`Problem fetching the HackeNews Stories - ${error}`}</div>}
<div className="stories-wrapper">
{stories &&
stories.map(({ objectID, url, title, author, points }) => (
title && url &&
<div className='stories-list' key={objectID}>
<h3><a href={url} target="_blank" rel="noreferrer">{title}</a> - By <b>{author}</b> ({points} points)</h3>
</div>
))}
</div>
</div>
);
};
export default HackerNewsStories;
和原生模块交互
在使用 React 做开发时有两种写法:
类组件
函数组件 + Hook (用的这种)
React 组件之间共享数据的方案:
使用 React 自带的 Context + Reducer 功能 (用的这种)
优点:无须引入其他的包
缺点:只能向子组件及子孙组件中共享数据
使用 Redux 实际组件之间的共享
优点:数据全都放到 Redux 中管理,无论什么层级都直接使用
缺点:需要单独安装,数据状态由它统一管理,很多代码写法不太一样
RN 中的布局
View 组件默认是相对定位的(可以直接使用 left 、top 相对于原来的位置定位)
View 组件的绝对定位都是相对于父组件定位的(因为父组件都是相对定位,所以默认都是子绝父相)
没有浮动,只能 flex
父组件一定要设置高度或者 flex:1 ,否则高度为0页面空白
调用原生模块方法
要实现一个自己的ReactPackage和ReactModule,在ReactModule中暴露js方法。
调试
调试方面强烈推荐使用 React Native Debugger,一个基于 React Native 官方调试方式、包含 React Inspector / Redux DevTools 独立应用:
- 基于官方的 Remote Debugger 且提供了更为丰富的功能
- 包含 react-devtools-core 的 React Inspector
- 包含 Redux DevTools, 且与 redux-devtools-extension 保持 API 一致
其它工具
- Watchman, Watchman 在更改时观察文件和记录,然后触发相应的操作,并由 React Native 在内部使用
UI框架
- react-native-elements
- Ant Design Mobile RN
参考资源
- https://github.com/jondot/awesome-react-native
- https://github.com/reactnativecn/react-native-guide
- Expo官网
- https://taro.zone/
- https://nervjs.github.io/taro-docs/docs/GETTING-STARTED
- 下载 Genymotion免费版
- 多终端开发
- https://oblador.github.io/react-native-vector-icons/