React
一、JSX 语法
1. JSX 是什么?
问题:JSX 是什么?
答案: 核心回答:JSX 是 JavaScript 的语法扩展,允许在 JavaScript 中编写类似 HTML 的标记。
代码示例:
// JSX 元素
const element = <h1 className="title">Hello, World!</h1>;
// 表达式嵌入
const name = '张三';
const element = <p>你好,{name}!</p>;
// 属性
const element = <img src={user.avatarUrl} alt={user.name} />;
// 条件渲染
const element = isLoggedIn ? <UserGreeting /> : <GuestGreeting />;
// 列表渲染
const list = users.map(user => (
<li key={user.id}>{user.name}</li>
));
二、React Hooks
2. useState
问题:useState
答案: 核心回答:useState 是 React Hook,用于在函数组件中添加状态。
代码示例:
import { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>计数: {count}</p>
<button onClick={() => setCount(count + 1)}>
增加
</button>
<button onClick={() => setCount(prev => prev + 1)}>
函数式更新
</button>
</div>
);
}
// 多个状态
const [name, setName] = useState('');
const [age, setAge] = useState(0);
// 对象状态
const [form, setForm] = useState({ name: '', email: '' });
setForm(prev => ({ ...prev, name: 'new name' }));
3. useEffect
问题:useEffect
答案: 核心回答:useEffect 处理副作用,类似于 componentDidMount/componentDidUpdate/componentWillUnmount。
代码示例:
import { useEffect } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// 组件挂载后执行
fetchUser(userId).then(setUser);
// 清理函数
return () => {
console.log('组件卸载或更新前清理');
};
}, [userId]); // 依赖数组
// 空依赖 - 只执行一次
useEffect(() => {
document.title = '加载中...';
}, []);
// 无依赖 - 每次渲染都执行
useEffect(() => {
console.log('每次渲染都执行');
});
}
4. useCallback 与 useMemo
问题:useCallback 与 useMemo 的区别
答案: 核心回答:useCallback 缓存函数,useMemo 缓存计算结果。
代码示例:
import { useCallback, useMemo } from 'react';
// useCallback - 缓存函数
const handleClick = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// useMemo - 缓存计算结果
const expensiveValue = useMemo(() => {
return computeExpensiveValue(a, b);
}, [a, b]);
// 避免不必要的重渲染
function Parent() {
const [count, setCount] = useState(0);
// 不使用 useCallback,每次渲染都会创建新函数
const handleClick = () => console.log('click');
// 使用 useCallback,只有 count 变化时才创建新函数
const handleClick = useCallback(() => {
console.log(count);
}, [count]);
return <Child onClick={handleClick} />;
}
三、React 原理
5. Virtual DOM
问题:Virtual DOM
答案: 核心回答:虚拟 DOM 是真实 DOM 的 JavaScript 对象表示,通过 diff 算法最小化更新。
代码示例:
// 虚拟 DOM 节点
const vdom = {
type: 'div',
props: { className: 'container' },
children: [
{
type: 'h1',
props: {},
children: 'Hello'
}
]
};
// React.createElement
const element = React.createElement(
'div',
{ className: 'container' },
React.createElement('h1', null, 'Hello')
);
// JSX 转换
// <div className="container"><h1>Hello</h1></div>
// 会被编译为 createElement 调用
6. React Diff 算法
问题:React Diff 算法
答案: 核心回答:React 通过 tree diff、component diff、element diff 三层比较实现高效更新。
代码示例:
// tree diff - 同层比较
// <ul>
// <li>A</li>
// <li>B</li>
// </ul>
// 变为
// <ul>
// <li>C</li>
// <li>B</li>
// </ul>
// 只会比较 li,而不是整个 ul
// component diff - 类型比较
// 同类型组件继续比较 props
// 不同类型直接替换
// element diff - key 的作用
// 无 key:A-B-C -> A-B-C(每个都更新)
// 有 key:A-B-C -> C-A-B(只移动 C)
// <li key="1">A</li>
// <li key="2">B</li>
// <li key="3">C</li>
7. Fiber 架构
问题:Fiber 架构
答案: 核心回答:Fiber 将渲染工作拆分成小单元,实现可中断、可恢复的渲染。
代码示例:
// Fiber 节点结构
const fiber = {
type: 'div',
key: null,
stateNode: /* DOM 节点 */,
child: /* 子 fiber */,
sibling: /* 兄弟 fiber */,
return: /* 父 fiber */,
alternate: /* workInProgress */,
effectTag: 'UPDATE', // PLACEMENT, DELETION, UPDATE
};
四、State 与生命周期
8. State 的使用
问题:State 的使用
答案: 核心回答:state 是组件私有数据,修改必须通过 setState。
代码示例:
class Counter extends React.Component {
state = {
count: 0,
message: 'Hello'
};
handleClick = () => {
// 异步更新,可能被合并
this.setState({ count: this.state.count + 1 });
// 函数式更新,避免依赖当前 state
this.setState(prevState => ({
count: prevState.count + 1
}));
// setState 回调
this.setState({ count: 1 }, () => {
console.log('状态已更新');
});
};
}
五、组件与 Props
9. Props
问题:Props 的使用
答案: 核心回答:Props 是父组件传递给子组件的数据,只读。
代码示例:
// Props 传递
<Welcome name="张三" age={25} />
// Props 验证
import PropTypes from 'prop-types';
Welcome.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number,
onClick: PropTypes.func,
style: PropTypes.object,
children: PropTypes.node
};
// 默认 Props
Welcome.defaultProps = {
age: 18
};
10. 函数组件与类组件
问题:函数组件与类组件的区别
答案: 核心回答:函数组件是无状态组件,类组件是有状态组件(现在推荐使用 Hooks)。
代码示例:
// 函数组件
function Welcome({ name }) {
return <h1>Hello, {name}</h1>;
}
// 类组件
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
// Hooks 替代类组件状态
function Counter() {
const [count, setCount] = useState(0);
return <div>Count: {count}</div>;
}
六、Redux 状态管理
11. Redux 核心概念
问题:Redux 的核心概念
答案: 核心回答:Store、Action、Reducer 是 Redux 核心。
代码示例:
// Action - 描述发生了什么
const ADD_TODO = 'ADD_TODO';
const action = {
type: ADD_TODO,
payload: { text: '学习 Redux' }
};
// Reducer - 根据 action 更新 state
function todos(state = [], action) {
switch (action.type) {
case ADD_TODO:
return [...state, action.payload];
default:
return state;
}
}
// Store - 存储唯一状态树
import { createStore } from 'redux';
const store = createStore(todos);
// 获取状态
store.getState();
// 派发 action
store.dispatch(action);
// 订阅变化
store.subscribe(() => console.log(store.getState()));
12. Redux 中间件
问题:Redux 中间件
答案: 核心回答:中间件扩展 dispatch 功能,处理异步 action。
代码示例:
// redux-thunk
const thunkMiddleware = store => next => action => {
if (typeof action === 'function') {
return action(store.dispatch, store.getState);
}
return next(action);
};
// redux-saga
import { call, put, takeEvery } from 'redux-saga/effects';
function* fetchUserSaga(action) {
try {
const user = yield call(Api.fetchUser, action.payload);
yield put({ type: 'FETCH_SUCCESS', payload: user });
} catch (e) {
yield put({ type: 'FETCH_ERROR', error: e.message });
}
}
function* rootSaga() {
yield takeEvery('FETCH_USER', fetchUserSaga);
}