目前使用AI开发,生成代码比较成熟的全栈组合前端是reactjs,所以这里整理一些reactjs 基础知识,方便生成reactjs 的时候,可以快速修改里面的代码。
本文档结合 React 官方文档编写,覆盖你日常开发中 80% 会用到的 React 概念。
你将学会
- 如何创建和嵌套组件
- 如何添加标记和样式
- 如何显示数据
- 如何渲染条件判断和列表
- 如何响应事件和更新界面
- 如何在组件间共享数据
- React 核心原理与最佳实践
一、React 渲染原理
虚拟 DOM 的创建
React 使用 React.createElement 创建虚拟 DOM,返回值是 React 元素(虚拟 DOM):
javascript
const element = React.createElement(
'div', // DOM 类型
{ // DOM 属性
style: {color: 'red'},
className: 'container'
},
'hello', // 子节点
React.createElement("span", {style: {color: 'blue'}}, 'world')
);
JSX 语法
JSX 是 JavaScript 的语法扩展,最终会被 Babel 编译成 React.createElement 调用:
javascript
let jsxElement = (
<div style={{color:'red'}} className='container'>
hello
<span style={{color:'blue'}}>world</span>
</div>
)
渲染方式
React 18 使用 createRoot API:
javascript
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
二、创建和嵌套组件
React 应用由组件构成。组件是 UI 的一部分,拥有自己的逻辑和外观。React 组件是返回标记的 JavaScript 函数。
函数组件(推荐)
函数组件是一个返回虚拟 DOM 的函数:
javascript
function MyButton() {
return (
<button>我是一个按钮</button>
);
}
// 在其他组件中使用
export default function MyApp() {
return (
<div>
<h1>欢迎来到我的应用</h1>
<MyButton />
</div>
);
}
注意:React 组件名必须以大写字母开头,HTML 标签必须小写。
类组件(了解即可)
类组件继承自 React.Component,必须实现 render 方法:
javascript
class ClassComponent extends React.Component {
constructor(props) {
super(props); // 调用父类构造函数,this.props = props
}
render() {
return <div>hello {this.props.name}</div>;
}
}
现代 React 开推荐使用函数组件 + Hooks,类组件主要用于维护老项目。
三、JSX 语法
JSX 是 JavaScript 的扩展语法,让在 JS 中写 HTML 变得更容易。
JSX 规则
javascript
// 1. 必须关闭所有标签
<br />
// 2. 必须有一个父元素包裹
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}
// 3. 使用 className 而不是 class
<img className="avatar" />
在 JSX 中使用 JavaScript
使用花括号 {} 可以在 JSX 中嵌入 JavaScript 表达式:
javascript
const user = { name: 'Hedy Lamarr', imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg', imageSize: 90 };
export default function Profile() {
return (
<>
<h1>{user.name}</h1>
<img
className="avatar"
src={user.imageUrl}
alt={'Photo of ' + user.name}
style={{
width: user.imageSize,
height: user.imageSize
}}
/>
</>
);
}
提示 :
style={{}}不是特殊语法,而是 JSX 花括号内的一个普通对象。
四、添加样式
使用 className
javascript
<img className="avatar" />
css
/* CSS 文件 */
.avatar {
border-radius: 50%;
}
使用内联样式
javascript
<div style={{ color: 'red', fontSize: '16px' }}>
红色文字
</div>
五、条件渲染
React 没有特殊的条件语法,使用普通的 JavaScript 条件语句。
方式一:if 语句
javascript
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return <div>{content}</div>;
方式二:三元运算符(推荐)
javascript
<div>
{isLoggedIn ? <AdminPanel /> : <LoginForm />}
</div>
方式三:逻辑与 &&(无 else 分支时)
javascript
<div>
{isLoggedIn && <AdminPanel />}
</div>
六、列表渲染
使用 JavaScript 的 map() 函数渲染列表:
javascript
const products = [
{ title: '卷心菜', isFruit: false, id: 1 },
{ title: '大蒜', isFruit: false, id: 2 },
{ title: '苹果', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return <ul>{listItems}</ul>;
}
重要 :每个列表项都需要唯一的
key属性,帮助 React 识别哪些项目发生了变化。
七、响应事件
在组件内部声明事件处理函数来响应事件:
javascript
function MyButton() {
function handleClick() {
alert('你点击了我!');
}
return (
<button onClick={handleClick}>
点击我
</button>
);
}
注意 :
onClick={handleClick}末尾没有括号!只传递函数引用,而不是调用它。React 会在用户点击时调用你的事件处理函数。
事件绑定方式
- 冒泡阶段 :
onClick - 捕获阶段 :
onClickCapture
事件方法
event.stopPropagation()- 阻止事件传播event.preventDefault()- 阻止默认行为(如 a 标签跳转)
javascript
class EventDemo extends React.Component {
childBubble(event) {
console.log('子节点冒泡');
event.stopPropagation();
}
clickLink = (event) => {
event.preventDefault();
}
}
事件执行顺序
React 事件系统和原生事件系统是独立的,执行顺序:
- React 捕获阶段
- React 冒泡阶段
- 原生捕获阶段
- 原生冒泡阶段
八、状态管理
组件使用**状态(State)**来"记忆"信息并显示它。
类组件状态
初始化状态
javascript
// 方式1:在构造函数中
constructor(props) {
super(props);
this.state = { number: 0 };
}
// 方式2:类属性(推荐)
state = { number: 0 };
setState 更新状态
javascript
// 对象方式
this.setState({ number: this.state.number + 1 });
// 函数方式(推荐,可基于旧状态计算新状态)
this.setState((prevState) => ({ number: prevState.number + 1 }));
// 带回调的 setState
this.setState(
(prevState) => ({ number: prevState.number + 1 }),
() => {
console.log("State updated!", this.state.number);
}
);
useState Hook
在函数组件中添加状态:
javascript
import { useState } from 'react';
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
点击了 {count} 次
</button>
);
}
count:当前状态值setCount:更新状态的函数useState(0):初始值为 0
函数式更新(推荐)
当新状态依赖于旧状态时,使用函数式更新:
javascript
function handleClick() {
setCount(prevCount => prevCount + 1);
}
setState 的异步与批量更新
React 17
- 异步批量更新 :在事件处理器、生命周期函数中,多个
setState会被合并 - 同步更新:在 setTimeout、Promise 等无法管理的场景中是同步的
javascript
handleClick = () => {
this.setState({ number: this.state.number + 1 }); // 批量合并
console.log(this.state); // 未更新
this.setState({ number: this.state.number + 1 }); // 批量合并
console.log(this.state); // 未更新
setTimeout(() => {
this.setState({ number: this.state.number + 1 }); // 同步更新
console.log(this.state); // 已更新
});
}
React 18
无论在什么场景,setState 都是批量的
九、Hooks 规则
- 只在顶层调用:不要在循环、条件或嵌套函数中调用 Hooks
- 只在 React 函数中调用:在 React 函数组件或自定义 Hook 中调用
javascript
// ✅ 正确
function MyComponent() {
const [count, setCount] = useState(0);
// ...
}
// ❌ 错误
function MyComponent() {
if (condition) {
const [count, setCount] = useState(0); // 不要这样做
}
}
十、组件间共享数据
问题:各自独立的状态
javascript
// 每个按钮有自己独立的 count
function MyApp() {
return (
<div>
<MyButton />
<MyButton />
</div>
);
}
解决:提升状态
将状态提升到最近的共同父组件,通过 props 传递:
javascript
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>计数器一起更新</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
点击了 {count} 次
</button>
);
}
这就是"提升状态(Lifting State Up)"的概念。
十一、常用 Hooks 详解
useState - 状态管理
javascript
const [count, setCount] = useState(0);
// 函数式更新
setCount(prevCount => prevCount + 1);
useReducer - 复杂状态管理
复杂状态管理,类似 Redux:
javascript
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'ADD':
return { count: state.count + 1 };
case 'MINUS':
return { count: state.count - 1 };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, initialState);
dispatch({ type: 'ADD' });
useEffect - 副作用处理
处理副作用,替代类组件的生命周期:
javascript
useEffect(() => {
// 相当于 componentDidMount
const timer = setInterval(() => {
setCount(count => count + 1);
}, 1000);
// 返回清理函数,相当于 componentWillUnmount
return () => {
clearInterval(timer);
};
}, []); // 空依赖数组 = 只执行一次
// 带依赖的 effect
useEffect(() => {
// 当 count 变化时执行
console.log('Count changed:', count);
}, [count]);
useContext - 跨组件共享数据
javascript
const MyContext = createContext('defaultValue');
// 提供数据
<MyContext.Provider value="hello">
<Child />
</MyContext.Provider>
// 消费数据
function Child() {
const value = useContext(MyContext);
return <div>{value}</div>;
}
useMemo - 缓存计算结果
javascript
const displayData = useMemo(() => ({ count }), [count]);
useCallback - 缓存回调函数
javascript
const incrementCount = useCallback(() => {
setCount(count => count + 1);
}, [count]);
useRef - 引用 DOM
在函数组件中创建 ref:
javascript
const inputRef = useRef(null); // {current: null}
function focusInput() {
inputRef.current.focus();
}
<input ref={inputRef} />
useImperativeHandle - 自定义 ref 暴露
自定义 ref 暴露给父组件的实例值:
javascript
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus() {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
const ForwardFancyInput = forwardRef(FancyInput);
useLayoutEffect - 同步副作用
同步执行副作用,在 DOM 变更后同步调用(阻塞浏览器绘制):
javascript
useLayoutEffect(() => {
// 在浏览器绘制之前执行
ref.current.style.transform = `translate(500px)`;
}, []);
十二、Ref 引用
createRef(类组件)
用于访问 DOM 元素或类组件实例:
javascript
class RefComponent extends React.Component {
inputRef = React.createRef(); // {current: null}
handleButtonClick = () => {
this.inputRef.current.focus(); // 访问真实 DOM
}
render() {
return (
<div>
<input ref={this.inputRef} type="text" />
<button onClick={this.handleButtonClick}>获得焦点</button>
</div>
);
}
}
访问类组件实例
javascript
class ParentComponent extends React.Component {
childRef = React.createRef();
handleClick = () => {
this.childRef.current.alertMessage(); // 调用子组件方法
}
render() {
return (
<div>
<ChildComponent ref={this.childRef}/>
</div>
);
}
}
forwardRef(转发 ref)
函数组件默认不能接收 ref,需要使用 forwardRef:
javascript
function ChildComponent(props, forwardRef) {
return <input ref={forwardRef}/>;
}
const ForwardChildComponent = React.forwardRef(ChildComponent);
十三、Context 上下文
用于跨组件层级共享数据,避免层层传递 props
创建 Context
javascript
const MyContext = React.createContext('defaultValue');
const { Provider, Consumer } = MyContext;
使用 Context
Provider 提供数据
javascript
<MyContext.Provider value={contextValue}>
<ChildComponent/>
</MyContext.Provider>
Consumer 消费数据(函数组件)
javascript
<MyContext.Consumer>
{(value) => <div>{value}</div>}
</MyContext.Consumer>
类组件使用 contextType
javascript
class MyClassComponent extends React.Component {
static contextType = MyContext;
render() {
return <div>{this.context}</div>;
}
}
多个 Context
javascript
<BorderProvider value={borderValue}>
<ColorProvider value={colorValue}>
<ChildComponent />
</ColorProvider>
</BorderProvider>
Context 嵌套规则
Consumer 会读取最近的 Provider 的值
十四、性能优化
shouldComponentUpdate
通过返回布尔值控制组件是否更新:
javascript
shouldComponentUpdate(nextProps, nextState) {
return nextState.number % 2 === 0; // 偶数才更新
}
PureComponent
自动进行浅比较,避免不必要的渲染:
javascript
class PureComp extends React.PureComponent {
render() {
return <div>{this.props.value}</div>;
}
}
React.memo
用于函数组件的记忆化:
javascript
const MemoCounter = React.memo(Counter);
// 自定义比较函数
const MemoComp = React.memo(Comp, (prevProps, nextProps) => {
return prevProps.value === nextProps.value;
});
性能优化最佳实践
javascript
function App() {
const [username, setUsername] = useState('zhufeng');
const [count, setCount] = useState(0);
// 缓存对象
const displayData = useMemo(() => ({ count }), [count]);
// 缓存回调函数
const incrementCount = useCallback(() => {
setCount(count => count + 1);
}, []);
return (
<div>
<input value={username} onChange={e => setUsername(e.target.value)} />
<MemoChildButton displayData={displayData} incrementCount={incrementCount} />
</div>
);
}
十五、生命周期(类组件)
挂载阶段 (Mounting)
- constructor - 初始化状态
- componentWillMount (已废弃) - 组件将要挂载
- render - 计算虚拟 DOM(纯函数,不能有副作用)
- componentDidMount - 挂载完成,可发起网络请求
更新阶段 (Updating)
- componentWillReceiveProps - 收到新属性(已废弃)
- getDerivedStateFromProps - 从属性派生状态(静态方法)
- shouldComponentUpdate - 决定是否更新(性能优化关键点)
- getSnapshotBeforeUpdate - 获取 DOM 更新前的快照
- componentWillUpdate - 将要更新(已废弃)
- render - 重新计算虚拟 DOM
- componentDidUpdate - 更新完成
卸载阶段 (Unmounting)
- componentWillUnmount - 清理操作(清除定时器、取消网络请求)
新生命周期方法示例
getDerivedStateFromProps
从 props 派生 state:
javascript
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.itemCount !== prevState.itemCount) {
return { itemCount: nextProps.itemCount };
}
return null; // 不更新状态
}
getSnapshotBeforeUpdate
获取 DOM 更新前的快照:
javascript
getSnapshotBeforeUpdate(prevProps, prevState) {
const list = this.listRef.current;
return list.scrollHeight; // 返回更新前的高度
}
componentDidUpdate(prevProps, prevState, snapshot) {
const list = this.listRef.current;
// 使用快照计算新的滚动位置
list.scrollTop = list.scrollTop + (list.scrollHeight - snapshot);
}
十六、重要概念
合成事件 (SyntheticEvent)
React 实现的事件系统,与原生事件隔离,跨浏览器兼容。
批量更新 (Batching)
React 会将多个状态更新合并为一次渲染,提高性能。
受控组件与非受控组件
- 受控组件:表单元素的值由 React state 控制
- 非受控组件:表单元素的值由 DOM 自身控制
组件通信方式
- Props - 父子组件通信
- Callback - 子父组件通信
- Context - 跨层级通信
- Redux/Zustand - 全局状态管理
- Event Bus - 兄弟组件通信
- Ref - 直接访问子组件实例
学习路径建议
第一步:掌握基础
- 组件与 JSX
- Props 传递
- 事件处理
- useState Hook
- 条件渲染和列表
第二步:深入理解
- useEffect 副作用处理
- 组件间通信
- Context 使用
- useRef 引用
第三步:进阶技能
- 性能优化
- 自定义 Hooks
- 状态管理(useReducer)
- 表单处理
第四步:实战项目
- React Router 路由
- Redux Toolkit / Zustand
- React Query 数据请求
- UI 组件库(Ant Design / Material-UI)