前端面试题大汇总:React 篇
基础知识
1. 什么是 React?它的主要特点是什么?
React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发并维护。它主要用于构建单页应用程序(SPA)和复杂的用户界面。React 的主要特点包括:
- 组件化:React 将 UI 分解成独立的、可重用的组件。每个组件都有自己的逻辑和控制。
- 虚拟 DOM:React 使用虚拟 DOM 来提高性能。虚拟 DOM 是一个内存中的树结构,React 会先在虚拟 DOM 中进行操作,然后批量更新真实 DOM。
- 声明式编程:React 采用声明式编程风格,开发者只需描述 UI 应该是什么样的,React 会负责处理 UI 的变化。
- JSX:React 使用 JSX(JavaScript XML)语法,允许在 JavaScript 中编写类似 HTML 的标记。
- 生态系统丰富:React 拥有丰富的生态系统,包括路由器(React Router)、状态管理库(Redux、MobX)等。
2. 什么是 JSX?它有什么优点?
JSX 是一种语法扩展,允许在 JavaScript 中编写类似 HTML 的标记。JSX 最终会被编译成普通的 JavaScript 代码。
优点:
- 可读性强:JSX 使得模板代码更加直观和易读,特别是对于复杂的 UI 结构。
- 类型检查:JSX 可以在编译时进行类型检查,减少运行时错误。
- 表达式支持:可以在 JSX 中嵌入 JavaScript 表达式,使得动态生成 UI 变得更加方便。
- 工具支持:现代开发工具(如 Babel)可以将 JSX 编译成兼容所有浏览器的 JavaScript 代码。
3. 什么是虚拟 DOM?它是如何工作的?
虚拟 DOM 是一个轻量级的内存中的 DOM 树表示。React 使用虚拟 DOM 来提高性能,避免频繁的操作真实 DOM。
工作原理:
- 创建虚拟 DOM:React 在内存中创建一个虚拟 DOM 树。
- Diff 算法:当状态改变时,React 会比较新的虚拟 DOM 和旧的虚拟 DOM,找出差异(即最小的变更集合)。
- 批量更新:React 将这些差异批量应用到真实 DOM,减少 DOM 操作次数,提高性能。
4. React 中的单向数据流是什么意思?
单向数据流 是 React 的核心设计理念之一。在单向数据流中,数据只能从父组件流向子组件,不能反向流动。这种方式使得数据流更加清晰和可控。
特点:
- 数据一致性:单向数据流确保了数据的一致性,避免了数据的混乱和不可预测的变化。
- 易于调试:数据流的单一方向使得调试更加容易,可以更容易地追踪数据的变化。
- 可预测性:单向数据流使得应用的状态变化更加可预测,便于维护和扩展。
5. 什么是 Props 和 State?它们的区别是什么?
Props(属性):
- 定义:Props 是组件之间传递数据的一种方式。父组件可以通过 props 向子组件传递数据。
- 不可变:Props 是只读的,子组件不能修改传入的 props。
- 用途:用于配置组件的行为和外观。
State(状态):
- 定义:State 是组件内部的状态,用于存储组件的数据。
- 可变:State 是可变的,可以通过
setState
方法更新。 - 用途:用于管理组件的内部状态,控制组件的行为和渲染。
区别:
- 来源:Props 来自父组件,State 是组件自身的状态。
- 可变性:Props 是只读的,State 是可变的。
- 用途:Props 用于配置组件,State 用于管理组件的内部状态。
6. 如何在 React 中创建一个组件?
函数组件:
const MyComponent = (props) => {
return <div>Hello, {props.name}!</div>;
};
类组件:
class MyComponent extends React.Component {
render() {
return <div>Hello, {this.props.name}!</div>;
}
}
7. 什么是函数组件和类组件?它们有什么区别?
函数组件:
- 定义:函数组件是一个简单的 JavaScript 函数,接收 props 作为参数,返回 JSX。
- 优点:代码更简洁,性能更好(因为没有类的开销)。
- 限制:早期版本的函数组件不支持生命周期方法和状态管理,但随着 Hooks 的引入,这些限制已经被解除。
类组件:
- 定义:类组件是继承自
React.Component
的 ES6 类,可以定义生命周期方法和管理状态。 - 优点:支持生命周期方法和状态管理,功能更强大。
- 缺点:代码相对复杂,性能略逊于函数组件。
8. 什么是纯组件?为什么要使用纯组件?
纯组件:
-
定义:纯组件是一种特殊的组件,它通过
React.memo
(函数组件)或PureComponent
(类组件)来实现。纯组件会在 props 或 state 发生变化时进行浅比较,如果前后值相同,则跳过重新渲染。 -
优点:
- 性能优化:减少不必要的重新渲染,提高应用性能。
- 简化逻辑:开发者不需要手动实现
shouldComponentUpdate
方法来优化性能。
使用场景:
- 静态数据:组件的 props 和 state 不经常变化。
- 复杂组件:组件内部逻辑复杂,重新渲染开销大。
9. 什么是 React Context API?它解决了什么问题?
React Context API:
-
定义:Context API 是 React 提供的一种在组件树中传递数据的机制,无需通过 props 逐层传递。
-
使用:
- 创建 Context:使用
React.createContext
创建一个 Context 对象。 - 提供 Context:使用
Context.Provider
组件将数据传递给子组件。 - 消费 Context:使用
Context.Consumer
组件或useContext
Hook 在子组件中访问数据。
- 创建 Context:使用
组件
1. 什么是受控组件和非受控组件?它们的区别是什么?
受控组件(Controlled Components) :
-
定义:受控组件是指那些其输入值由 React 的状态(state)控制的表单组件。每次用户输入时,都会触发一个事件处理器,更新组件的状态,从而更新表单的值。
-
特点:
- 状态管理:表单的值由 React 状态管理。
- 事件处理:每次用户输入时,都会触发事件处理器。
-
示例:
import React, { useState } from 'react'; const ControlledForm = () => { const [value, setValue] = useState(''); const handleChange = (e) => { setValue(e.target.value); }; const handleSubmit = (e) => { e.preventDefault(); console.log('Form submitted with value:', value); }; return ( <form onSubmit={handleSubmit}> <input type="text" value={value} onChange={handleChange} /> <button type="submit">Submit</button> </form> ); }; export default ControlledForm;
非受控组件(Uncontrolled Components) :
-
定义:非受控组件是指那些其输入值不由 React 状态管理的表单组件。相反,它们依赖于 DOM API 来获取表单的值。
-
特点:
- DOM API:通过
ref
获取表单的值。 - 初始值:可以通过
defaultValue
或defaultChecked
属性设置初始值。
- DOM API:通过
-
示例:
import React, { useRef } from 'react'; const UncontrolledForm = () => { const inputRef = useRef(null); const handleSubmit = (e) => { e.preventDefault(); console.log('Form submitted with value:', inputRef.current.value); }; return ( <form onSubmit={handleSubmit}> <input type="text" ref={inputRef} /> <button type="submit">Submit</button> </form> ); }; export default UncontrolledForm;
2. 如何在 React 中实现条件渲染?
条件渲染 是指根据条件决定是否渲染某个组件或元素。React 提供了多种方式来实现条件渲染:
-
三元运算符:
const App = ({ isLoggedIn }) => { return ( <div> {isLoggedIn ? <h1>Welcome back!</h1> : <h1>Please log in.</h1>} </div> ); };
-
逻辑与运算符:
const App = ({ user }) => { return ( <div> {user && <h1>Welcome, {user.name}!</h1>} </div> ); };
-
使用
if
语句:const App = ({ items }) => { if (items.length === 0) { return <h1>No items found.</h1>; } return ( <ul> {items.map(item => ( <li key={item.id}>{item.name}</li> ))} </ul> ); };
3. 什么是高阶组件(HOC)?请给出一个实际的例子。
高阶组件(Higher-Order Component, HOC) 是一个函数,它接受一个组件并返回一个新的组件。HOC 用于复用组件逻辑,增强组件的功能。
示例: 假设我们有一个 withLogging
HOC,用于在组件渲染时记录日志。
import React from 'react';
// 高阶组件
const withLogging = (WrappedComponent) => {
return class extends React.Component {
componentDidMount() {
console.log(`Component ${WrappedComponent.name} mounted`);
}
componentWillUnmount() {
console.log(`Component ${WrappedComponent.name} will unmount`);
}
render() {
return <WrappedComponent {...this.props} />;
}
};
};
// 被包裹的组件
const MyComponent = (props) => {
return <h1>Hello, {props.name}!</h1>;
};
// 使用 HOC
const MyComponentWithLogging = withLogging(MyComponent);
export default MyComponentWithLogging;
4. 什么是 Render Props?它有什么优点?
Render Props 是一种在 React 中共享代码的技术,通过在组件中传递一个函数作为 prop 来实现。这个函数负责返回 JSX,从而实现在不同组件间共享逻辑。
优点:
- 复用逻辑:可以在多个组件中复用相同的逻辑。
- 灵活性:可以自由地决定如何渲染组件,而不受限于预定义的结构。
示例: 假设我们有一个 MouseTracker
组件,用于跟踪鼠标位置。
import React from 'react';
// 跟踪鼠标位置的组件
class MouseTracker extends React.Component {
constructor(props) {
super(props);
this.state = { x: 0, y: 0 };
}
handleMouseMove = (event) => {
this.setState({
x: event.clientX,
y: event.clientY
});
};
render() {
return (
<div style={{ height: '100vh' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
// 使用 Render Props 的组件
const App = () => {
return (
<MouseTracker render={({ x, y }) => (
<h1>The mouse position is ({x}, {y})</h1>
)} />
);
};
export default App;
5. 如何在 React 中实现列表渲染?
列表渲染 是指根据数组数据动态生成多个组件或元素。React 提供了 map
方法来实现这一功能。
示例: 假设我们有一个 items
数组,需要渲染成一个列表。
import React from 'react';
const App = ({ items }) => {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
const items = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
];
export default () => <App items={items} />;
6. 什么是 Fragment?它的作用是什么?
Fragment 是 React 提供的一个特殊组件,用于包裹多个子元素,而不会在 DOM 中添加额外的节点。这在需要返回多个根节点时非常有用。
作用:
- 避免额外的 DOM 节点:在需要返回多个根节点时,使用 Fragment 可以避免添加不必要的 DOM 节点。
- 保持代码整洁:可以使 JSX 代码更加简洁和易读。
示例:
import React from 'react';
const App = () => {
return (
<React.Fragment>
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</React.Fragment>
);
};
// 简写形式
const App = () => {
return (
<>
<h1>Title</h1>
<p>Paragraph 1</p>
<p>Paragraph 2</p>
</>
);
};
export default App;
7. 如何在 React 中实现事件处理?
事件处理 是指在 React 中处理用户的交互操作,如点击、输入等。React 使用合成事件系统来处理事件。
示例:
import React, { useState } from 'react';
const App = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default App;
8. 什么是 Refs?如何使用 Refs?
Refs 是 React 提供的一种访问 DOM 节点或在类组件中访问实例的方法。Refs 可以用于获取输入值、管理焦点、触发动画等。
使用方法:
- 创建 Ref:使用
useRef
Hook 或React.createRef
。 - 附加 Ref:将 Ref 附加到需要访问的 DOM 节点或组件实例。
- 访问 Ref:通过
ref.current
访问 DOM 节点或组件实例。
示例:
import React, { useRef } from 'react';
const App = () => {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export default App;
状态管理
1. 什么是 Redux?它的主要特点是什么?
Redux 是一个用于管理应用状态的 JavaScript 库,通常与 React 一起使用。它提供了一种集中管理应用状态的方式,使得状态管理更加可预测和可维护。
主要特点:
- 单一数据源:整个应用的状态存储在一个单一的 store 中,确保了状态的一致性。
- 状态不可变:状态是不可变的,每次状态变化时,都会生成一个新的状态对象。
- 纯函数:通过纯函数(reducer)来处理状态变化,使得状态变化可预测。
- 中间件支持:支持中间件,可以扩展 Redux 的功能,如异步操作、日志记录等。
- 开发者工具:提供了强大的开发者工具,可以调试、回溯和重放状态变化。
2. 什么是 Redux Thunk?它解决了什么问题?
Redux Thunk 是一个中间件,允许你在 action 创建函数中返回一个函数而不是一个 action 对象。这个返回的函数可以包含异步逻辑,并在适当的时候 dispatch 一个或多个 action。
解决问题:
- 异步操作:Redux Thunk 允许你处理异步操作,如 AJAX 请求,而不需要在 reducer 中处理异步逻辑。
- 复杂逻辑:可以处理复杂的业务逻辑,如条件 dispatch、多次 dispatch 等。
示例:
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
const initialState = { data: null };
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'FETCH_DATA_SUCCESS':
return { ...state, data: action.payload };
default:
return state;
}
};
const fetchData = () => async (dispatch) => {
const response = await fetch('/api/data');
const data = await response.json();
dispatch({ type: 'FETCH_DATA_SUCCESS', payload: data });
};
const store = createStore(reducer, applyMiddleware(thunk));
store.dispatch(fetchData());
3. 什么是 Redux Saga?它与 Redux Thunk 有什么区别?
Redux Saga 是一个用于管理应用副作用(如异步操作)的库,使用 Generator 函数来处理异步逻辑。
区别:
- Generator 函数:Redux Saga 使用 Generator 函数,提供了更强大的控制流和错误处理机制。
- 可测试性:Redux Saga 的副作用可以更容易地进行单元测试。
- 复杂逻辑:Redux Saga 更适合处理复杂的异步逻辑和并发操作。
示例:
import { takeLatest, call, put } from 'redux-saga/effects';
import { fetchDataSuccess, fetchDataFailure } from './actions';
function* fetchDataSaga() {
try {
const response = yield call(fetch, '/api/data');
const data = yield response.json();
yield put(fetchDataSuccess(data));
} catch (error) {
yield put(fetchDataFailure(error));
}
}
function* watchFetchData() {
yield takeLatest('FETCH_DATA_REQUEST', fetchDataSaga);
}
export default function* rootSaga() {
yield watchFetchData();
}
4. 什么是 MobX?它与 Redux 有什么区别?
MobX 是一个简单的状态管理库,使用观察者模式来管理应用状态。它允许你直接操作状态,而不需要通过纯函数和 action。
区别:
- 直接操作状态:MobX 允许你直接修改状态,而不需要通过 action 和 reducer。
- 自动反应:MobX 使用装饰器和 observable 来自动跟踪状态变化,并更新相关的视图。
- 简单易用:MobX 的 API 更加简单,学习曲线更低。
示例:
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react';
class Store {
data = null;
constructor() {
makeAutoObservable(this);
}
fetchData = async () => {
const response = await fetch('/api/data');
const data = await response.json();
this.data = data;
};
}
const store = new Store();
const App = observer(() => {
return (
<div>
<button onClick={() => store.fetchData()}>Fetch Data</button>
{store.data && <pre>{JSON.stringify(store.data, null, 2)}</pre>}
</div>
);
});
5. 如何在 React 中使用 Redux?
步骤:
-
安装 Redux 和 React-Redux:
npm install redux react-redux
-
创建 Reducer:
const initialState = { count: 0 }; const counterReducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 }; case 'DECREMENT': return { ...state, count: state.count - 1 }; default: return state; } };
-
创建 Store:
import { createStore } from 'redux'; import counterReducer from './counterReducer'; const store = createStore(counterReducer);
-
提供 Store:
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import store from './store'; import App from './App'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
-
连接组件:
import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; const Counter = () => { const count = useSelector(state => state.count); const dispatch = useDispatch(); const increment = () => dispatch({ type: 'INCREMENT' }); const decrement = () => dispatch({ type: 'DECREMENT' }); return ( <div> <h1>Count: {count}</h1> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); }; export default Counter;
6. 什么是 Context API?它与 Redux 有什么区别?
Context API 是 React 提供的一种在组件树中传递数据的机制,无需通过 props 逐层传递。
区别:
- 简单性:Context API 更简单,适合小型应用或局部状态管理。
- 性能:Context API 在状态变化时会重新渲染所有订阅的组件,可能会影响性能。
- 功能:Redux 功能更强大,适合大型应用和全局状态管理,提供了更多的工具和中间件支持。
示例:
import React, { createContext, useContext, useState } from 'react';
// 创建 Context
const ThemeContext = createContext();
// 提供 Context
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
// 使用 Context
const App = () => {
return (
<ThemeProvider>
<Header />
<Content />
</ThemeProvider>
);
};
const Header = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<button onClick={toggleTheme}>Toggle Theme</button>
</header>
);
};
const Content = () => {
const { theme } = useContext(ThemeContext);
return (
<div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<h1>Welcome to the App</h1>
</div>
);
};
7. 什么是 React Query?它解决了什么问题?
React Query 是一个用于管理和缓存数据的库,特别适合处理 API 请求和数据同步。
解决问题:
- 数据缓存:自动缓存 API 响应,减少不必要的网络请求。
- 数据同步:自动处理数据的加载、更新和错误处理。
- 简化代码:提供简单的 API,减少数据管理的样板代码。
- 性能优化:通过缓存和优化请求,提高应用性能。
示例:
import React from 'react';
import { useQuery } from 'react-query';
import axios from 'axios';
const fetchUser = async (id) => {
const response = await axios.get(`https://api.example.com/users/${id}`);
return response.data;
};
const User = ({ userId }) => {
const { data, error, isLoading } = useQuery(['user', userId], () => fetchUser(userId));
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h1>User: {data.name}</h1>
<p>Email: {data.email}</p>
</div>
);
};
export default User;
生命周期
1. 请列出 React 组件的生命周期方法,并简要说明每个方法的作用。
React 组件的生命周期可以分为三个阶段:挂载(Mounting)、更新(Updating)和卸载(Unmounting)。以下是各个生命周期方法及其作用:
挂载阶段(Mounting)
-
constructor(props)
:- 作用:初始化组件的 state 和绑定事件处理器。
- 调用时机:组件实例被创建时。
-
static getDerivedStateFromProps(props, state)
:- 作用:在组件实例被创建和更新时调用,用于根据 props 更新 state。
- 调用时机:组件实例被创建和每次更新之前。
-
render()
:- 作用:返回组件的 JSX,描述 UI 的结构。
- 调用时机:组件实例被创建和每次更新时。
-
componentDidMount()
:- 作用:组件挂载完成后调用,通常用于发起网络请求、设置定时器等。
- 调用时机:组件首次渲染到 DOM 后。
更新阶段(Updating)
-
static getDerivedStateFromProps(props, state)
:- 作用:在组件更新时调用,用于根据新的 props 更新 state。
- 调用时机:组件接收到新 props 或 state 变化时。
-
shouldComponentUpdate(nextProps, nextState)
:- 作用:决定组件是否需要重新渲染,默认返回
true
。 - 调用时机:组件接收到新 props 或 state 变化时。
- 作用:决定组件是否需要重新渲染,默认返回
-
render()
:- 作用:返回组件的 JSX,描述 UI 的结构。
- 调用时机:组件实例被创建和每次更新时。
-
getSnapshotBeforeUpdate(prevProps, prevState)
:- 作用:在组件更新前捕获一些信息,这些信息可以在
componentDidUpdate
中使用。 - 调用时机:组件更新前。
- 作用:在组件更新前捕获一些信息,这些信息可以在
-
componentDidUpdate(prevProps, prevState, snapshot)
:- 作用:组件更新完成后调用,通常用于更新 DOM 或发起网络请求。
- 调用时机:组件更新后。
卸载阶段(Unmounting)
-
componentWillUnmount()
:- 作用:组件卸载前调用,通常用于清理工作,如取消网络请求、清除定时器等。
- 调用时机:组件从 DOM 中移除前。
2. 什么是 React 的新生命周期方法?它们取代了哪些旧方法?
React 16.3 引入了一些新的生命周期方法,以解决旧方法的一些问题,并提高组件的可预测性和安全性。以下是新生命周期方法及其取代的旧方法:
-
static getDerivedStateFromProps(props, state)
:- 取代:
componentWillReceiveProps
- 作用:在组件实例被创建和更新时调用,用于根据 props 更新 state。
- 取代:
-
getSnapshotBeforeUpdate(prevProps, prevState)
:- 取代:
componentWillUpdate
- 作用:在组件更新前捕获一些信息,这些信息可以在
componentDidUpdate
中使用。
- 取代:
-
componentDidCatch(error, info)
:- 新增:用于捕获和处理组件树中的错误。
- 作用:在组件树中捕获错误时调用,可以用于记录错误或显示错误页面。
3. 什么是 getDerivedStateFromProps
?它的作用是什么?
getDerivedStateFromProps
是一个静态方法,用于在组件实例被创建和更新时根据 props 更新 state。它是一个纯函数,不能执行副作用操作。
作用:
- 同步更新 state:确保组件的 state 始终与 props 保持一致。
- 避免不必要的更新:通过返回
null
,可以避免不必要的 state 更新。
示例:
class MyComponent extends React.Component {
static getDerivedStateFromProps(props, state) {
if (props.counter !== state.prevCounter) {
return { prevCounter: props.counter, hasChanged: true };
}
return null;
}
render() {
return (
<div>
<p>Counter: {this.props.counter}</p>
{this.state.hasChanged && <p>Counter has changed!</p>}
</div>
);
}
}
4. 什么是 getSnapshotBeforeUpdate
?它的作用是什么?
getSnapshotBeforeUpdate
是一个生命周期方法,用于在组件更新前捕获一些信息,这些信息可以在 componentDidUpdate
中使用。它通常用于捕获 DOM 的当前状态。
作用:
- 捕获 DOM 状态:在组件更新前捕获 DOM 的当前状态,以便在更新后进行比较或处理。
- 避免闪烁:确保在更新后能够平滑地过渡到新的状态。
示例:
class ScrollingList extends React.Component {
constructor(props) {
super(props);
this.listRef = React.createRef();
}
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.list.length < this.props.list.length) {
const list = this.listRef.current;
return list.scrollHeight - list.scrollTop;
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
const list = this.listRef.current;
list.scrollTop = list.scrollHeight - snapshot;
}
}
render() {
return (
<div ref={this.listRef}>
{this.props.list.map(item => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
}
5. 什么是 componentDidCatch
?它的作用是什么?
componentDidCatch
是一个生命周期方法,用于捕获和处理组件树中的错误。它类似于 JavaScript 中的 try...catch
语句,但作用于 React 组件。
作用:
- 捕获错误:在组件树中捕获任何未捕获的错误。
- 记录错误:可以用于记录错误信息,发送错误报告等。
- 显示错误页面:可以用于显示友好的错误页面,提升用户体验。
示例:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, info: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
this.setState({ error, info });
// 可以在这里记录错误信息
console.error("Caught an error:", error, info);
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
const App = () => (
<ErrorBoundary>
<MyComponent />
</ErrorBoundary>
);
Hooks
1. 什么是 Hooks?它们解决了什么问题?
Hooks 是 React 16.8 引入的新特性,允许你在不编写类组件的情况下使用状态和其他 React 特性。Hooks 使得函数组件可以拥有状态和生命周期方法,从而提高了代码的可读性和可维护性。
解决的问题:
- 状态管理:在函数组件中管理状态,而不需要转换为类组件。
- 生命周期方法:在函数组件中使用生命周期方法,而不需要编写复杂的类组件。
- 逻辑复用:通过自定义 Hooks 复用组件逻辑,提高代码复用性。
- 代码简洁:使得函数组件的代码更加简洁和易读。
2. 请列举并简要说明 React 中常用的 Hooks。
常用 Hooks:
useState
:用于在函数组件中添加状态。useEffect
:用于在函数组件中执行副作用操作,如数据获取、订阅或手动更改 DOM。useContext
:用于在函数组件中访问 React 的 Context。useReducer
:用于在函数组件中管理复杂的状态逻辑。useMemo
:用于 memoize 计算结果,避免不必要的计算。useCallback
:用于 memoize 回调函数,避免不必要的重新渲染。useRef
:用于在函数组件中创建一个可变的引用对象。useImperativeHandle
:用于自定义暴露给父组件的实例值。useLayoutEffect
:与useEffect
类似,但在所有的 DOM 变更之后同步调用。useDebugValue
:用于在 React DevTools 中显示自定义 Hooks 的标签。
3. 什么是 useState
?它如何工作?
useState
是一个 Hook,用于在函数组件中添加状态。
工作原理:
- 初始化状态:第一次调用
useState
时,传入的初始值会被用作初始状态。 - 更新状态:返回一个数组,第一个元素是当前状态,第二个元素是一个用于更新状态的函数。
示例:
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<h1>Count: {count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;
4. 什么是 useEffect
?它如何工作?
useEffect
是一个 Hook,用于在函数组件中执行副作用操作,如数据获取、订阅或手动更改 DOM。
工作原理:
- 执行副作用:在组件挂载和更新时执行副作用操作。
- 清理副作用:返回一个可选的清理函数,用于在组件卸载或下次执行副作用前清理上一次的副作用。
示例:
import React, { useState, useEffect } from 'react';
const DataFetcher = () => {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('/api/data');
const json = await response.json();
setData(json);
};
fetchData();
// 清理函数
return () => {
console.log('Cleanup');
};
}, []); // 依赖数组为空,表示仅在组件挂载时执行
return (
<div>
{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : <p>Loading...</p>}
</div>
);
};
export default DataFetcher;
5. 什么是 useContext
?它如何工作?
useContext
是一个 Hook,用于在函数组件中访问 React 的 Context。
工作原理:
- 访问 Context:传入一个 Context 对象,返回该 Context 的当前值。
- 订阅变化:当 Context 的值发生变化时,使用
useContext
的组件会重新渲染。
示例:
import React, { createContext, useContext } from 'react';
// 创建 Context
const ThemeContext = createContext();
// 提供 Context
const ThemeProvider = ({ children }) => {
const [theme, setTheme] = React.useState('light');
const toggleTheme = () => {
setTheme(prevTheme => prevTheme === 'light' ? 'dark' : 'light');
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
// 使用 Context
const App = () => {
return (
<ThemeProvider>
<Header />
<Content />
</ThemeProvider>
);
};
const Header = () => {
const { theme, toggleTheme } = useContext(ThemeContext);
return (
<header style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<button onClick={toggleTheme}>Toggle Theme</button>
</header>
);
};
const Content = () => {
const { theme } = useContext(ThemeContext);
return (
<div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff' }}>
<h1>Welcome to the App</h1>
</div>
);
};
6. 什么是 useReducer
?它如何工作?
useReducer
是一个 Hook,用于在函数组件中管理复杂的状态逻辑。
工作原理:
- 定义 Reducer:传入一个 reducer 函数,该函数接收当前状态和一个 action,返回新的状态。
- 初始化状态:传入初始状态或一个初始化函数。
- 派发 Action:返回一个数组,第一个元素是当前状态,第二个元素是一个用于派发 action 的函数。
示例:
import React, { useReducer } from 'react';
const initialState = { count: 0 };
const reducer = (state, action) => {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
};
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState);
const increment = () => {
dispatch({ type: 'increment' });
};
const decrement = () => {
dispatch({ type: 'decrement' });
};
return (
<div>
<h1>Count: {state.count}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</div>
);
};
export default Counter;
7. 什么是 useMemo
和 useCallback
?它们的作用是什么?
useMemo
:
- 作用:用于 memoize 计算结果,避免不必要的计算。
- 工作原理:传入一个计算函数和一个依赖数组,只有当依赖数组中的值发生变化时,才会重新计算。
示例:
import React, { useMemo } from 'react';
const ExpensiveComponent = ({ count }) => {
const expensiveCalculation = useMemo(() => {
console.log('Performing expensive calculation');
return count * 1000;
}, [count]);
return <div>Expensive Calculation Result: {expensiveCalculation}</div>;
};
export default ExpensiveComponent;
useCallback
:
- 作用:用于 memoize 回调函数,避免不必要的重新渲染。
- 工作原理:传入一个回调函数和一个依赖数组,只有当依赖数组中的值发生变化时,才会返回一个新的回调函数。
示例:
import React, { useCallback, useState } from 'react';
const ChildComponent = ({ onIncrement }) => {
return <button onClick={onIncrement}>Increment</button>;
};
const ParentComponent = () => {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<h1>Count: {count}</h1>
<ChildComponent onIncrement={increment} />
</div>
);
};
export default ParentComponent;
8. 什么是 useRef
?它如何工作?
useRef
是一个 Hook,用于在函数组件中创建一个可变的引用对象。
工作原理:
- 创建引用:返回一个可变的引用对象,其
.current
属性可以保存任何值。 - 持久化值:引用对象在组件的整个生命周期内保持不变,可以用于保存 DOM 节点、计时器等。
示例:
import React, { useRef } from 'react';
const FocusInput = () => {
const inputRef = useRef(null);
const focusInput = () => {
inputRef.current.focus();
};
return (
<div>
<input type="text" ref={inputRef} />
<button onClick={focusInput}>Focus Input</button>
</div>
);
};
export default FocusInput;
9. 什么是自定义 Hooks?请给出一个实际的例子。
自定义 Hooks 是一种将逻辑提取到可重用函数中的方法。自定义 Hooks 以 use
开头,可以组合多个内置 Hooks,封装复杂逻辑。
示例: 假设我们需要一个自定义 Hook 来处理 API 请求。
import { useState, useEffect } from 'react';
const useFetch = (url) => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error('Network response was not ok');
}
const json = await response.json();
setData(json);
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
};
const DataFetcher = () => {
const { data, loading, error } = useFetch('/api/data');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
return (
<div>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
};
export default DataFetcher;
性能优化
1. 什么是 React 的性能优化技术?请列举并简要说明。
React 提供了多种性能优化技术,以提高应用的响应速度和用户体验。以下是一些常见的性能优化技术:
-
Key:
- 作用:在列表渲染中,为每个列表项提供一个唯一的标识符,帮助 React 识别哪些项发生了变化、添加或删除。
- 好处:提高虚拟 DOM 的 diff 效率,减少不必要的重新渲染。
-
Code Splitting:
- 作用:将应用的代码分割成多个小块,按需加载。
- 好处:减少初始加载时间,提高首屏渲染速度。
-
Lazy Loading:
- 作用:延迟加载组件,直到需要时才加载。
- 好处:减少初始加载时间,提高应用性能。
-
Memoization:
- 作用:缓存计算结果,避免不必要的重复计算。
- 好处:提高组件的渲染性能,减少计算开销。
-
React.memo:
- 作用:对函数组件进行浅比较,如果 props 没有变化,则跳过重新渲染。
- 好处:减少不必要的重新渲染,提高性能。
-
ShouldComponentUpdate:
- 作用:在类组件中,通过返回布尔值来决定组件是否需要重新渲染。
- 好处:减少不必要的重新渲染,提高性能。
-
PureComponent:
- 作用:继承自
React.PureComponent
的组件会进行浅比较,如果 props 和 state 没有变化,则跳过重新渲染。 - 好处:减少不必要的重新渲染,提高性能。
- 作用:继承自
-
UseCallback 和 useMemo:
- 作用:分别用于 memoize 回调函数和计算结果,避免不必要的重新渲染和计算。
- 好处:提高组件的渲染性能,减少计算开销。
-
Profiler API:
- 作用:用于测量应用的性能,找出性能瓶颈。
- 好处:帮助开发者优化应用性能,提高用户体验。
2. 什么是 Key?它在列表渲染中的作用是什么?
Key 是 React 用于识别列表中每个元素的唯一标识符。在列表渲染中,为每个列表项提供一个唯一的 key
属性,可以帮助 React 识别哪些项发生了变化、添加或删除,从而提高虚拟 DOM 的 diff 效率。
作用:
- 提高性能:通过
key
,React 可以更高效地比对和更新列表项,减少不必要的重新渲染。 - 避免错误:没有
key
时,React 会发出警告,并可能导致意外的行为。
示例:
import React from 'react';
const ItemList = ({ items }) => {
return (
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
};
const items = [
{ id: 1, name: 'Apple' },
{ id: 2, name: 'Banana' },
{ id: 3, name: 'Cherry' }
];
export default () => <ItemList items={items} />;
3. 什么是 Code Splitting?如何在 React 中实现 Code Splitting?
Code Splitting 是一种将应用的代码分割成多个小块的技术,按需加载这些代码块,从而减少初始加载时间,提高首屏渲染速度。
实现方法:
- 动态导入:使用
import()
语法动态导入模块。 - React.lazy 和 Suspense:结合
React.lazy
和Suspense
实现组件的懒加载。
示例:
import React, { Suspense } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = React.lazy(() => import('./components/Home'));
const About = React.lazy(() => import('./components/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</Suspense>
</Router>
);
export default App;
4. 什么是 Lazy Loading?如何在 React 中实现 Lazy Loading?
Lazy Loading 是一种延迟加载组件的技术,直到需要时才加载。这可以减少初始加载时间,提高应用性能。
实现方法:
- React.lazy:用于声明懒加载的组件。
- Suspense:用于在组件加载时显示一个 fallback UI。
示例:
import React, { lazy, Suspense } from 'react';
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const App = () => (
<div>
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
</div>
);
export default App;
5. 什么是 Memoization?如何在 React 中实现 Memoization?
Memoization 是一种优化技术,通过缓存计算结果来避免不必要的重复计算。在 React 中,可以使用 useMemo
和 useCallback
来实现 memoization。
实现方法:
- useMemo:用于 memoize 计算结果。
- useCallback:用于 memoize 回调函数。
示例:
import React, { useMemo, useCallback } from 'react';
const ExpensiveComponent = ({ count }) => {
const expensiveCalculation = useMemo(() => {
console.log('Performing expensive calculation');
return count * 1000;
}, [count]);
return <div>Expensive Calculation Result: {expensiveCalculation}</div>;
};
const ParentComponent = () => {
const [count, setCount] = React.useState(0);
const [text, setText] = React.useState('');
const increment = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []);
return (
<div>
<h1>Count: {count}</h1>
<input type="text" value={text} onChange={e => setText(e.target.value)} />
<ExpensiveComponent count={count} />
<button onClick={increment}>Increment</button>
</div>
);
};
export default ParentComponent;
路由
1. 什么是 React Router?它的主要特点是什么?
React Router 是一个用于 React 应用的路由库,它允许你在单页应用(SPA)中实现多页面的导航和路由管理。
主要特点:
- 声明式路由:使用声明式的方式来定义路由,使代码更加清晰和易于维护。
- 动态路由匹配:支持动态参数匹配,可以根据 URL 参数动态加载不同的组件。
- 嵌套路由:支持嵌套路由,可以轻松实现多级嵌套的页面结构。
- 编程式导航:提供编程式导航的方法,可以在代码中控制页面的跳转。
- 路由守卫:支持路由守卫,可以在路由切换前后执行特定的逻辑。
- 懒加载:支持代码分割和懒加载,可以按需加载组件,提高应用性能。
2. 如何在 React 中实现路由?
在 React 中使用 React Router 实现路由的基本步骤如下:
-
安装 React Router:
npm install react-router-dom
-
引入必要的组件:
jsx 深色版本 import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
-
配置路由:
import React from 'react'; import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom'; import Home from './Home'; import About from './About'; import Contact from './Contact'; const App = () => ( <Router> <div> <nav> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/contact">Contact</Link></li> </ul> </nav> <Switch> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/contact" component={Contact} /> </Switch> </div> </Router> ); export default App;
3. 什么是 Route 和 Link?它们的作用是什么?
Route:
-
作用:定义一个路由规则,当 URL 匹配时,会渲染对应的组件。
-
属性:
path
:定义匹配的路径。component
:匹配路径时渲染的组件。exact
:是否精确匹配路径。render
:匹配路径时渲染的函数。children
:匹配路径时渲染的子组件。
示例:
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
Link:
-
作用:用于在应用中创建导航链接,点击后会改变 URL 并触发路由匹配。
-
属性:
to
:目标路径。
示例:
<Link to="/">Home</Link>
<Link to="/about">About</Link>
4. 什么是 Switch?它的作用是什么?
Switch:
- 作用:用于包裹多个
Route
组件,确保只有一个Route
被渲染。当找到第一个匹配的Route
时,停止匹配后续的Route
。 - 优点:避免多个
Route
同时匹配,确保路由的唯一性。
示例:
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
5. 如何实现嵌套路由?
嵌套路由是指在一个路由组件中定义子路由,实现多级嵌套的页面结构。
示例:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
import Profile from './Profile';
const App = () => (
<Router>
<div>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
</Switch>
</div>
</Router>
);
const Contact = () => (
<div>
<h2>Contact</h2>
<nav>
<ul>
<li><Link to="/contact/profile">Profile</Link></li>
</ul>
</nav>
<Switch>
<Route path="/contact/profile" component={Profile} />
</Switch>
</div>
);
export default App;
6. 什么是 Redirect?它的作用是什么?
Redirect:
-
作用:用于在组件中进行重定向,将用户从当前路径重定向到另一个路径。
-
属性:
from
:当前路径。to
:目标路径。push
:是否将重定向路径添加到历史记录中。
示例:
import React from 'react';
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';
const App = () => (
<Router>
<div>
<nav>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/contact">Contact</Link></li>
</ul>
</nav>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/contact" component={Contact} />
<Redirect from="/old-path" to="/new-path" />
</Switch>
</div>
</Router>
);
export default App;
其他
1. 什么是 TypeScript?如何在 React 中使用 TypeScript?
TypeScript 是一种静态类型的编程语言,它是 JavaScript 的超集,增加了类型系统和编译时检查。TypeScript 可以帮助开发者提前发现类型错误,提高代码质量和可维护性。
如何在 React 中使用 TypeScript:
-
安装 TypeScript:
npm install typescript @types/react @types/react-dom --save-dev
-
创建 TypeScript 文件: 将
.js
文件改为.tsx
文件(对于 React 组件),并在文件顶部添加类型声明。 -
配置 tsconfig.json: 在项目根目录下创建
tsconfig.json
文件,配置 TypeScript 编译选项。{ "compilerOptions": { "target": "es5", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "noFallthroughCasesInSwitch": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react-jsx" }, "include": ["src"] }
-
编写带有类型的 React 组件:
import React from 'react'; interface Props { name: string; } const HelloWorld: React.FC<Props> = ({ name }) => { return <div>Hello, {name}!</div>; }; export default HelloWorld;
2. 什么是 Webpack?它的主要功能是什么?
Webpack 是一个模块打包工具,它可以将各种资源(如 JavaScript、CSS、图片等)视为模块,并将其打包成一个或多个 bundle 文件。
主要功能:
- 模块打包:将多个模块打包成一个或多个 bundle 文件。
- 代码分割:将代码分割成多个小块,按需加载。
- 加载器(Loaders) :通过加载器处理不同类型的文件(如 CSS、图片等)。
- 插件(Plugins) :通过插件扩展 Webpack 的功能,如代码压缩、HTML 生成等。
- 热模块替换(HMR) :在开发过程中实时更新模块,无需刷新页面。
示例配置:
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader'
}
},
{
test: /.css$/,
use: ['style-loader', 'css-loader']
}
]
},
plugins: [
// 插件配置
],
devServer: {
contentBase: path.join(__dirname, 'dist'),
hot: true
}
};
3. 什么是 Babel?它的主要功能是什么?
Babel 是一个 JavaScript 编译器,可以将现代 JavaScript 代码转换为向后兼容的版本,使其能够在更广泛的环境中运行。
主要功能:
- 语法转换:将 ES6+ 语法转换为 ES5 语法。
- 插件系统:通过插件扩展 Babel 的功能,如转换装饰器、类属性等。
- 预设:预设是一组插件的集合,简化配置过程。
示例配置:
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"plugins": ["@babel/plugin-proposal-class-properties"]
}
4. 什么是 ESLint?它的主要功能是什么?
ESLint 是一个用于识别和报告 JavaScript 代码中模式问题的工具,旨在统一代码风格并发现潜在错误。
主要功能:
- 代码检查:检查代码风格和潜在错误。
- 规则配置:通过配置文件自定义检查规则。
- 插件系统:通过插件扩展 ESLint 的功能,如支持 TypeScript、React 等。
示例配置:
{
"env": {
"browser": true,
"es6": true
},
"extends": "eslint:recommended",
"rules": {
"indent": ["error", 2],
"quotes": ["error", "double"],
"semi": ["error", "always"]
}
}
5. 什么是 Jest?它的主要功能是什么?
Jest 是一个用于 JavaScript 的测试框架,特别适合 React 应用的单元测试和集成测试。
主要功能:
- 断言库:内置丰富的断言库。
- 模拟函数:支持函数和模块的模拟。
- 快照测试:自动生成和比对快照,确保组件输出的一致性。
- 异步测试:支持 Promise 和 async/await 的异步测试。
- 覆盖率报告:生成代码覆盖率报告。
示例测试:
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const linkElement = getByText(/learn react/i);
expect(linkElement).toBeInTheDocument();
});
test('button click increments counter', () => {
const { getByText } = render(<App />);
const button = getByText('Click me');
fireEvent.click(button);
expect(getByText('Count: 1')).toBeInTheDocument();
});
6. 什么是 Enzyme?它的主要功能是什么?
Enzyme 是一个用于 React 组件的单元测试工具,提供了丰富的 API 用于渲染和查询组件。
主要功能:
- 浅渲染:只渲染组件的顶层,不渲染子组件。
- 完整渲染:渲染整个组件树,适用于集成测试。
- 静态渲染:将组件渲染为静态 HTML,适用于无 DOM 环境。
- 选择器:提供丰富的选择器 API,用于查询和操作组件。
示例测试:
import React from 'react';
import { shallow } from 'enzyme';
import App from './App';
describe('<App />', () => {
it('renders without crashing', () => {
const wrapper = shallow(<App />);
expect(wrapper.find('h1').text()).toEqual('Hello, world!');
});
it('handles button click', () => {
const wrapper = shallow(<App />);
wrapper.find('button').simulate('click');
expect(wrapper.find('p').text()).toEqual('Count: 1');
});
});
7. 什么是 Storybook?它的主要功能是什么?
Storybook 是一个用于开发和展示 UI 组件的工具,支持 React、Vue、Angular 等多种框架。
主要功能:
- 组件隔离:在隔离的环境中开发和展示组件。
- 文档生成:自动生成组件文档,展示组件的各种状态和用法。
- 交互式 playground:提供交互式 playground,方便开发者测试和调试组件。
- 插件系统:通过插件扩展功能,如添加样式指南、性能测试等。
示例配置:
-
安装 Storybook:
npx sb init
-
编写故事:
import React from 'react'; import { Button } from './Button'; import { storiesOf } from '@storybook/react'; storiesOf('Button', module) .add('with text', () => <Button>Click me</Button>) .add('with emoji', () => <Button>😀 😎 👍 💯</Button>);
8. 什么是 Server-Side Rendering(SSR)?如何在 React 中实现 SSR?
Server-Side Rendering (SSR) 是一种在服务器端生成 HTML 标记的技术,然后将生成的 HTML 发送到客户端浏览器。这可以提高首屏加载速度,改善 SEO。
如何在 React 中实现 SSR:
-
安装必要的包:
npm install react react-dom express @loadable/server
-
创建服务器端渲染文件:
// server.js const express = require('express'); const React = require('react'); const ReactDOMServer = require('react-dom/server'); const App = require('./App').default; const app = express(); app.get('*', (req, res) => { const html = ReactDOMServer.renderToString(<App />); res.send(` <!DOCTYPE html> <html> <head> <title>SSR Example</title> </head> <body> <div id="root">${html}</div> <script src="/static/bundle.js"></script> </body> </html> `); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
-
创建客户端入口文件:
// client.js import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.hydrate(<App />, document.getElementById('root'));
9. 什么是 Static Site Generation(SSG)?如何在 React 中实现 SSG?
Static Site Generation (SSG) 是一种在构建时生成静态 HTML 文件的技术,然后将这些文件部署到静态文件服务器。这可以提高首屏加载速度,降低服务器负载。
如何在 React 中实现 SSG:
-
使用 Next.js:
-
安装 Next.js:
npx create-next-app my-app cd my-app
-
创建静态页面:
// pages/index.js import React from 'react'; const Home = () => { return <div>Welcome to the Static Site</div>; }; export default Home;
-
生成静态文件:
npm run build npm start
-
-
使用 Gatsby:
-
安装 Gatsby:
npx gatsby-cli new my-app cd my-app
-
创建静态页面:
// src/pages/index.js import React from 'react'; const Home = () => { return <div>Welcome to the Static Site</div>; }; export default Home;
-
生成静态文件:
npm run build npm run serve
-
Vue 和 React 的区别
Vue 和 React 都是非常流行的前端框架,各有其特点和适用场景。下面详细对比 Vue 和 React 的主要区别:
1. 基本概念
React:
- 定义:React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发和维护。
- 核心理念:组件化开发,虚拟 DOM,单向数据流。
Vue:
- 定义:Vue 是一个用于构建用户界面的渐进式框架,由尤雨溪开发和维护。
- 核心理念:组件化开发,虚拟 DOM,双向数据绑定。
2. 学习曲线
React:
- 学习曲线:相对较陡峭,需要了解 JSX、状态管理、生命周期方法等概念。
- 文档:官方文档详细,但内容较多,初学者可能需要花费更多时间学习。
Vue:
- 学习曲线:较为平缓,API 设计友好,文档清晰,初学者可以快速上手。
- 文档:官方文档简洁明了,适合快速学习。
3. 模板语法
React:
-
模板语法:使用 JSX,将 JavaScript 和 HTML 结合在一起,需要一定的 JavaScript 基础。
-
示例:
const App = () => ( <div> <h1>Hello, {name}!</h1> <button onClick={() => setName('World')}>Change Name</button> </div> );
Vue:
-
模板语法:使用类似于 HTML 的模板语法,支持指令和插值表达式。
-
示例:
<template> <div> <h1>Hello, {{ name }}!</h1> <button @click="changeName">Change Name</button> </div> </template> <script> export default { data() { return { name: 'Vue' }; }, methods: { changeName() { this.name = 'World'; } } }; </script>
4. 数据绑定
React:
-
数据绑定:单向数据流,通过
setState
更新状态。 -
示例:
const [name, setName] = useState('React'); const handleChange = (event) => { setName(event.target.value); };
Vue:
-
数据绑定:支持双向数据绑定,通过
v-model
实现。 -
示例:
<template> <div> <input v-model="name" /> <p>Name: {{ name }}</p> </div> </template> <script> export default { data() { return { name: 'Vue' }; } }; </script>
5. 状态管理
React:
-
状态管理:通常使用 Redux 或 MobX 进行全局状态管理。
-
示例:
import { useSelector, useDispatch } from 'react-redux'; const App = () => { const count = useSelector(state => state.count); const dispatch = useDispatch(); const increment = () => { dispatch({ type: 'INCREMENT' }); }; return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); };
Vue:
-
状态管理:通常使用 Vuex 进行全局状态管理。
-
示例:
<template> <div> <p>Count: {{ count }}</p> <button @click="increment">Increment</button> </div> </template> <script> import { mapState, mapActions } from 'vuex'; export default { computed: { ...mapState(['count']) }, methods: { ...mapActions(['increment']) } }; </script>
6. 生态系统
React:
- 生态系统:非常丰富,有大量的第三方库和工具支持,如 Redux、MobX、React Router、Styled Components 等。
- 社区:活跃度高,社区贡献者众多。
Vue:
- 生态系统:也在不断壮大,有 Vuex、Vue Router、Vuetify 等官方和第三方库支持。
- 社区:虽然不如 React 社区大,但也非常活跃,特别是在国内有较高的 popularity。
7. 性能
React:
- 性能:React 的虚拟 DOM 和高效的 diff 算法确保了良好的性能。
- 优化:可以通过代码分割、懒加载、memoization 等技术进一步优化性能。
Vue:
- 性能:Vue 也采用了虚拟 DOM 和高效的 diff 算法,性能表现良好。
- 优化:通过异步组件、懒加载、keep-alive 等技术优化性能。
8. 适用场景
React:
- 适用场景:适合大型复杂应用,特别是需要高度定制和灵活扩展的应用。
- 企业级应用:广泛应用于企业级应用,如 Facebook、Instagram 等。
Vue:
- 适用场景:适合中小规模应用,特别是需要快速开发和迭代的应用。
- 渐进式框架:可以逐步引入,适合现有项目的改造和升级。
9. 社区和支持
React:
- 社区:全球范围内非常活跃,有大量的教程、文档和社区支持。
- 支持:Facebook 提供官方支持,社区贡献者众多。
Vue:
- 社区:虽然不如 React 社区大,但也在快速增长,特别是在国内有较高的 popularity。
- 支持:官方文档详细,社区活跃,有许多中文资源。
总结
- React 更适合大型复杂应用,需要高度定制和灵活扩展的场景。
- Vue 更适合中小规模应用,需要快速开发和迭代的场景。