在面试 React 前端开发岗位时,我们当然应该为即将面对的技术问题做好充分准备。React 是目前用户界面构建领域最具人气的 JavaScript 库之一,企业雇主往往非常注重评估受试者对 React 核心概念、最佳实践以及相关技术方法的掌握情况。在本文中,我们将演讲 React 前端开发者在面试中经常遇到的 44 个问题。通过熟悉这些问题和答案,大家有望增加成功几率,充分展现自己在 React 开发方面的知识储备和技能水平。咱们闲言少叙,马上进入正题。
上篇是前22道题,下篇是后22道题,你能顺利通关吗?
-
useState: 用于管理函数组件中的状态。
-
useEffect: 用于在函数组件中执行 side effects,例如获取数据或订阅事件。
-
useContext: 用于访问函数组件当中 React 上下文的值。
-
useRef: 用于为跨渲染持续存在的元素或值创建可变引用。
-
useCallback: 用于记忆函数,以防止不必要的重新渲染。
-
useMemo: 用于记忆值,即将成本高昂的计算结果缓存起来以提高性能。
-
useReducer: 负责使用 reducer 函数管理状态,原理类似于 Redux。
-
useLayoutEffect: 与 useEffect 类似,但效果会在所有 DOM 更改之后再同步运行。
这些 hooks 提供强大的工具,可用于管理状态、处理 side effects 和重用 React 函数组件当中的逻辑。
了解更多:https://react.dev/reference/react
虚拟 DOM 是 React 中提出的概念,用于为实际 DOM(文档对象模型)创建一个轻量化虚拟表示,并将其存储在内存当中。这是一种用于优化 Web 应用程序性能的编程技术。
当 React 组件的数据或状态发生变更时,虚拟 DOM 也会随之更新,而非直接操作实际 DOM。此后,虚拟 DOM 计算组件先前状态与更新状态之间差异的过程,被称为“diffing”。
一旦发现存在差异,React 将仅更新实际 DOM 当中的必要部分,借此高效反映变更内容。这种方式最大限度减少了实际 DOM 上的操作数量,进而提高了应用程序的整体性能。
通过使用虚拟 DOM,React 在提供动态及交互式用户界面创建方法的同时,也保证应用程序始终拥有最佳效率和渲染速度。
要渲染一个元素数组,可以使用 map() 方法迭代该数组,并返回一个新的 React 元素数组。
const languages = [
"JavaScript",
"TypeScript",
"Python",
];
function App() {
return (
{languages.map((language) => - {language}
)}
);
}
了解更多:https://react.dev/learn/rendering-lists
受控组件与非受控组件之间的最大区别,在于如何管理和更新自身状态。
受控组件的状态由 React 负责控制,该组件接受其当前值并通过 props 进行更新。当值发生改变时,它会触发回调函数,也就是说该组件不会存储自己的内部状态。相反,由父组件管理该值并将其传递给受控组件。
另一方面,非受控组件则使用 refs 或其他方法在内部管理自身状态。这类组件独立存储并更新其状态,不依赖于 props 或回调。父组件对非受控组件的状态控制能力较弱。
了解更多:https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components
基于类的组件和函数组件之间的主要区别,在于二者的定义方式和所用语法不同。
基于类的组件被定义为 ES6 类,属于 React.Component 类的扩展。它们使用 render 方法返回定义组件输出的 JSX(JavaScript XML)。类组件可以通过 this.state 和 this.setState() 访问其生命周期方法和状态管理。
class App extends React.Component {
state = {
value: 0,
};
handleAgeChange = () => {
this.setState({
value: this.state.value + 1
});
};
render() {
return (
<>
Value is {this.state.value}
Increment value
>
);
}
}
另一方面,函数组件被定义为简单的 JavaScript 函数。它们接受 props 作为参数并直接返回 JSX。函数组件无权访问生命周期方法或者状态。但随着 React 16.8 中 React hooks 机制的出现,函数组件现在也可以管理状态并使用其他功能,例如上下文和效果。
import { useState } from 'react';
const App = () => {
const [value, setValue] = useState(0);
const handleAgeChange = () => {
setValue(value + 1);
};
return (
<>
Value is {value}
Increment value
>
);
}
一般来讲,函数组件往往更简单、易于阅读和测试。所以除非确实需要类组件,否则建议大家尽量使用函数组件。
生命周期方法,属于一种钩入组件生命周期各个阶段的方法,允许开发者在特定时间执行特定的代码。
以下是几种主要生命周期方法:
-
constructor: 这也是创建组件时调用的第一个方法,用于初始化状态并绑定事件处理程序。在函数组件中,我们可以使用 useState hook 来实现类似的效果。
-
render: 此方法负责渲染 JSX 标记,并返回要在屏幕上显示的内容。
-
componentDidMount: 此方法将在组件于 DOM 中渲染后被立即调用,通常用于初始化任务,例如 API 调用或设置事件侦听器。
-
componentDidUpdate: 此方法会在组件的 props 或 state 发生变更时被调用,允许开发者执行 side effects、根据变更更新组件或者触发其他 API 调用。
-
componentWillUnmount: 此方法会在组件从 DOM 中删除之前被调用,用于清理conponentDidMount 中设置的一切资源,例如删除事件侦听器或取消计时器。
某些生命周期方法(例如 componentWillMount、componentWillReceiveProps 和 componentWillUpdate)现已被弃用,或者被其他方法或 hooks 所替代。
至于“this”方法,是指类组件的当前实例。我们可以用它访问组件内的属性和方法。在函数组件中不需要使用“this”,因为函数不会绑定至特定实例。
useState 会返回一个状态值和一条更新该值的函数。
const [value, setValue] = useState('Some state');
在初始渲染期间,返回的状态与传递来的首个参数值相匹配。setState 函数用于更新该状态,它采用新的状态值作为参数并对组件的重新渲染操作进行排队。setState 函数还可以接受回调函数作为参数,该函数会将之前的状态值作为参数。
了解更多:https://react.dev/reference/react/useState
useEffect hook 允许我们在函数组件中执行 side effects。
在 React 的渲染阶段,函数组件的主体之内不得出现突变、订阅、计时器、日志记录及其他 side effects,这些可能导致用户界面中出现难以理解的错误和一致性冲突。
相反,这里建议使用 useEffect。传递给 useEffect 的函数将在渲染被提交至屏幕后才开始执行;而如果您传递一组依赖项作为第二参数,则每当有依赖项发生变更时,都会调用该函数。
useEffect(() => {
console.log('Logging something');
}, [])
了解更多:https://react.dev/reference/react/useEffect
一般来说,useEffect 所创建的资源需要在组件离开屏幕前进行清理或重置,例如订阅或计时器标记。
为此,传递给 useEffect 的函数可以返回一个清理函数。该清理函数将在组件被从用户界面中删除之前运行,防止发生内存泄漏。此外,如果组件经过多次渲染(属于常见情况),则在执行下一效果之前会先清除上一效果。
useEffect(() => {
function handleChange(value) {
setValue(value);
}
SomeAPI.doFunction(id, handleChange);
return function cleanup() {
SomeAPI.undoFunction(id, handleChange);
};
})
Props 是指从父组件传递给当前组件的数据。Props 有只读限制,无法更改。
// 父组件
const Parent = () => {
const data = "Hello, World!";
return (
);
};
// 子组件
const Child = ({ data }) => {
return {data}
;
};
了解更多:https://react.dev/learn/passing-props-to-a-component
状态管理器是帮助管理应用程序状态的工具或库,负责提供一个集中的存储或容器,用以容纳并管理可由应用程序中各个组件访问并更新的数据。
状态管理器可以解决以下几个问题。首先,最好将数据同与之相关的逻辑 / 组件彼此分离。第二,在使用本地状态并在组件之间进行传递时,由于组件中可能存在深层嵌套,因此代码往往会比较复杂。通过建立全局存储,我们可以访问并修改来自任意组件的数据。
除了 React Context 以外,常见的状态管理库还有 Redux 和 MobX。
了解更多:https://mobx.js.org/README.html
了解更多:https://redux-toolkit.js.org/
如果仅需要在单一组件内使用,而且无需传递给其他组件,则建议使用本地状态。本地状态还适用于组件只需在列表中表示单一项目的情况。但如果组件拆分涉及到嵌套组件,而且数据需要沿层次结构进行传递,则最好使用全局状态。
13. Redux 中的 reducer 是什么,
Reducer 属于纯函数,并将状态和操作作为参数。在 reducer 内部,我们会跟踪接收到的 action 类型,再根据它修改状态并返回一个新的状态对象。
export default function appReducer(state = initialState, action) {
// Reducer通常会查看action类型字段来决定如何执行
switch (action.type) {
// 根据action的具体类型选择执行方式
default:
// 如果此reducer无法识别action类型
// 或者此action不重要,则直接返回现有状态
}
}
了解更多:https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers
Action 属于简单的 JavaScript 对象,其组成为字段加类型。
我们也可以为其添加数据作为负载payload。要改变状态,则须调用我们传递给action的dispatch调度函数。
{
type: "SOME_TYPE",
payload: "Any payload",
}
了解更多:https://redux.js.org/tutorials/fundamentals/part-3-state-actions-reducers
Redux 实现的是 Flux 模式,即应用程序的可预测状态管理模式。它通过引入单向数据流与应用程序状态的集中存储机制,帮助管理应用程序状态。
了解更多:https://www.newline.co/fullstack-react/30-days-of-react/day-18/#:~:text=Flux%20is%20a%20pattern%20for,default%20method%20for%20handling%20data.
Mobx 实现的是 Observer 模式,也被称为发布 - 订阅模式。
了解更多:https://www.patterns.dev/posts/observer-pattern
Mobx 提供 observable 和 computed 等修饰器来定义可观察状态与反应函数。用 action 修饰的操作用于修改状态,确保跟踪所有变更。Mobx 还提供自动依赖项跟踪、不同反应类型、对反应性的细粒度控制,以及通过 mobx-react 包与 React 的无缝集成。总的来说,Mobx 能够以可观察状态的变化为基础,自动执行更新过程以简化状态管理。
我们可以使用 observalbe 装饰器将该变量定义为 observable,借此实现对状态下变量的访问。例如:
import { observable, computed } from 'mobx';
class MyStore {
@observable myVariable = 'Hello Mobx';
@computed get capitalizedVariable() {
return this.myVariable.toUpperCase();
}
}
const store = new MyStore();
console.log(store.capitalizedVariable); // Output: HELLO MOBX
store.myVariable = 'Hi Mobx';
console.log(store.capitalizedVariable); // Output: HI MOBX
在本示例中,使用 observable 装饰器将 myVariable 定义为 observable。之后,我们可以使用 store.myVariable 访问该变量。如此一来,对 myVariable 所做的任何更改都会自动触发相关组件或反应的更新。