React 面试题详解
React 基础
- 什么是纯函式 (pure function)? 为什么 React 的函式元件需要是纯函式?
- 纯函式:只要有相同的输入,就会有相同的输出」;不会有副作用(side effects)不改变函式以外的存在
- 如果不纯 (impure),会让渲染出的画面不稳定,可能出现我们预期外的 UI 呈现。
- 什么是 JSX? 为什么要用 JSX?
- JSX 的全称是 JavaScript XML。JSX 等于是把标记 (markup) 与逻辑 (logic) 写在一起。
- JSX 规则
- 回传单一个根元素: 如果有一元件最终回传多个元素,我们需要将这些元素用一元素包起来当做父层元素.(例如用 或使(<> 和</>)。
- 所有标签都需要 Close: 例如: 一定要用 表示。
- 几乎所有属性名称是用 camelCase 表示
- 请解释 React 生命周期?
- Class Component(类组件)旧时代产物
class MyComponent extends React.Component {
render() {
return <div>Hello</div>;
}
}
- Function Component(函数组件)——现代主流
function MyComponent() {
return <div>Hello</div>;
}
- React 组件有三个核心生命周期:Mounting、Updating、Unmounting。
在 Mounting 时依序执行 constructor → render → componentDidMount。
在 Updating 时,props 或 state 改变会触发 render,更新完成后执行 componentDidUpdate。
在 Unmounting 时执行 componentWillUnmount 做清理。
整个流程分成 Render 阶段 和 Commit 阶段:Render 阶段纯计算、不能有副作用;Commit 阶段才会真的操作 DOM,并触发副作用方法。
Functional Component 没有这些 lifecycle 方法,但用 useEffect 来覆盖 mount/update/unmount 的副作用场景。
- 什么是 Virtual DOM?
- DOM(Document Object Model)是浏览器把 HTML 转换成的、可供 JavaScript 操作的树形结构。HTML 本身只是文本。浏览器解析后,会变成一个节点树,每个标签、文字、属性都会变成一个对象(Node),这样 JS 才能读写、修改、增删结构。
- 一个存放在内存里的 JavaScript 对象树,用来映射真实 DOM 的结构。因为它只是普通对象,所以创建和比较(diff)都非常快。
- 使用 Virtual DOM 的好处是,可以使用声明式语法编写 React.使用声明式的写法让开发者不需要直接操作 DOM,而是透过 state 来告诉 React 我们想要的 UI 画面,React 会确保 DOM 和 state 是匹配的。
- 在渲染过程中,React 会自动生成 Virtual DOM,并且比较前后两次 Virtual DOM 的差异,只更新发生变化的部分。
- React Hooks 是什么?
- 什么是 Hook?:Hook = 把「状态逻辑」和「副作用逻辑」从 class 中抽出来,让函数组件变得有能力。
- useState —— 状态管理的最小单元
const [count, setCount] = useState(0);
<button onClick={() => setCount(count + 1)}>{count}</button>
- useEffect —— 副作用(异步、订阅、事件、DOM 操作)
useEffect(() => {
const id = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(id);
}, []);
- useLayoutEffect —— 更早执行的副作用
useLayoutEffect(() => {
const rect = ref.current.getBoundingClientRect();
console.log(rect);
});
- useReducer —— 管理更复杂状态
function reducer(state, action) {
switch (action.type) {
case 'inc':
return { count: state.count + 1 };
default:
return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
<button onClick={() => dispatch({ type: 'inc' })}>
{state.count}
</button>
- useCallback —— 让函数的“引用地址”在依赖不变时稳定,从而避免不必要渲染和副作用重复执行。只在组件第一次渲染时创建这个函数,以后所有渲染都复用同一个函数引用。
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
- useMemo —— “只有依赖变化时才重新执行计算,否则复用上一次的结果”。防止每次渲染都重新 filter + sort
const total = useMemo(() => {
return items.reduce((sum, v) => sum + v, 0);
}, [items]);
- useRef —— useRef —— 保存“可变值”,但不会触发渲染
const inputRef = useRef();
useEffect(() => {
inputRef.current.focus();
}, []);
- 为什么只能在最顶端层呼叫 Hook?从 useState 实作原理来回答
- if…else 判断式中,那每次渲染的顺序可能就会产生变化,这会使得 React 无法得知每个 Hook 对应的值应该返回什么,这将导致 state 的顺序可能错乱。这也是为什么,我们不该在回圈、条件式或是巢状的 function 内呼叫 Hook,以及只能在最顶端层呼叫 Hook。
- Hooks 是按调用顺序绑定 state 的,而不是按变量名。只要顺序不稳定,状态就一定错乱。
- 请解释 useEffect?与 useLayoutEffect 的区别?
- useEffect = 渲染后再说,慢一点没影响的逻辑放这里。
- useLayoutEffect = 画面还没显示前,先把布局相关的逻辑处理完。
- 为什么 React 渲染列表时需要加上 key?
- 因为 React 更新列表不是“全部重建”,而是“尽可能复用旧节点”。为了复用,它必须回答一个问题:“新列表里的第 X 个元素,对应旧列表里的谁?”React 本身不知道。只靠数组顺序是靠不住的,因为顺序可能变化。所以 React 需要开发者提供一个稳定的身份:key。
- 为什么避免把 index 当成 key:insert/remove 会让 index 全体偏移 → 全部节点被误解。React 会误判为“全部变化”→ 大面积重建 DOM
- 为什么更新 React 中的 state 要用 immutable 的写法? 什么是 immutable? 该如何写才会是 immutable?
- immutable(不可变)=不直接修改原对象,而是创建一个新对象/新数组。mutable(可变)=直接改原对象本身。关键差异:是否产生了新引用(new reference)。React 只有在“引用变化(reference changed)”时,才知道 state 变了。
- 展开运算子就是我们常见的 …
- JavaScript 的immutable数组方法
- 加入元素到数组:
slice - 从数组中移除元素:
filter - 改变数组中的值:
map
- 加入元素到数组: