一、React Key的作用
1.1 Key的定义
Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。
1.2 Key的作用
- 帮助 React 识别哪些元素发生了变化
- 提高 diff 算法的效率
- 避免不必要的重新渲染
1.3 有Key与没Key的区别
| 情况 | 有Key | 没Key |
|---|---|---|
| 列表更新 | 精确定位变化的元素 | 可能导致错误的重新渲染 |
| 元素复用 | 根据Key匹配原有元素 | 无法匹配,可能创建新元素 |
| 性能 | 更高 | 可能性能下降 |
1.4 同一层级节点比较
React 的 diff 算法只在同一层级的节点之间进行比较:
- 相同Key:认为是同一个节点,比较属性变化
- 不同Key:认为是新节点,执行插入/删除操作
- 跨层级:无法移动,只能删除旧节点、创建新节点
1.5 最佳实践
- 使用稳定的唯一ID作为Key
- 避免使用数组下标作为Key(会导致渲染错误)
- 避免使用随机数作为Key
二、虚拟DOM与Diff算法
2.1 虚拟DOM定义
虚拟DOM 本质上是 JavaScript 对象,是对 真实DOM 的抽象表现。
const vdom = {
type: "div",
props: { className: "container", children: "Hello" },
};
2.2 虚拟DOM优势
- 减少直接操作DOM,提高性能
- 跨平台能力更强(SSR、Native等)
- 批量更新,减少重排重绘
2.3 Diff算法流程
- 状态变更:记录新树和旧树的差异
- 对比差异:通过diff算法找出最小更新点
- 批量更新:将差异更新到真正的DOM中
2.4 render函数原理
- 根据
tagName生成父标签,读取props,设置属性 - 如果有
content,设置innerHTML或innerText - 如果存在子元素,遍历子元素递归调用
render方法 - 将生成的子元素依次添加到父元素中,返回根元素
2.5 Diff算法优化策略
- 同级比较:只比较同层级的节点
- 类型比较:不同类型节点直接替换
- Key匹配:通过Key精确匹配节点
三、组件通信方式
3.1 父子组件通信
父->子通信:
- 使用
props传递数据
;
function ChildComponent(props) {
return {props.name};
}
子->父通信:
- 使用回调函数传递数据
function Parent() {
const handleData = data => console.log(data);
return ;
}
function Child({ onData }) {
return ;
}
父调用子方法:
- 使用
ref获取子组件实例
const childRef = useRef();
childRef.current.handleMethod();
3.2 兄弟组件通信
通过父组件中转:
- 状态提升到父组件,子组件通过props和回调通信
使用状态管理库:
- Redux、MobX、Zustand 等
发布订阅模式:
- 使用 EventEmitter 或自定义事件系统
3.3 跨级组件通信
Context API:
const ThemeContext = React.createContext("light");
function App() {
return (
);
}
function ThemedButton() {
const theme = useContext(ThemeContext);
return ;
}
3.4 现代状态管理方案
| 方案 | 特点 | 适用场景 |
|---|---|---|
| Zustand | 轻量级,API简洁 | 中小型应用 |
| Jotai | 原子化状态管理 | 细粒度状态管理 |
| Recoil | Facebook开发 | 大型应用 |
3.5 组件组合
将子组件作为 children 或 render prop 传递,避免过度使用 Context:
function Card({ children }) {
return {children};
}
function MouseTracker({ render }) {
return {render(mousePosition)};
}
四、JSX解析
4.1 JSX本质
JSX 是 JavaScript 的语法扩展,React 使用它来描述 UI 结构。
4.2 编译过程
JSX 通过编译器(如 Babel)转换为 React.createElement 函数调用:
const element = Hello;
const element = React.createElement("div", { className: "greeting" }, "Hello");
4.3 createElement参数
React.createElement(
type, // 元素类型
props, // 元素属性
children // 子元素
);
五、React生命周期
5.1 类组件生命周期
挂载阶段:
| 钩子 | 说明 |
|---|---|
| constructor | 组件实例创建 |
| getDerivedStateFromProps | 从props派生state(静态方法) |
| render | 渲染组件 |
| componentDidMount | 组件挂载完成 |
更新阶段:
| 钩子 | 说明 |
|---|---|
| getDerivedStateFromProps | props变化时更新state |
| shouldComponentUpdate | 决定是否重新渲染 |
| render | 重新渲染 |
| getSnapshotBeforeUpdate | 获取更新前快照 |
| componentDidUpdate | 组件更新完成 |
卸载阶段:
| 钩子 | 说明 |
|---|---|
| componentWillUnmount | 组件卸载前清理 |
5.2 错误处理生命周期
| 钩子 | 说明 |
|---|---|
| getDerivedStateFromError | 捕获子组件错误(静态方法) |
| componentDidCatch | 实例方法,记录错误信息 |
5.3 废弃的生命周期
componentWillMount、componentWillReceiveProps、componentWillUpdate- React 16 标记为 UNSAFE_
- React 18 完全移除
5.4 Hooks生命周期(函数组件)
| 类组件钩子 | Hook |
|---|---|
componentDidMount |
useEffect(() => {}, []) |
componentDidUpdate |
useEffect(() => {}, [deps]) |
componentWillUnmount |
useEffect(() => { return () => {} }, []) |
useEffect(() => {
console.log("mounted");
return () => console.log("cleanup");
}, []);
5.5 useLayoutEffect
- 与 useEffect 类似,但同步执行
- 在 DOM 更新后同步执行,用于DOM测量
六、React性能优化
6.1 渲染优化
函数组件优化:
| Hook | 作用 |
|---|---|
React.memo |
缓存组件渲染结果 |
useMemo |
缓存计算结果 |
useCallback |
缓存函数引用 |
const MemoizedComponent = React.memo(Component);
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
类组件优化:
- 使用
PureComponent自动进行浅比较 - 实现
shouldComponentUpdate自定义比较逻辑
通用优化:
- 使用稳定的
key值 - 避免在 render 中创建新对象、新函数
- 保持 props 和 state 扁平化
- 使用
return null代替display: none
6.2 长列表优化
- 虚拟滚动:
react-window、react-virtualized - 分页加载或无限滚动
6.3 图片优化
- 懒加载图片
- 使用适当尺寸的图片
- 使用 WebP 格式
6.4 动画优化
- 使用
transform和opacity实现动画 - 使用
requestAnimationFrame优化动画性能 - 避免布局抖动
6.5 代码分割
const LazyComponent = React.lazy(() => import("./LazyComponent"));
function App() {
return (
}>
);
}
6.6 Concurrent Mode优化
useTransition:标记非紧急更新useDeferredValue:延迟处理非紧急状态
6.7 构建优化
- 使用
esbuild或swc加速构建 - 配置合理的
sourcemap - 压缩和混淆代码
- 提取 CSS 到单独文件
七、this绑定
7.1 绑定方式对比
| 方式 | 写法 | 性能 | 推荐度 |
|---|---|---|---|
| bind方法 | this.handleClick = this.handleClick.bind(this) |
每次渲染创建新函数 | 中 |
| 箭头函数 | onClick={() => this.handleClick()} |
每次渲染创建新函数 | 中 |
| 类属性 | handleClick = () => {} |
只创建一次 | 高 |
7.2 推荐写法
class MyComponent extends React.Component {
handleClick = () => {
console.log(this);
};
}
八、React 18新特性
8.1 并发特性
| 特性 | 说明 |
|---|---|
| 时间切片 | 将渲染拆分为小任务,优先处理用户交互 |
| Suspense | 支持组件级别代码分割和数据获取 |
| useTransition | 标记非紧急更新 |
| useDeferredValue | 延迟处理非紧急状态 |
8.2 自动批处理
- 所有状态更新都会被批处理
- 包括 setTimeout、Promise 回调中的更新
8.3 新根API
import { createRoot } from "react-dom/client";
const root = createRoot(document.getElementById("root"));
root.render( );
8.4 服务端组件
- 服务端渲染组件,减少客户端包体积
- 支持直接访问后端数据
8.5 其他改进
| Hook | 说明 |
|---|---|
| useId | 生成唯一ID |
| useSyncExternalStore | 订阅外部状态 |
| useInsertionEffect | CSS-in-JS优化 |
8.6 现代渲染策略
| 策略 | 说明 |
|---|---|
| Streaming SSR | 流式服务端渲染,逐步发送HTML,减少首屏时间 |
| SSG | 静态站点生成,构建时预渲染页面 |
| ISR | 增量静态再生,运行时按需重新生成页面 |
九、React Fiber
9.1 Fiber架构目的
React Fiber 是 React 16 引入的新的调和算法,解决同步渲染阻塞问题。
9.2 核心特性
| 特性 | 说明 |
|---|---|
| 增量渲染 | 将渲染拆分为小单元 |
| 优先级调度 | 根据任务优先级安排执行顺序 |
| 可中断 | 允许处理更高优先级任务 |
| 时间切片 | 空闲时间执行渲染任务 |
9.3 Fiber工作流程
- Render阶段:生成Fiber树,确定需要更新的内容
- Commit阶段:执行DOM操作
十、setState同步异步
10.1 合成事件与钩子函数
在合成事件和钩子函数中,setState 是”异步”的:
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
this.setState({ count: this.state.count + 1 }, () => {
console.log(this.state.count);
});
10.2 原生事件与setTimeout
在原生事件和 setTimeout 中,setState 是同步的:
document.getElementById("btn").addEventListener("click", () => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
});
setTimeout(() => {
this.setState({ count: this.state.count + 1 });
console.log(this.state.count);
}, 0);
10.3 批量更新
- 同一值的多次 setState 会被覆盖
- 不同值的 setState 会合并批量更新
10.4 异步原理揭秘
setState 的”异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的”异步”,当然可以通过第二个参数 setState(partialState, callback) 中的 callback 拿到更新后的结果。
十一、Redux核心概念
11.1 数据流
用户行为 → Action → Reducer → Store → View11.2 核心组成
| 组成 | 说明 |
|---|---|
| Store | 存储所有状态 |
| Action | 描述发生的事件 |
| Reducer | 根据Action更新状态 |
11.3 React-Redux
| 组件 | 说明 |
|---|---|
| Provider | 从最外部封装整个应用,传递store |
| Connect | 连接React组件与Redux store |
Connect 的工作原理:
- 包装原组件,将
state和action通过props的方式传入到原组件内部 - 监听
store tree变化,使其包装的原组件可以响应state变化
十二、高阶组件
12.1 定义
高阶组件是参数为组件,返回值为新组件的函数。
function withAuth(WrappedComponent) {
return function WithAuthComponent(props) {
if (isAuthenticated) {
return ;
}
return ;
};
}
12.2 作用
- 代码复用,逻辑抽象
- 渲染劫持
- State 抽象和更改
- Props 更改
十三、创建组件的方式
13.1 函数组件(推荐)
const Counter = () => {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Count: ${count}`;
}, [count]);
return (
{count}
);
};
13.2 类组件
class Counter extends React.Component {
state = { count: 0 };
increment = () => {
this.setState({ count: this.state.count + 1 });
};
render() {
return (
{this.state.count}
);
}
}
13.3 现代组件写法
| 写法 | 说明 |
|---|---|
| React.memo | 缓存函数组件渲染 |
| forwardRef | 传递ref给子组件 |
| lazy | 动态导入组件 |
十四、元素与组件的区别
| 概念 | 说明 |
|---|---|
| React元素 | 普通对象,描述UI结构 |
| 组件 | 类或纯函数,由元素构成 |
十五、React核心特点
15.1 核心思想
- React:函数式编程,单向数据流,数据不可变
- 对比:Vue 采用响应式系统,数据驱动视图
15.2 模板语法
- React:使用 JSX,将 HTML 与 JavaScript 结合,更灵活
- 对比:Vue 使用 HTML-like 模板语法,支持指令
15.3 状态管理
| 方案 | 特点 | 适用场景 |
|---|---|---|
| Redux | 成熟稳定,生态完善 | 大型应用 |
| Zustand | 轻量级,API简洁 | 中小型应用 |
| Jotai | 原子化状态管理 | 细粒度状态管理 |
15.4 性能优化
| 优化方式 | 说明 |
|---|---|
| memo/useMemo/useCallback | 手动优化渲染性能 |
| 并发模式 | React 18 新特性 |
| 编译时优化 | Tree Shaking、静态提升 |
15.5 适用场景
- 大型应用
- 复杂状态管理需求
- 需要良好的 TypeScript 支持
十六、Redux与Vuex对比
16.1 核心区别
| 对比项 | Redux | Vuex |
|---|---|---|
| 数据可变性 | 不可变,每次返回新state | 可变,直接修改 |
| 变化检测 | 订阅机制触发更新通知 | getter/setter监听 |
| 适用框架 | React | Vue |