2、React基础回顾:JSX语法、组件化思想、Props与State、生命周期与Hooks入门
好,咱们正式开始React移动端适配之前,我得先带大家把React的基础再过一遍。你可能会想:「这些我都知道啊,干嘛还要讲?」
嗯,我理解。但说实话,移动端开发里很多坑,恰恰就出在这些基础概念上。比如JSX里一个不小心写错了条件渲染,整个页面白屏;再比如State更新不及时,导致动画卡顿。这些我都踩过。
所以这一章,咱们不搞花架子,就扎扎实实把JSX、组件、Props、State、生命周期和Hooks这几个核心概念捋一遍。我会结合移动端的实际场景来讲,保证你听完有收获。
2.1 JSX语法:不只是HTML的「糖」
JSX,说白了就是JavaScript的语法扩展。它让你能在JS里写类似HTML的代码。我第一次看到它时,心里想:「这不就是模板引擎吗?」后来才发现,完全不是一回事。
核心要点:JSX最终会被Babel编译成React.createElement()调用。所以它本质上是JS,不是HTML。
来看个移动端常见的例子——条件渲染一个加载状态:
function LoadingScreen({ isLoading, children }) {
return (
<div className="container">
{isLoading ? (
<div className="spinner">
<span>加载中...</span>
</div>
) : (
children
)}
</div>
);
}
这里有个坑,我当年在项目里遇到过:JSX里的注释要用{/* */},而不是HTML的。否则页面直接报错,找半天都找不到原因。
我的习惯:在JSX里写多行表达式时,一定要用括号包起来。比如上面那个三元表达式,不加括号的话,代码格式化后容易出问题。
另外,移动端开发中,JSX里经常要处理样式。记住:class要写成className,style要写成对象形式:
// 正确写法
<div className="header" style={{ fontSize: '16px', color: '#333' }}>
标题
</div>
2.2 组件化思想:搭积木的艺术
React的核心思想就是组件化。你想想看,一个移动端页面,从上到下可以分为:状态栏、导航栏、内容区、底部Tab。每一块都是一个独立的组件。
我建议你把组件分为两类:
- 展示组件(Presentational):只负责UI呈现,不关心数据来源。比如一个按钮、一个卡片。
- 容器组件(Container):负责数据获取和状态管理。比如一个用户列表页。
为什么要这么分?我在做一个电商App时,一开始把所有逻辑都写在一个组件里,结果改一个需求要改三处代码,差点崩溃。后来拆成容器+展示,维护成本直接降了一半。
注意:移动端组件要尽量「小」。一个组件超过200行代码,就该考虑拆分了。否则后面改起来,你连滚动条都不想拖。
2.3 Props与State:数据流动的两条腿
Props和State,是React数据管理的基石。我经常跟团队新人说:Props是父组件给你的「遗产」,State是你自己的「私房钱」。
| 特性 | Props | State |
|---|---|---|
| 来源 | 父组件传递 | 组件内部定义 |
| 可变性 | 不可变(只读) | 可变(通过setState) |
| 用途 | 配置组件 | 管理组件内部数据 |
| 更新触发 | 父组件重新渲染 | 调用setState或useState的更新函数 |
举个移动端的例子——一个可滑动的轮播图组件:
function Carousel({ images, autoPlay }) {
// images 和 autoPlay 是 Props,由父组件传入
const [currentIndex, setCurrentIndex] = useState(0);
// currentIndex 是 State,组件自己管理
useEffect(() => {
if (autoPlay) {
const timer = setInterval(() => {
setCurrentIndex(prev => (prev + 1) % images.length);
}, 3000);
return () => clearInterval(timer);
}
}, [autoPlay, images.length]);
return (
<div className="carousel">
<img src={images[currentIndex]} alt={`slide-${currentIndex}`} />
</div>
);
}
这里有个细节:State的更新是异步的。我曾经在移动端做下拉刷新时,直接写了setCount(count + 1),结果连续快速下拉时,计数总是少一次。后来改成setCount(prev => prev + 1)才解决。
避坑指南:如果你要基于当前State计算新值,一定要用函数式更新。这是React官方推荐的写法,也是我踩坑后养成的习惯。
2.4 生命周期与Hooks入门
类组件的生命周期,说复杂也复杂,说简单也简单。无非就是:挂载 → 更新 → 卸载三个阶段。
但说实话,自从Hooks出现后,我几乎不再用类组件了。为什么?因为Hooks让逻辑复用变得极其简单。
来看几个最常用的Hooks:
useState:管理组件状态
const [count, setCount] = useState(0);
这个不用多说,上面已经用过好几次了。
useEffect:处理副作用
useEffect可以看作是类组件中componentDidMount、componentDidUpdate、componentWillUnmount的合体。我一般用它来做三件事:
- 数据请求(API调用)
- 订阅/取消订阅(比如WebSocket)
- 手动修改DOM(比如操作canvas)
useEffect(() => {
// 组件挂载时执行
fetchData();
// 返回的清理函数,组件卸载时执行
return () => {
console.log('组件卸载了,清理工作');
};
}, []); // 空数组表示只在挂载和卸载时执行
注意:useEffect的依赖数组一定要写全。我见过太多因为漏掉依赖导致bug的案例。如果你不确定,可以用ESLint的react-hooks/exhaustive-deps规则来检查。
useRef:保存可变引用
这个在移动端特别有用。比如你要获取一个DOM元素的尺寸,或者保存一个定时器的ID:
function AutoScrollList() {
const listRef = useRef(null);
useEffect(() => {
// 滚动到底部
listRef.current.scrollTop = listRef.current.scrollHeight;
}, []);
return <div ref={listRef} className="list">...</div>;
}
自定义Hooks:逻辑复用的终极武器
我建议你养成写自定义Hooks的习惯。比如,移动端经常要监听网络状态,我可以封装一个useNetworkStatus:
function useNetworkStatus() {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
// 使用
function App() {
const isOnline = useNetworkStatus();
return <div>{isOnline ? '在线' : '离线'}</div>;
}
你看,这样写出来,逻辑清晰,复用性强。我在多个项目里都用过这个Hook,每次改需求只需要改一处。
总结一下:React基础其实不难,难的是在移动端场景下灵活运用。JSX要注意语法细节,组件要拆得够小,Props和State要分清楚,Hooks要善用。把这些基础打牢了,后面讲适配方案时你才能跟得上。
好,这一章就到这里。下一章我们开始真正进入移动端适配的世界——聊聊rem、vw、px这些单位到底怎么选。