英文 | https://blog.stackademic.com/top-40-reactjs-interview-questions-and-answers-for-2024-70c94e5fccca
ReactJS 已成为现代 Web 开发的基石,其基于组件的架构和高效的渲染使其成为构建动态用户界面的首选。
无论你是希望提高技能的经验丰富的开发人员,还是准备即将到来的 ReactJS 面试的求职者,本指南都将为 ReactJS 开发中的关键概念和最佳实践提供宝贵的见解。
让我们深入探讨有助于你在 2024 年 ReactJS 面试中取得好成绩的基本主题。
1.ReatcJS是什么以及它是如何工作的?
ReactJS 是一个功能强大的 JavaScript 库,用于使用构建块创建交互式用户界面。
ReactJS 的运行原理是声明式和基于组件的方法。这些组件是小型的独立单元,可以组合在一起构建复杂的用户界面。
当 React 应用程序运行时,它会在内存中创建用户界面的虚拟表示,称为虚拟 DOM。Virtual DOM 是一个轻量级 JavaScript 对象,包含实际 DOM 元素的所有属性和属性。这是一个在内存中保留 UI 的理想表示并将其与实际 DOM 同步的编程概念。
当 React 组件的 state 或 props 发生变化时,React 会创建一个新的 VDOM 树。
VDOM 与 React 的协调算法相结合,计算新的和以前的 VDOM 表示之间的差异。
然后,它仅更新实际 DOM 中已更改的部分,从而最大限度地减少整页刷新的需要并提高性能。
2. Shadow DOM 和 Virtual DOM 有什么区别?解释和解过程。
虚拟 DOM:它是库在内存中保存的实际 DOM(文档对象模型)的轻量级副本。当对虚拟 DOM 进行更改时,库会计算更新实际 DOM 的最有效方法,并且仅进行这些特定更改,而不是重新渲染整个 DOM。
Shadow DOM:Shadow DOM 专注于封装 Web 组件的样式和结构。它是一种浏览器技术,主要用于在 Web 组件中确定变量和 CSS 的范围。
以便其内部实现对页面的其余部分隐藏。它允许您创建具有自己的样式和标记的独立组件,这些组件不会干扰页面其余部分的样式或行为。
协调:这是 React 更新浏览器 DOM 并使 React 工作得更快的过程。React 使用 diff 算法,以便组件更新可预测且更快。
当我们进行更改或添加数据时,React 会创建一个新的 Virtual DOM 并将其与前一个进行比较。
这种比较是通过 Diffing 算法完成的。现在 React 将 Virtual DOM 与 Real DOM 进行比较。它找出已更改的节点并仅更新 Real DOM 中已更改的节点,其余节点保持原样。
3. 元素和组件有什么区别?
React 中的 Element 是一个普通对象,描述组件实例或 DOM 节点及其所需的属性,也称为 props。元素是 React 应用程序的最小构建块,通常使用 JSX 创建,JSX 是 JavaScript 的语法扩展。
const element = React.createElement("h1");
//returns an object similar to this one:
{
type: 'h1',
props: {}
}
另一方面,组件是可重用的 UI 部分,可以由一个或多个元素组成。React 中的组件可以是函数组件,也可以是类组件。它们封装了渲染和行为的逻辑,并且可以接受输入数据(道具)并维护内部状态。
const App(){
return <div>Hello World !</div>;
}
export default App;
4.reactjs中的state和props是什么?
状态用于管理组件的内部数据及其随时间的变化。状态是可变的,可以使用 setState 方法进行更新。状态更改可以是异步的。
状态的更改会触发组件的重新呈现,从而允许用户界面反映更新后的状态。
class Button extends React.Component {
constructor() {
super();
this.state = {
count: 0,
};
}
updateCount() {
this.setState((prevState, props) => {
return { count: prevState.count + 1 }
});
}
render() {
return (<button
onClick={() => this.updateCount()}
>
Clicked {this.state.count} times
</button>);
}
}
Props(属性的缩写)是一种将数据从父组件传递到子组件的机制。它们是只读的(不可变的),有助于使组件可重用和可定制。
Props 作为属性传递给组件,并且可以使用类组件中的 this.props 在组件内进行访问,或者作为函数组件的参数进行访问。
5. 什么是纯组件和 React.memo()?
纯组件是 React 中的一种组件,它通过浅层 prop 和状态比较自动实现 shouldComponentUpdate() 方法。
这意味着纯组件仅在 props 或 state 发生更改时才会重新渲染。它在处理类组件时特别有用,并且可以通过避免不必要的重新渲染来帮助提高性能。
import React, {PureComponent} from "react";
class BlogPostExcerpt extends PureComponent {
constructor(props) {
super(props)
this.state = { clicked: false }
}
render() {
return (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
)
}
}
React.memo() 是一个高阶组件,与功能组件一起使用以防止不必要的重新渲染。它的工作原理是记住组件渲染的结果,并且只有在 props 发生变化时才重新渲染。
当处理接收相同道具但不需要在每次更改时重新渲染的功能组件时,这尤其有用。
另外,如果组件很轻并且使用多个 props 渲染,请避免使用 React Memo。
import React from 'react';
import TodoItem from './TodoItem';
const Todo = React.memo(({ list }) => {
// console.log('Todo component rendered');
return (
<ul>
{list.map((item) => (
<TodoItem key={item.id} item={item} />
))}
</ul>
);
});
export default Todo;
6. React 中什么是合成事件?
合成事件是浏览器本机事件系统的跨浏览器包装器。它们旨在确保不同浏览器和设备之间的行为和性能一致。
它们提供了统一的 API 来处理 React 中的事件,无论浏览器如何。
要在 React 中使用合成事件,您只需向组件添加事件处理程序即可。事件处理程序将传递 SyntheticEvent 对象的实例。
然后,您可以使用 SyntheticEvent 对象的属性和方法来处理该事件。
function handleClick(event) {
// Do something with the event
}
<button onClick={handleClick}>Click me!</button>
在此示例中,单击按钮时,handleClick() 函数将传递 SyntheticEvent 对象的实例。然后,handleClick() 函数可以使用 SyntheticEvent 对象的属性和方法来处理该事件。
7. 组件生命周期有哪些不同阶段?
在 React 中,组件生命周期由三个主要阶段组成:安装、更新和卸载。每个阶段都包含特定的生命周期方法,允许您在组件生命周期的不同点执行操作。
安装:
构造函数:这是创建组件时调用的第一个方法。它用于初始化状态和绑定事件处理程序。
getDerivedStateFromProps:当接收到新的 props 或 state 时,在渲染之前调用此方法。它允许组件根据 props 的变化更新其内部状态。
render:此方法负责根据当前状态和属性渲染组件的 UI。
componentDidMount:该方法在组件第一次渲染后调用。它用于执行需要完全安装组件的任何操作,例如数据获取或设置订阅。
更新中:
getDerivedStateFromProps:当接收到新的 props 或 state 时,在渲染之前调用此方法。它允许组件根据 props 的变化更新其内部状态。
shouldComponentUpdate:该方法在组件重新渲染之前调用。它允许您控制组件是否应根据状态或道具的变化进行更新。
render:再次调用 render 方法来根据状态或 props 的变化来更新组件的 UI。
getSnapshotBeforeUpdate:在将最近呈现的输出提交到 DOM 之前调用此方法。它使您的组件能够在 DOM 可能发生更改之前从 DOM 捕获一些信息。
componentDidUpdate:该方法在组件因 state 或 props 变化而重新渲染后被调用。它用于在更新后执行操作,例如更新 DOM 以响应状态更改。
卸载:
componentWillUnmount:在组件从 DOM 中删除之前调用此方法。它用于执行任何清理,例如取消网络请求或清理订阅。
错误处理:
static getDerivedStateFromError(error):当后代组件抛出错误时,在“渲染”阶段调用此方法。它允许组件更新其状态以响应错误。
componentDidCatch(error, info):当后代组件抛出错误时,在“提交”阶段调用此方法。它用于捕获组件树中发生的错误并执行副作用,例如记录错误。
8. 什么是高阶分量?
高阶组件 (HOC) 是 React 中用于重用组件逻辑的强大而灵活的模式。
高阶组件是一种将组件作为参数并返回具有增强功能的新组件的函数。这允许您以可重用的方式抽象和共享多个组件之间的行为。
HOC 允许您向组件添加附加功能,而无需修改组件的代码
import React from 'react';
const withLoadingIndicator = (WrappedComponent) => {
return class WithLoadingIndicator extends React.Component {
state = {
isLoading: true
};
componentDidMount() {
// Simulate a loading delay
setTimeout(() => {
this.setState({ isLoading: false });
}, 2000);
}
render() {
if (this.state.isLoading) {
return <div>Loading...</div>;
}
return <WrappedComponent {...this.props} />;
}
};
};
// Usage
class MyComponent extends React.Component {
render() {
return <div>Content loaded!</div>;
}
const MyComponentWithLoadingIndicator = withLoadingIndicator(MyComponent);
在此示例中,withLoadingIndicator 高阶组件将一个组件作为参数,并返回一个添加加载指示器功能的新组件。仅当加载状态设置为 false 时,才会呈现包装的组件。
以下是 HOC 的常见用例列表:
条件渲染
验证
数据获取
造型
状态管理
缓存和记忆
国际化(i18n)
9. 什么是 context 和 useContext Hook?
在 React 中,Context 提供了一种通过组件树传递数据的方法,而无需在每个级别手动向下传递 props。
它旨在共享可被视为 React 组件树的全局数据的数据,例如当前经过身份验证的用户或主题。
上下文是使用 React.createContext 函数创建的。这将创建一个由提供者和消费者组成的上下文对象。Provider 组件用于包装组件树中上下文数据可用的部分,Consumer 组件用于使用上下文数据。
useContext() 挂钩用于使用功能组件内的上下文数据。它将上下文对象作为参数并返回当前上下文值。
import React, { createContext, useContext } from 'react';
// Create a context
const ThemeContext = createContext('light');
// A component that consumes the context using the useContext hook
const ThemedComponent = () => {
const theme = useContext(ThemeContext);
return <div>Current theme: {theme}</div>;
};
// A component that provides the context value using the Provider
const App = () => {
return (
<ThemeContext.Provider value="dark">
<ThemedComponent />
</ThemeContext.Provider>
);
};
在此示例中,我们使用 createContext 创建一个 ThemeContext 并提供默认值“light”。
然后,我们使用 ThemedComponent 中的 useContext 钩子从上下文中使用当前主题值。
在 App 组件中,我们使用 ThemeContext.Provider 包装 ThemedComponent 并提供值“dark”,该值会覆盖默认值。
10. 什么是无状态和有状态组件?
无状态组件是一种 React 组件,它被定义为纯 JavaScript 函数,并且表示没有内部状态管理的 UI 元素。
这些组件不管理自己的状态,也无法访问生命周期方法。他们只是接收“道具”并将其呈现给用户界面。无状态组件通常用于静态组件,其中所呈现的数据不需要更新。
import React from 'react';
const Greeting = (props) => {
return <h1>Hello, {props.name}!</h1>;
};
export default Greeting;
有状态组件用于管理状态、处理用户交互以及实现复杂的 UI 逻辑。
当数据随时间发生变化时,需要有状态组件,并且组件需要了解更新才能呈现它。他们能够使用 setState 方法保存和管理自己的状态。他们还可以访问生命周期方法。
随着 React hooks 的引入,有状态组件也可以使用函数式组件来编写。
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const incrementCount = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={incrementCount}>Increment</button>
</div>
);
};
export default Counter;
11.为什么我们不应该直接更新状态?
setState() 是一个异步操作,当你直接更新状态时,React 不会检测到发生了变化,因为它不会触发重新渲染过程。这可能会导致您的 UI 无法反映更新后的状态,从而导致难以调试的不一致和错误。
12. 回调函数作为 setState() 的参数的目的是什么?
setState() 不会立即改变 this.state() ,而是创建一个挂起的状态转换。调用此方法后访问 this.state() 可能会返回现有值。(意味着我们在调用 setState() 时不应该依赖当前状态)
解决方案是将一个函数传递给 setState(),并以先前的状态作为参数。通过这样做,我们可以避免由于 setState() 的异步特性而导致用户在访问时获取旧状态值的问题。
// Problem : It will not update count with +3 on each click
const hanldeCountIncrement = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
}
// count will be 1 not 3
// Solution: always use prev to update state.
const hanldeCountIncrement = () => {
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
setCount((prev) => prev + 1);
}
// on each click, value of count will update by +3
13. HTML 和 React 事件处理有什么区别?
HTML 和 React 事件处理在很多方面都很相似,但也有一些关键区别:
句法:
在 HTML 中,事件处理程序通常直接作为 HTML 标记中的属性编写,例如 <button οnclick="handleClick()">Click me</button>。
在 React 中,事件处理程序被指定为 JSX 元素上的驼峰式命名属性,例如 <button onClick={handleClick}>Click me</button>。
处理事件:
在 HTML 中,事件处理程序通常是内联函数或全局函数。
在 React 中,事件处理程序通常定义为组件类上的方法。
事件绑定:
在 HTML 中,要访问触发事件的元素(this 上下文),通常需要使用 this 或 event.target。
在 React 中,您可以在构造函数中使用箭头函数或 .bind(this) 显式绑定 this 上下文,也可以使用类属性(例如箭头函数语法)自动绑定 this。
事件对象:
在 HTML 中,事件对象会自动传递给事件处理函数。
在 React 中,事件对象也会自动传递给事件处理函数,但 React 会规范化事件对象以确保不同浏览器之间的行为一致。
防止默认行为:
在 HTML 中,为了防止事件的默认行为(例如,防止表单提交),您可以使用 event.preventDefault() 等方法。
在 React 中,您还在事件处理函数中使用 event.preventDefault(),但您在传递给该函数的事件对象上调用它。
事件冒泡和捕获:
HTML 和 React 都支持事件冒泡和捕获,其中事件从最里面的元素传播到最外面的元素(冒泡),反之亦然(捕获)。
在事件传播方面,React 的事件处理与 HTML 的事件处理类似。
14. 如何在 JSX 回调中绑定方法或事件处理程序?
在 React 中,有几种方法可以在 JSX 回调中绑定方法或事件处理程序。以下是最常见的方法:
在 JSX 中使用箭头函数(内联绑定):
class MyComponent extends React.Component {
handleClick = () => {
// Handler logic
};
render() {
return <button onClick={() => this.handleClick()}>Click me</button>;
}
}
在使用箭头函数的函数组件中:
import React from 'react';
const MyComponent = () => {
const handleClick = () => {
// Handler logic
};
return <button onClick={handleClick}>Click me</button>;
};
export default MyComponent;
构造函数中的绑定:
class MyComponent
extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
// Handler logic
}
render() {
return <button onClick={this.handleClick}>Click me</button>;
}
}
15. refs 有什么用,React.createRef 和 useRef hook 是什么?
在 React 中,“ref”是一个对象,它提供了一种引用或访问特定 DOM 节点或 React 元素的方法。Refs 通常用于与 DOM 命令式交互,例如聚焦输入、获取其尺寸或访问其方法。
引用是使用类组件中的 React.createRef() 方法或功能组件中的 useRef() 挂钩创建的。
创建后,可以使用 ref 属性将 ref 附加到 React 元素。这允许您使用 ref 对象的当前属性访问底层 DOM 节点或 React 元素。
在类组件中使用 createRef()
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
componentDidMount() {
// Accessing the DOM node using the ref
this.myRef.current.focus();
}
render() {
return <input ref={this.myRef} />;
}
}
在功能组件中使用 useRef() 钩子
import React, { useRef, useEffect } from 'react';
const MyComponent = () => {
const myRef = useRef(null);
useEffect(() => {
// Accessing the DOM node using the ref
myRef.current.focus();
}, []);
return <input ref={myRef} />;
};
16. 什么是前裁判?
转发引用是一种允许父组件将引用传递给其子组件的技术。当您需要从父组件访问子组件的 DOM 节点或 React 实例时,这会很有用。
转发引用通常用于高阶组件 (HOC) 和其他包装组件。
// ParentComponent.js
import React, { useRef } from 'react';
import ChildComponent from './ChildComponent';
const ParentComponent = () => {
const inputRef = useRef(null);
// Function to focus on the input
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
{/* Using ChildComponent and passing down the ref */}
<ChildComponent ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export default ParentComponent;
// ChildComponent.js
import React from 'react';
const ChildComponent = React.forwardRef((props, ref) => {
return <input ref={ref} />;
});
export default ChildComponent;
在这个例子中,ChildComponent是一个用React.forwardRef包装的功能组件。
这种包装允许 ChildComponent 接收从其父组件 (ParentComponent) 传递的 ref。
在 ParentComponent 内部,使用 useRef 挂钩创建一个 ref (inputRef)。然后使用 ref 属性将该引用传递给 ChildComponent。
因此,ParentComponent 中的 inputRef 现在指向 ChildComponent 呈现的输入元素,从而使父组件能够在单击按钮时强制聚焦于输入。
17. 什么是反应纤维?
React Fiber 是 React 16 中引入的一种新的协调算法。它旨在使 React 应用程序更快、更流畅,特别是对于具有大量更新的复杂应用程序。
React Fiber 的工作原理是将协调过程分解为更小的工作单元,称为纤维。纤程可以按任何顺序调度和执行,这使得 React 可以确定工作的优先级并避免阻塞主线程。
这使得 React 应用程序即使在长时间运行的任务(例如渲染大型列表或对复杂场景进行动画处理)期间也能保持响应。
18. 什么是受控组件和非受控组件?
React 中有两种处理表单的主要方法,它们在基本层面上有所不同:数据的管理方式。
非受控组件:在非受控组件中,表单数据由 DOM 本身处理,React 不通过状态控制输入值。
输入值由 DOM 管理,通常在需要时使用 ref 来访问输入值。
当您想要将 React 与非 React 代码或库集成,或者当您需要优化大型表单的性能时,不受控制的组件非常有用。
import React, { useRef } from 'react';
const UncontrolledComponent = () => {
const inputRef = useRef(null);
const handleSubmit = (event) => {
event.preventDefault();
console.log('Input value:', inputRef.current.value);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={inputRef} />
<button type="submit">Submit</button>
</form>
);
};This is the uncontrolled components way. The state is stored in the DOM rather than in the component state (notice we used this.curriculum to access the uploaded file, and have not touched the state.
受控组件:表单数据由 React 组件(而不是 DOM)处理,方法是将输入值存储在状态中,并在输入更改时更新状态。
输入值由 React 状态控制,输入的更改通过事件处理程序进行处理,从而更新状态。
当组件管理的表单字段中的元素状态发生变化时,我们使用 onChange 属性来跟踪它。
import React, { useState } from 'react';
const ControlledComponent = () => {
const [value, setValue] = useState('');
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<input
type="text"
value={value}
onChange={handleChange}
/>
);
};
19. 如何用动态键名设置状态?
要在 React 中使用动态键名称设置状态,可以在 ES6 中使用计算属性名称。计算属性名称允许您使用表达式来指定对象文字中的属性名称。以下是实现这一目标的方法:
import React, { useState } from 'react';
const UserRegistrationForm = () => {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
password: '',
});
const handleChange = async (event) => {
const { name, value } = event.target;
setFormData(prevData => ({ ...prevData, [name]: value }));
// Dynamic key name using computed property name
};
return (
<div>
<h2>User Registration</h2>
<form onSubmit={handleSubmit}>
<div>
<input type="text" name="firstName" value={formData.firstName} onChange={handleChange} />
</div>
<div>
<input type="text" name="lastName" value={formData.lastName} onChange={handleChange} />
</div>
<div>
<input type="email" name="email" value={formData.email} onChange={handleChange} />
</div>
<div>
<input type="password" name="password" value={formData.password} onChange={handleChange} />
</div>
<button type="submit">Submit</button>
</form>
</div>
);
};
export default UserRegistrationForm;
20. 如何在 React 中对 props 应用验证?
在 React 中,您可以使用 PropTypes 或 TypeScript 对 props 应用验证。PropTypes 是 React 提供的一种运行时类型检查机制,用于确保传递给组件的 props 满足特定条件。以下是如何使用 PropTypes 对 props 应用验证:
使用 PropTypes:
import React from 'react';
import PropTypes from 'prop-types';
const MyComponent = ({ name, age }) => {
return (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
};
MyComponent.propTypes = {
name: PropTypes.string.isRequired, // Require a string prop
age: PropTypes.number.isRequired, // Require a number prop
};
export default MyComponent;
使用 TypeScript:如果您使用 TypeScript,您可以为 props 定义接口并直接指定类型。TypeScript 将在编译时检查类型,提供静态类型检查。
import React from 'react';
interface MyComponentProps {
name?: string;
age?: number;
}
const MyComponent: React.FC<MyComponentProps> = ({ name = '', age = 18}) => {
return (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
};
export default MyComponent;
21. 什么是儿童道具?
React 中的 Children 属性是一个特殊的属性,它允许您将子组件或元素传递给父组件。这使您可以创建灵活的、可重用的组件,并可以使用任何内容进行自定义。
例如,您可以创建一个接受 Children 属性的 Button 组件。这将允许您将任何文本或其他组件传递给 Button 组件,并将它们呈现在按钮内。
以下是接受 Children 属性的 Button 组件的示例:
const Button = (props) => {
return (
<button onClick={props.onClick}>
{props.children}
</button>
);
};
使用 Button 组件时,放置在 Button 的开始和结束标记之间的任何内容都将作为 Children 属性传递。
<Button onClick={() => alert('Hello world!')}>
Click me!
</Button>
这将呈现一个带有文本“Click me!”的按钮。在它里面。单击该按钮时,它将调用 onClick 函数,该函数会警告消息“Hello world!”。
22.什么是渲染道具?
Render props 是 React 中的一种模式,其中组件的 render 方法返回一个函数,并且该函数作为 prop 传递给子组件。该函数通常称为“render prop”,负责渲染组件的内容。
import React from 'react';
// Parent component that provides data to its children using a render prop
class DataProvider extends React.Component {
state = {
data: ['apple', 'banana', 'cherry'],
};
render() {
return this.props.children(this.state.data);
}
}
// Child component that consumes data from the parent using a render prop
const DataConsumer = ({ data }) => (
<ul>
{data.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
// Usage of the DataProvider and DataConsumer components
const App = () => (
<div>
<h1>Fruits List:</h1>
<DataProvider>
{(data) => <DataConsumer data={data} />}
</DataProvider>
</div>
);
export default App;
Render props 提供了一种在 React 组件之间以灵活且可重用的方式共享代码和行为的方法。render prop 以一个函数作为参数,负责渲染组件的 UI。
该函数可用于渲染任何类型的 UI,包括其他 React 组件。
const MyComponent = ({ render }) => {
return render();
};
const MyOtherComponent = () => {
const display = () => (
<div>
<h1>This is my component!</h1>
</div>
)
return (
<MyComponent render={display} />
);
};
// This will render the following HTML:
// <h1>This is my component!</h1>23. How to make an AJAX call and in which component lifecycle methods should I make an AJAX call?
在此示例中,MyComponent 组件采用 render prop 作为参数。然后,MyOtherComponent 组件将一个函数传递给 render prop,该 prop 负责渲染组件的 UI。
23. 如何进行 AJAX 调用以及应该在哪些组件生命周期方法中进行 AJAX 调用?
在 React 中,您可以使用各种方法和库(例如 fetch、Axios 或本机 XMLHttpRequest)进行 AJAX 调用(也称为数据获取)。
组件挂载:首次挂载组件时可以进行AJAX调用。这通常在类组件的 componentDidMount 生命周期方法中完成,或者在函数组件的带有空依赖数组 ([]) 的 useEffect 挂钩中完成。这可确保在首次呈现组件时进行一次 AJAX 调用。
import React, { useEffect } from 'react';
import axios from 'axios';
const MyComponent = () => {
useEffect(() => {
axios.get('https://api.example.com/data')
.then(response => {
// Handle successful response
})
.catch(error => {
// Handle error
});
}, []);
return <div>My Component</div>;
};
export default MyComponent;
组件卸载:如果需要在组件卸载时取消 AJAX 请求或执行清理,可以在类组件的 componentWillUnmount 生命周期方法中或在功能组件的 useEffect 钩子返回的清理函数中执行此操作。
useEffect(() => {
// Make AJAX call
return () => {
// Cancel AJAX requests or perform cleanup
};
}, []);
24. 什么是 React Hook?有哪些重要的钩子?
React Hooks 是使功能组件能够使用 React 中的状态和生命周期功能的函数。它们在 React 16.8 中引入,是为了解决功能组件中的状态管理和副作用问题,允许开发人员在不编写类的情况下使用状态和其他 React 功能。
以下是一些重要的 React Hook:
使用状态
使用效果
使用备忘录
使用回调
使用引用
使用Reducer
使用上下文
使用布局效果
自定义 Hooks:https://shorturl.at/eo346(Nextjs-React 项目的自定义 Hooks 集合)
25. React 中的错误边界是什么?
错误边界的工作方式类似于 JavaScript catch {} 块,但适用于组件。只有类组件可以是错误边界。
错误边界是 React 组件,它可以捕获子组件树中任何位置的 JavaScript 错误,记录这些错误,并显示后备 UI,而不是崩溃的组件树。
错误边界会在渲染期间、生命周期方法以及其下方的整个树的构造函数中捕获错误。
错误边界无法捕获自身内部的错误。
如果类组件定义了生命周期方法 static getDerivedStateFromError() 或 componentDidCatch() 中的一个(或两个),则该类组件将成为错误边界。使用 static getDerivedStateFromError() 在引发错误后呈现后备 UI。
使用 componentDidCatch() 来记录错误信息。
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = {
hasError: false,
error: null,
errorInfo: null
};
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
// You can also log the error to an error reporting service
console.error('Error:', error);
console.error('Error Info:', errorInfo);
}
render() {
if (this.state.hasError) {
// Fallback UI for when an error occurs
return <h1>Something went wrong.</h1>;
}
return this.props.children; // Render children normally
}
}
export default ErrorBoundary;
要使用错误边界组件,您可以将其包裹在您想要被错误边界覆盖的组件周围:
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import MyComponent from './MyComponent';
const App = () => (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
export default App;
26.react-dom包有什么用?
React DOM 是一个 JavaScript 库,用于将 React 组件渲染到浏览器的文档对象模型 (DOM)。它提供了许多与 DOM 交互的方法,例如创建元素、更新属性和删除元素。
React DOM 与 React 结合使用来构建用户界面。React 使用虚拟 DOM 来跟踪 UI 的状态,React DOM 负责更新真实 DOM 以匹配虚拟 DOM。
React DOM 是一个易于使用的轻量级库。它提供了许多功能,可以轻松创建和维护复杂的 UI。
27.如何在React中使用装饰器?
在 React 中,装饰器是包装组件以提供附加功能的高阶函数。虽然 JavaScript 本身不支持装饰器,但它们可以与 Babel 等库一起使用来增强 React 组件。
装饰器是 React 中的一项强大功能,它允许您向组件添加功能,而无需修改其代码。这对于添加日志记录、性能跟踪或要应用于多个组件的其他功能非常有用。
要在 React 中使用装饰器,首先需要安装 babel-plugin-transform-decorators-legacy 包。安装该软件包后,您需要将 .babelrc 文件添加到项目根目录中。.babelrc 文件应包含以下代码:
{
"plugins": ["babel-plugin-transform-decorators-legacy"]
}
添加 .babelrc 文件后,您需要更新 tsconfig.json 文件以启用实验性装饰器。为此,请将以下行添加到 tsconfig.json 文件中:
"experimentalDecorators": true
启用实验性装饰器后,您就可以开始在 React 组件中使用它们。要使用装饰器,只需将其放在组件类定义之前即可。
例如,以下代码演示了如何使用装饰器在渲染 React 组件时记录该组件的名称:
import React from "react";
function logComponent(Component) {
return class extends React.Component {
render() {
console.log(Component.name);
return <Component {...this.props} />;
}
};
}
@logComponent
class MyComponent extends React.Component {
render() {
return <div>Hello, world!</div>;
}
}
export default MyComponent;
当您渲染 MyComponent 组件时, logComponent 装饰器会将组件的名称记录到控制台。这对于调试或跟踪组件的性能很有用。
28. 是否可以在不调用 setState 的情况下强制组件重新渲染?
是的,您可以使用React提供的forceUpdate方法强制组件重新渲染,而无需调用setState。
forceUpdate 方法会导致组件重新渲染,就好像其状态或 props 已更改,即使它们实际上并未更改。
import React from 'react';
class MyComponent extends React.Component {
render() {
return (
<div>
<p>Current time: {new Date().toLocaleTimeString()}</p>
<button onClick={this.forceUpdateHandler}>Force Update</button>
</div>
);
}
forceUpdateHandler = () => {
// Call forceUpdate to force the component to re-render
this.forceUpdate();
};
}
export default MyComponent;
forceUpdate方法是React中类组件特有的,不能在函数式组件中使用。功能组件没有实例,因此没有像forceUpdate 这样的实例方法可供它们使用。
29. 什么是反应门户?
React Portal 是 React JavaScript 库中的一项功能,允许您在正常组件层次结构之外渲染组件。
它提供了一种将组件的内容渲染到 DOM(文档对象模型)树的不同部分(通常位于其父组件之外)的方法。
React Portal 还确保门户组件内的事件和状态更新按预期工作,即使该组件在其父级 DOM 层次结构之外呈现也是如此。
当您需要在 DOM 中的不同位置渲染组件的内容时(例如创建模式对话框、工具提示或弹出窗口时),这非常有用。
要使用 React Portal,您需要使用 ReactDOM.createPortal() 方法创建一个门户容器。该方法需要两个参数:要渲染的内容和要渲染内容的 DOM 元素。
import React, { useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
const Modal = ({ children }) => {
const [modalContainer, setModalContainer] = useState(null);
useEffect(() => {
const container = document.createElement('div');
document.body.appendChild(container);
setModalContainer(container);
return () => {
document.body.removeChild(container);
};
}, []);
if (!modalContainer) {
return null;
}
return ReactDOM.createPortal(
<div className="modal">
<div className="modal-content">
{children}
</div>
</div>,
modalContainer
);
};
const App = () => {
const [showModal, setShowModal] = useState(false);
const toggleModal = () => {
setShowModal(!showModal);
};
return (
<div>
<h1>React Portal Example</h1>
<button onClick={toggleModal}>Toggle Modal</button>
{showModal && (
<Modal>
<p>This is a modal dialog rendered using React Portal.</p>
</Modal>
)}
</div>
);
};
export default App;
30. 如何在页面加载时将输入元素聚焦?
您可以通过使用 JSX 中的 autoFocus 属性或通过以编程方式将输入元素集中在功能组件中的 useEffect 挂钩或类组件中的 componentDidMount 生命周期方法中,将输入元素集中在页面加载上。
使用自动对焦属性:
import React from 'react';
const MyComponent = () => {
return <input type="text" autoFocus />;
};
export default MyComponent;
使用引用对象:
import React, { useEffect, useRef } from 'react';
const MyComponent = () => {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current.focus();
}, []);
return <input type="text" ref={inputRef} />;
};
export default MyComponent;
31. React 中的服务器端渲染如何工作?
服务器端渲染(SSR)是一种在将 React 应用程序发送到客户端之前在服务器上渲染它们的技术。
SSR 可以通过减少客户端需要下载和执行的 JavaScript 量来提高性能。SSR 还可以通过使搜索引擎更轻松地索引您的 React 应用程序来提高 SEO。
以下是 React 中服务器端渲染工作原理的高级概述:
初始请求:当用户向服务器发出页面请求时,服务器接收该请求并开始处理它。
组件渲染:服务器识别需要为请求的页面渲染的 React 组件。然后,它使用服务器端渲染引擎(例如 ReactDOMServer)将这些组件渲染为 HTML。
数据获取:如果组件需要来自 API 或数据库的数据,服务器会获取该数据并在渲染过程中将其传递给组件。
HTML 生成:渲染组件并获取任何必要的数据后,服务器会生成页面的完整 HTML 表示形式,包括应用程序的初始状态。
向客户端发送 HTML:服务器将生成的 HTML 发送回客户端作为对初始请求的响应。
客户端水合:当客户端收到 HTML 时,它还会下载包含 React 代码的 JavaScript 包。
然后,客户端 JavaScript 会“水化” HTML,附加事件侦听器并重新建立任何客户端状态,使页面具有交互性。
32. 优化 React App 有哪些不同的方法?
随着 ReactJS 应用程序复杂性和用户群的增长,扩展 ReactJS 应用程序需要优化其性能、可维护性和可扩展性。以下是 ReactJS 中应用程序优化和扩展的一些技术:
a) 代码分割/延迟加载/动态导入:
代码拆分涉及将 JavaScript 包分解为更小、更易于管理的块。您可以根据不同的路由、组件或其他逻辑划分将其拆分为单独的文件,而不是一次性将整个应用程序代码发送到客户端。
这允许您仅加载当前视图所需的代码,从而减少初始加载时间并提高性能。
延迟加载是一种在初始页面加载时推迟非关键资源加载的策略。通过延迟加载,组件、图像或其他资源仅在实际需要时才从服务器获取。
React.lazy 和 Suspense 形成了延迟加载依赖项并仅在需要时加载的完美方式。
Suspense 是一个可用于包装任何延迟加载组件的组件。使用其后备属性来输出一些 JSX 或组件输出。
import React from 'react'
import { BrowserRouter, Routes, Route } from 'react-router-dom'
const TodoList = React.lazy(() => import('./routes/TodoList'))
const NewTodo = React.lazy(() => import('./routes/NewTodo'))
const App = () => (
<BrowserRouter>
<React.Suspense fallback={<p>Please wait</p>}>
<Routes>
<Route exact path="/" element={<TodoList/>} />
<Route path="/new" element={<NewTodo/>} />
</Routes>
</React.Suspense>
</BrowserRouter>
)
动态导入是 JavaScript 的一项功能,允许您在运行时异步导入模块。这意味着您可以按需加载模块,而不是在应用程序的初始加载时加载。
动态导入通常与代码分割和延迟加载结合使用,以仅在需要时加载特定的模块或组件。
import React, { useState, useEffect } from 'react';
const LazyLoadedComponent = () => {
const [module, setModule] = useState(null);
useEffect(() => {
const loadModule = async () => {
const dynamicModule = await import('./DynamicModule');
setModule(dynamicModule);
};
loadModule();
}, []);
if (!module) {
return <div>Loading...</div>;
}
const DynamicModule = module.default;
return <DynamicModule />;
};
export default LazyLoadedComponent;
在 ReactJS 应用程序中,您可以使用 Webpack 等工具实现代码分割、延迟加载和动态导入,Webpack 为这些功能提供内置支持。
例如,您可以使用动态 import() 语句异步加载模块,Webpack 会自动拆分代码并为动态导入的模块生成单独的包。
b) 服务器端渲染(SSR):如前所述,SSR 可以通过在服务器上渲染初始 HTML 来改善初始加载时间和 SEO。这对于大规模应用特别有利。
c) 优化捆绑包大小:密切关注捆绑包大小,并通过删除未使用的依赖项、使用树摇动和最小化大型库的使用来优化它。
d) React.memo() 或 PureComponent:React.memo() 和 PureComponent 都是 React 中的性能优化技术,可以帮助防止不必要的组件重新渲染。(检查第 6 题)
e) 使用 React.Fragments 或 <> </> 它可以让您对子列表进行分组,而无需添加额外的节点并避免额外的 HTML 元素包装器。
class Comments extends React.PureComponent{
render() {
return (
<>
<h1>Comment Title</h1>
<p>comments</p>
<p>comment time</p>
</>
);
}
}
f) 节流和去抖动事件操作:这两种技术都用于控制调用函数的速率。它们可用于减少用户事件触发的 API 调用数量并提高应用程序性能,而不会影响用户体验。
去抖动会延迟代码的执行,直到用户在指定的时间内停止执行特定操作。它导致函数在再次运行之前等待一定时间。这限制了调用函数的速率。
限制可确保函数以指定的时间间隔执行,并且该时间间隔内的其他调用将被忽略。通过限制,您可以限制函数调用的频率。例如,您可能决定最多每 1500 毫秒执行一次函数。
g) useMemo() 和 useCallback():这两个钩子都可以通过减少组件需要重新渲染或记住组件或昂贵操作的结果的次数来帮助优化 React 组件。
h) 使用 Web Workers 执行 CPU 大量任务:Web Workers 可以在 Web 应用程序的后台线程中运行脚本操作,与主执行线程分开。通过在单独的线程中执行繁重的处理,主线程(通常是 UI)能够运行而不会被阻塞或减慢。
i) 虚拟化长列表:列表虚拟化或窗口化是一种在渲染长数据列表时提高性能的技术。
该技术在任何给定时间仅渲染一小部分行,并且可以显着减少重新渲染组件所需的时间以及创建的 DOM 节点的数量。React 库是react-window 和react-virtualized。
j) 分析和优化您的 Webpack 捆绑包膨胀:在生产部署之前,您应该检查并分析您的应用程序捆绑包以删除不需要的插件或模块。
您可以考虑使用 Webpack Bundle Analyzer,它允许您使用交互式可缩放树形图来可视化 Webpack 输出文件的大小。
33.如何保证react应用程序的安全以及react中哪些是受保护的路由?
保护 React 应用程序涉及实施各种措施来保护其免受常见安全威胁和漏洞的影响。以下是确保 React 应用程序安全的一些最佳实践:
身份验证:使用 OAuth 2.0 或 OpenID Connect 等行业标准协议实施用户身份验证。使用 jsonwebtoken 等库或 Firebase Authentication 等身份验证服务来安全地处理身份验证。
授权:用户通过身份验证后,强制执行访问控制和授权规则,以根据用户角色和权限限制对应用程序某些部分的访问。根据需要实施基于角色的访问控制 (RBAC) 或基于属性的访问控制 (ABAC)。
HTTPS:确保您的应用程序通过 HTTPS 提供服务,以加密客户端和服务器之间传输的数据。这有助于防止各种攻击,例如中间人攻击,并确保数据隐私和完整性。
输入验证:清理和验证用户输入,以防止跨站点脚本 (XSS) 和 SQL 注入攻击等常见安全漏洞。使用验证器等库进行输入验证,并在用户输入呈现在 UI 中或在服务器上处理它们之前对其进行清理。
安全通信:使用 TLS/SSL 等安全通信协议在客户端和服务器之间传输敏感数据。避免通过不安全的渠道以纯文本形式发送敏感信息。
保护敏感数据:避免在客户端代码或本地存储中存储密码或 API 密钥等敏感数据。相反,应将敏感数据安全地存储在服务器上,并使用安全的身份验证机制来访问它。
内容安全策略 (CSP):实施内容安全策略,通过指定加载脚本、样式表和其他资源的可信源来降低 XSS 攻击的风险。使用 Content-Security-Policy 标头为您的应用配置 CSP。
跨站点请求伪造 (CSRF) 保护:实施 CSRF 保护机制,以防止代表经过身份验证的用户执行未经授权的请求。使用 CSRF 令牌或同源策略等技术来减轻 CSRF 攻击。
错误处理和日志记录:实施适当的错误处理和日志记录机制来检测和响应安全事件和异常。监控应用程序日志和用户活动,以识别潜在的安全威胁和漏洞。
React 中的受保护路由是在授予对应用程序中某些页面或组件的访问权限之前需要身份验证或授权的路由。
您可以通过使用高阶组件 (HOC)、渲染道具或上下文提供程序来实现受保护的路由,以检查用户的身份验证状态或权限,并有条件地渲染适当的组件或在需要身份验证时将用户重定向到登录页面。
有几种不同的方法可以在 React 中实现受保护的路由。一种常见的方法是使用 React Router 库。React Router 允许您定义路由并指定哪些用户有权访问每个路由。
import React from 'react';
import { Route, Redirect } from 'react-router-dom';
const ProtectedRoute = ({ component: Component, isAuthenticated, ...rest }) => (
<Route
{...rest}
render={(props) =>
isAuthenticated ? <Component {...props} /> : <Redirect to="/login" />
}
/>
);
export default ProtectedRoute;
在此示例中,ProtectedRoute 组件检查用户是否经过身份验证 (isAuthenticated)。
如果用户通过身份验证,它将呈现指定的组件(作为 prop 传递),否则,它将用户重定向到登录页面。您可以使用此 ProtectedRoute 组件来包装 React 应用程序中需要身份验证的任何路由。
34. React 编码最佳实践是什么?
React 编码最佳实践有助于确保您的代码可读、可维护且高效。以下是编写 React 代码时需要遵循的一些关键最佳实践:
组件组合:将您的 UI 分解为更小的、可重用的组件,每个组件处理一个职责。这促进了代码重用、可维护性和关注点分离。
// Example of component composition
import React from 'react';
const Header = () => <header>Header</header>;
const Sidebar = () => <aside>Sidebar</aside>;
const Content = () => <main>Content</main>;
const App = () => (
<div>
<Header />
<Sidebar />
<Content />
</div>
);
export default App;
单一职责原则 (SRP):每个组件都应具有单一职责,例如呈现 UI、管理状态或处理用户交互。避免创建执行过多操作的组件,因为这可能会导致代码复杂且难以维护。
// Example of a component with single responsibility
import React from 'react';
const UserList = ({ users }) => (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
export default UserList;
使用函数组件:只要有可能,就使用函数组件而不是类组件。函数式组件更简单、更简洁、更容易推理。使用 useState 和 useEffect 等钩子来管理功能组件中的状态和副作用。
// Example of a functional component
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount(count + 1);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default Counter;
避免在渲染方法中使用复杂的 JSX:将复杂的 JSX 结构分解为更小、更易于管理的组件或辅助函数。这提高了可读性并使代码更易于维护。
// Example of breaking down complex JSX into smaller components
import React from 'react';
const UserProfile = ({ user }) => (
<div>
<Avatar avatarUrl={user.avatarUrl} />
<UserInfo name={user.name} email={user.email} />
</div>
);
const Avatar = ({ avatarUrl }) => <img src={avatarUrl} alt="Avatar" />;
const UserInfo = ({ name, email }) => (
<div>
<h2>{name}</h2>
<p>Email: {email}</p>
</div>
);
export default UserProfile;
使用描述性变量名称:使用准确描述变量或组件用途的描述性变量名称。这使您的代码对于其他开发人员来说更具可读性和理解性。
一致的格式和命名约定:在整个代码库中遵循一致的格式和命名约定。这包括缩进、间距、变量和组件的命名以及文件命名约定。一致性提高了代码的可读性,并使其更易于导航和理解。
避免直接状态变更:更新状态时,始终使用 React 提供的函数(例如,类组件中的 setState、功能组件中的 useState hook)以避免直接变更状态。直接状态突变可能会导致不可预测的行为和错误。
// Example of updating state without mutating it directly
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(prevCount => prevCount + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
};
export default Counter;
优化性能:通过最大限度地减少不必要的重新渲染、使用记忆技术(例如 React.memo、useMemo)以及对大型列表或表实施虚拟化来优化性能。
使用 React DevTools 等工具分析您的应用程序,并根据需要解决性能瓶颈。
优雅地处理错误:实施错误边界以捕获和处理组件中的错误。向用户显示信息性错误消息,并将错误记录到控制台或日志服务以进行调试。
// Example of error boundary
import React, { Component } from 'react';
class ErrorBoundary extends Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error:', error);
console.error('Error Info:', errorInfo);
}
render() {
if (this.state.hasError) {
return <div>Something went wrong!</div>;
}
return this.props.children;
}
}
export default ErrorBoundary;
使用 PropTypes 或 TypeScript 进行类型检查:使用 PropTypes 或 TypeScript 为组件和 props 添加类型检查。类型检查有助于及早发现错误并提供更好的代码文档和工具支持。
// Example of using PropTypes for type checking
import React from 'react';
import PropTypes from 'prop-types';
const User = ({ name, age }) => (
<div>
<p>Name: {name}</p>
<p>Age: {age}</p>
</div>
);
User.propTypes = {
name: PropTypes.string.isRequired,
age: PropTypes.number.isRequired
};
export default User;
测试组件:为组件编写测试涉及使用 Jest 和 React 测试库等测试库来确保组件按预期运行。
每个测试用例都会根据组件的功能而有所不同,因此这里没有提供具体的示例代码。
35.如何进行React应用程序的组件级和端到端测试?
要测试 React 应用程序,您可以使用各种测试工具和技术。以下是测试 React 应用程序的一些常用方法:
单元测试:使用 Jest 等测试框架以及 Enzyme 或 React 测试库等工具为各个组件编写单元测试。这些测试可以单独检查每个组件的渲染、行为和状态。
让我们使用 Jest 和 React 测试库为此 Button 组件编写一些单元测试用例。
// Button.js
import React from 'react';
const Button = ({ label, onClick }) => {
return (
<button onClick={onClick}>{label}</button>
);
}
export default Button;
// button.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';
test('renders button with correct label', () => {
const { getByText } = render(<Button label="Click me" />);
const buttonElement = getByText('Click me');
expect(buttonElement).toBeInTheDocument();
});
test('triggers onClick function when button is clicked', () => {
const onClickMock = jest.fn();
const { getByText } = render(<Button label="Click me" onClick={onClickMock} />);
const buttonElement = getByText('Click me');
fireEvent.click(buttonElement);
expect(onClickMock).toHaveBeenCalledTimes(1);
});
集成测试:通过编写集成测试来测试不同组件如何协同工作。您可以使用 Jest 和 React 测试库等工具来模拟用户交互并测试应用程序的整体行为。
// Counter.js
import React from 'react';
const Counter = ({ count }) => {
return <p>Count: {count}</p>;
}
export default Counter;
// Button.js
import React from 'react';
const Button = ({ onClick }) => {
return <button onClick={onClick}>Increment</button>;
}
export default Button;
// testIntegration.test.js
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Counter from './Counter';
import Button from './Button';
test('increments count when button is clicked', () => {
const { getByText } = render(
<div>
<Counter count={0} />
<Button />
</div>
);
const countElement = getByText('Count: 0');
const buttonElement = getByText('Increment');
fireEvent.click(buttonElement);
expect(countElement).toHaveTextContent('Count: 1');
});
在此测试用例中,我们将 Counter 和 Button 组件一起渲染在父组件中。然后,我们使用 React 测试库中的 getByText 函数来获取我们想要交互的元素。之后,我们使用 fireEvent.click 模拟按钮上的单击事件,并断言 Counter 组件中显示的计数已增加。
此集成测试用例确保 Counter 和 Button 组件按预期协同工作,并可以作为测试 React 应用程序中组件之间更复杂交互的起点。
端到端测试:使用 Cypress 或 Selenium 等工具编写端到端测试,模拟用户在真实浏览器环境中与应用程序的交互。这些测试可以帮助您发现不同组件和服务交互时可能出现的问题。
// Form.js
import React, { useState } from 'react';
const Form = ({ onSubmit }) => {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const handleSubmit = (e) => {
e.preventDefault();
onSubmit({ name, email, message });
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Name"
/>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
/>
<textarea
value={message}
onChange={(e) => setMessage(e.target.value)}
placeholder="Message"
/>
<button type="submit">Submit</button>
</form>
);
};
export default Form;
// SubmitButton.js
import React from 'react';
const SubmitButton = ({ onSubmit }) => {
return <button onClick={onSubmit}>Submit</button>;
};
export default SubmitButton;
现在,让我们使用 Jest 和 React 测试库为这些组件编写一个集成测试用例。
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import Form from './Form';
import SubmitButton from './SubmitButton';
test('submits form data when submit button is clicked', () => {
const handleSubmit = jest.fn();
const { getByPlaceholderText, getByText } = render(
<div>
<Form onSubmit={handleSubmit} />
<SubmitButton onSubmit={handleSubmit} />
</div>
);
const nameInput = getByPlaceholderText('Name');
const emailInput = getByPlaceholderText('Email');
const messageInput = getByPlaceholderText('Message');
const submitButton = getByText('Submit');
fireEvent.change(nameInput, { target: { value: 'John Doe' } });
fireEvent.change(emailInput, { target: { value: 'john@example.com' } });
fireEvent.change(messageInput, { target: { value: 'Hello, this is a test message' } });
fireEvent.click(submitButton);
expect(handleSubmit).toHaveBeenCalledWith({
name: 'John Doe',
email: 'john@example.com',
message: 'Hello, this is a test message'
});
});
在此测试用例中,我们将 Form 和 SubmitButton 组件一起呈现在父组件中。然后,我们使用 React 测试库中的 getByPlaceholderText 和 getByText 函数来获取输入元素和提交按钮。
之后,我们使用 fireEvent.change 模拟输入字段中的更改,并使用 fireEvent.click 模拟提交按钮上的单击事件。最后,我们断言使用正确的表单数据调用了handleSubmit 函数。
快照测试:快照测试是一种捕获组件输出“快照”并将其与先前存储的快照进行比较的方法。
使用 Jest 创建和维护组件输出的快照。这使您可以轻松检测 UI 随着时间的推移发生的意外变化。
// Button.js
import React from 'react';
const Button = ({ label, onClick }) => {
return <button onClick={onClick}>{label}</button>;
};
export default Button;
// Button.test.js
import React from 'react';
import { render } from '@testing-library/react';
import Button from './Button';
test('matches snapshot', () => {
const { asFragment } = render(<Button label="Click me" />);
expect(asFragment()).toMatchSnapshot();
});
在此测试中,我们使用 React 测试库中的渲染函数来渲染带有标签“Click me”的 Button 组件。然后,我们使用 asFragment 方法将组件的渲染输出作为快照检索,并使用 toMatchSnapshot 将其与存储的快照进行比较。
当您第一次运行此测试时,它将创建一个快照文件(例如 Button.test.js.snap),其中包含 Button 组件的渲染输出。在后续测试运行中,它将当前输出与存储的快照进行比较,如果存在任何差异,则测试失败。
模拟:使用 Jest 等工具来模拟外部依赖项(例如 API 调用),以隔离您正在测试的代码并使您的测试更具可预测性。
// UserList.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const UserList = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
const fetchUsers = async () => {
try {
const response = await axios.get('https://api.example.com/users');
setUsers(response.data);
} catch (error) {
console.error('Error fetching users', error);
}
};
fetchUsers();
}, []);
return (
<div>
<h1>User List</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
};
export default UserList;
// UserList.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import UserList from './UserList';
import axios from 'axios';
jest.mock('axios');
test('renders list of users', async () => {
const mockUsers = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
];
axios.get.mockResolvedValue({ data: mockUsers });
render(<UserList />);
// You can use screen.getByText, screen.getByRole, etc. to assert the presence of the user list in the rendered component
expect(await screen.findByText('Alice')).toBeInTheDocument();
expect(await screen.findByText('Bob')).toBeInTheDocument();
});
在此测试中,我们使用 jest.mock 来模拟 axios.get 函数,并为模拟的 API 调用提供解析值。
然后,我们渲染 UserList 组件并使用断言来验证用户列表是否根据模拟的 API 响应正确渲染。
36. React 使用的不同 npm 模块有哪些?
以下是有用的 npm 模块列表:
react-router-dom
redux
redux-thunk
formik
yup
framer-motion
react-bootstrap
styled-component
ESLint
react-i18next
redux-from
axios
react-testing-library
jest
37. React 有哪些新功能?
React 18 推出了一些关键更新和新功能。React 18 专注于提高 React 应用程序的性能和并发渲染功能。
自动批处理:
React 18 引入了一个新的自动批处理功能,该功能将状态更新分组在一起并一次性渲染它们。这可以通过减少 DOM 更新次数来提高性能。通过这样做,可以避免不必要的重新渲染。
function App() { const [count, setCount
] = useState(0);
const [alert, setAlert] = useState(false);
const handleClick =()=>{
setCount(count => count + 1);
setAlert(alert => !alert);
// React re-renders once at the end. This is batching!
}
return (
<div>
<button onClick={handleClick}>Increment</button>
<h1 classname={`${alert ? "bg-blue " : "bg-black"}`}>{count}</h1>
</div>
);
}
但是,也有一些例外,例如在处理事件后更新状态。例如,如果您需要获取数据,然后更新上面事件处理程序handleClick中的状态,React不会批量更新,而是独立执行。
function App() {
const [count, setCount] = useState(0);
const [alert, setAlert] = useState(false);
const handleClick =()=>{
fetch().then(() => {
setCount(count => count + 1); // React re-renders this!
setAlert(alert => !alert); // React re-renders this!
});
}
return (
<div>
<button onClick={handleClick}>Increment</button>
<h1 classname={`${alert ? "bg-blue " : "bg-black"}`}>{count}</h1>
</div>
);
}
并发反应:
React 18还引入了一种新的并发模式,允许React同时处理多个任务。这可以通过使 React 更好地响应用户输入来提高性能。
它帮助 React 根据不同任务的重要性和紧急程度确定更新和渲染的优先级,确保高优先级更新得到更快的处理。
在React的早期版本中,一旦渲染开始,就不能中断,直到完成。
在 React 18 中,React 可以中断、暂停或恢复渲染。它甚至可以放弃它以快速响应用户交互。
手头的任务有多大并不重要;重要的是。当有更紧急的任务时,React 会将其视为优先事项。
Suspense:
React 18 还引入了一个新的Suspense功能,允许 React 延迟渲染组件,直到其数据可用。这可以防止 React 在等待数据时呈现空白屏幕,从而改善用户体验。
服务器组件:
React 18 还引入了一个新的服务器组件功能,允许 React 在服务器上渲染组件并将它们流式传输到客户端。这可以通过减少客户端需要下载的 JavaScript 量来提高性能。
新的客户端和服务器渲染 API:
React 18 还引入了新的客户端和服务器渲染 API,使在客户端和服务器上渲染 React 组件变得更加容易。
服务器渲染,也称为服务器端渲染 (SSR),是一种 Web 开发技术,其中 Web 服务器处理网页请求并生成初始 HTML 内容。
该 HTML 被发送到用户的浏览器,然后浏览器可以更快地呈现页面,因为它已经有一些要显示的内容。
过渡:
React 18 还引入了一个新的过渡功能,允许 React 以动画方式对 UI 进行更改。这可以使 UI 的更改显得更流畅,从而改善用户体验。
新的严格模式行为:
在 React 18 中,严格模式将确保组件不会因为反复安装和卸载而受到影响。这是什么意思?例如,当用户离开屏幕并返回时,应立即看到上一个屏幕。
正常流程如下:
当用户第一次到达屏幕时,React 会挂载该组件
当用户离开屏幕时,React 会卸载组件
当用户返回屏幕时,React 会再次安装该组件。
新钩子:
React 18 引入了新的hook,例如
useId()
useTransition()
useDeferredValue()
useSyncExternalStore()
useInsertionEffect()
38.ReactJS 设计模式是什么?
ReactJS 设计模式是针对 React 开发中常见问题的可重用解决方案。它们为开发人员在构建 React 应用程序时提供了一个框架,有助于提高代码质量、可读性和可维护性。
以下是一些最流行的 ReactJS 设计模式:
容器组件模式:也称为智能哑组件模式,此模式将容器组件(管理状态和逻辑的智能组件)与表示组件(专注于呈现 UI 的哑组件)分开。这种分离通过保持关注点分离来提高可重用性和可维护性。
高阶组件 (HOC):HOC 是接受组件作为参数并返回具有增强功能的新组件的函数。
它们通过使用附加功能包装组件来实现代码重用、横切关注点和行为组合。示例包括身份验证 HOC、数据获取 HOC 和记忆 HOC。
渲染道具:渲染道具是一种模式,其中组件的渲染方法返回一个函数(渲染道具),该函数为子组件提供数据或行为。
它通过 props 传递数据和函数来实现组件组合和代码共享。示例包括数据获取组件、可重用逻辑组件和上下文提供程序。
Context API:Context API 允许组件共享全局状态,而无需手动通过组件树传递 props。它提供了一种通过组件树传递数据的方法,而无需在每个级别显式传递 props。上下文对于管理应用程序范围的状态、主题配置和用户首选项很有用。
复合组件:复合组件是一种模式,其中一组组件一起工作以形成更高级别的组件。组中的每个组件都维护自己的状态和行为,但它们一起工作以实现共同的目标。示例包括选项卡式界面、折叠式菜单和表单控件。
状态管理模式:React 应用程序通常使用不同的状态管理模式(例如 Redux、MobX 或 Context API)来管理复杂的状态和数据流。这些模式提供集中的状态管理、可预测的数据流和关注点分离,使得在大型应用程序中管理应用程序状态变得更加容易。
不可变数据模式:不可变数据模式鼓励使用不可变数据结构和函数式编程原则来管理 React 应用程序中的状态更新。Immutable.js 和 Immer 等库提供了用于创建和更新不可变数据结构、提高性能并减少状态管理中的错误的实用程序。
错误边界模式:错误边界是在其子组件树中的任何位置捕获 JavaScript 错误并显示回退 UI 而不是使整个应用程序崩溃的组件。它们提供了一种优雅地处理错误并防止错误在组件树上传播的方法,从而提高了 React 应用程序的稳定性和可靠性。
StateReducer:StateReducer模式是一种在React应用程序中管理状态的方法。它使用减速器函数根据操作更新状态。此模式通常与 Redux(React 的状态管理库)结合使用。
Prop Drilling:Prop Drilling 是一种通过组件树向下传递数据的技术。当在彼此不直接相关的组件之间共享数据时,这可能是必要的。然而,道具钻探会使代码难以阅读和维护,因此应谨慎使用。
必须注意的是,React 设计模式不仅限于这些模式,您还可以实现多种不同的设计模式。
39. 什么是nextjs,如何创建nextjs应用程序以及它与reactjs有何不同?
React 是一个用于构建用户界面的库。它是声明性的、高效的、灵活的。Next.js 是一个构建在 React 之上的框架,并提供服务器端渲染、静态站点生成和自动路由等附加功能。
运行以下命令来创建 NextJS 应用程序:
npx create-next-app@latest testNextApp
40. 如何构建 ReactJS 应用程序?
构建 ReactJS 应用程序涉及设计结构和组织组件、状态管理、路由、数据获取以及应用程序的其他方面,以实现可维护性、可扩展性和性能。以下是如何构建 ReactJS 应用程序的高级概述:
项目结构:
逻辑地组织您的项目结构,将相关文件和文件夹分组在一起。
考虑使用带有基于功能的文件夹的模块化架构,其中每个功能或模块都有自己的文件夹,其中包含组件、样式、测试和其他相关文件。
分离关注点并在表示组件(UI)和容器组件(业务逻辑)之间保持清晰的分离。
组件设计:
将您的 UI 分解为更小的、可重用的组件,每个组件处理一个职责。
遵循组件组合原则,即较大的组件由较小的组件组成,从而促进代码重用和可维护性。
尽可能使用带有钩子的功能组件来管理状态和副作用,因为它们更简单、更简洁。
状态管理:
根据应用程序的复杂性和要求选择合适的状态管理解决方案。
对于更简单的应用程序,请使用带有 useState 和 useEffect 挂钩的本地组件状态。
对于具有共享状态或全局状态的复杂应用程序,请考虑使用 Redux、MobX 或 Context API 等库。
遵循管理状态的最佳实践,例如不变性、单一事实来源和关注点分离。
路由:
使用 React Router 或 Reach Router 等库实现客户端路由,以处理应用程序内的导航和路由。
定义路由和路由参数以将 URL 映射到组件并管理不同视图之间的导航。
使用路由防护和嵌套路由来保护路由并管理基于用户身份验证和授权的访问控制。
数据获取:
使用 Axios、fetch 或 GraphQL 客户端等库从外部 API 或来源获取数据。
使用 useEffect 钩子在组件渲染后执行数据获取和副作用。
实施加载、错误处理和缓存策略来处理异步数据获取并改善用户体验。
造型:
选择最适合您的项目要求的样式方法,例如 CSS、Sass、CSS 模块、样式组件或 Tailwind CSS。
通过使用基于组件的样式技术,保持样式的模块化、范围化和可维护性。
使用 CSS 框架或设计系统来保持组件和视图之间的一致性并简化样式。
测试:
编写单元测试、集成测试和端到端测试,以确保 React 组件和应用程序的可靠性和功能性。
使用 Jest、React 测试库、Enzyme 或 Cypress 等测试库来编写和运行测试。
遵循测试 React 组件的最佳实践,例如关注用户交互、测试边缘情况和模拟依赖项。
优化:
通过最小化捆绑包大小、减少渲染时间和提高整体应用程序性能来优化性能。
实现代码拆分、延迟加载和树摇动,以减少初始加载时间并提高页面加载性能。
使用 Chrome DevTools、Lighthouse 或 WebPageTest 等性能监控工具来分析和分析您的应用程序,并相应地优化性能瓶颈。
辅助功能:
通过遵循 Web 可访问性标准 (WCAG) 和指南来确保可访问性,以使残疾人可以使用您的应用程序。
使用语义 HTML 元素,为图像提供替代文本,并确保键盘导航和屏幕阅读器兼容性。
使用 Axe、Lighthouse 或屏幕阅读器等工具测试应用程序的可访问性,以识别和修复可访问性问题。
部署:
选择用于部署 React 应用程序的部署策略和平台,例如 Netlify、Vercel、AWS 或 Heroku 等托管提供商。
设置持续集成和持续部署 (CI/CD) 管道以自动化部署流程并确保部署顺利可靠。
配置生产部署的环境变量、安全设置和性能优化。
通过遵循这些架构原则和最佳实践,您可以设计和架构一个结构良好、可扩展且可维护的 ReactJS 应用程序,以满足您的项目和用户的需求。
学习更多技能
请点击下方公众号