面试题答案
一键面试设计思路
- 统一状态管理:使用 Redux 或 MobX 作为状态管理方案,确保服务端和客户端共享相同的状态逻辑。这意味着在服务端渲染(SSR)期间,初始化状态并将其序列化传递到客户端,客户端在挂载时可以直接使用这些状态。
- 生命周期钩子的合理使用:在 React 组件的生命周期函数中,结合状态管理方案进行数据获取、状态更新等操作。例如,在
componentDidMount
(客户端)或getInitialProps
(Next.js 等 SSR 框架特定)中发起异步数据请求,并将结果更新到状态管理库中。 - 避免水合问题:确保服务端渲染生成的 HTML 与客户端渲染的初始状态一致。这可以通过在服务端预填充状态,并在客户端避免重复获取已经在服务端获取的数据来实现。
- 优化渲染性能:利用状态管理库的特性,如 Redux 的
shouldComponentUpdate
或 MobX 的响应式编程,减少不必要的重新渲染。同时,使用 memoization 技术,如 React.memo 或 MobX 的computed
,缓存计算结果。
实现要点
- Redux 结合实现
- 服务端:
- 在服务端渲染时,创建 Redux store 并使用中间件(如
redux-thunk
或redux-saga
)处理异步操作。 - 例如,在处理请求时,根据请求参数获取数据并更新 store 状态。
import { createStore, applyMiddleware } from'redux'; import thunk from'redux-thunk'; import rootReducer from './reducers'; const serverStore = createStore(rootReducer, applyMiddleware(thunk)); // 假设存在一个异步 action 获取用户数据 serverStore.dispatch(fetchUserData());
- 将 store 的状态序列化并传递到客户端。
- 在服务端渲染时,创建 Redux store 并使用中间件(如
- 客户端:
- 在客户端创建相同的 Redux store,并使用服务端传递过来的初始状态进行初始化。
const preloadedState = window.__PRELOADED_STATE__; const clientStore = createStore(rootReducer, preloadedState, applyMiddleware(thunk));
- 在 React 组件中,使用
react - redux
的connect
或useSelector
/useDispatch
hooks 来连接组件与 Redux store。 - 在
componentDidMount
中,避免重复获取已经在服务端获取的数据。例如,可以通过判断状态是否已经存在来决定是否发起请求。
import React, { Component } from'react'; import { connect } from'react - redux'; import { fetchUserData } from './actions'; class UserComponent extends Component { componentDidMount() { if (!this.props.user) { this.props.fetchUserData(); } } render() { return <div>{this.props.user.name}</div>; } } const mapStateToProps = state => ({ user: state.user }); const mapDispatchToProps = { fetchUserData }; export default connect(mapStateToProps, mapDispatchToProps)(UserComponent);
- 服务端:
- MobX 结合实现
- 服务端:
- 创建 MobX store 实例,并在服务端处理请求时更新 store 状态。
import { makeObservable, observable, action } from'mobx'; class UserStore { user = null; constructor() { makeObservable(this, { user: observable, fetchUserData: action }); } async fetchUserData() { const response = await fetch('/api/user'); const data = await response.json(); this.user = data; } } const serverUserStore = new UserStore(); serverUserStore.fetchUserData();
- 将 store 的状态序列化并传递到客户端。
- 客户端:
- 在客户端创建相同的 MobX store 实例,并使用服务端传递过来的初始状态进行初始化。
const preloadedState = window.__PRELOADED_STATE__; const clientUserStore = new UserStore(); clientUserStore.user = preloadedState.user;
- 在 React 组件中,使用
mobx - react
的observer
函数将组件转换为响应式组件。
import React from'react'; import { observer } from'mobx - react'; import UserStore from './UserStore'; const UserComponent = observer(() => { return <div>{UserStore.user.name}</div>; }); export default UserComponent;
- 服务端:
- 避免水合问题
- 数据预取一致性:确保服务端和客户端使用相同的数据预取逻辑。例如,在 Redux 中,使用相同的 action creator 和 reducer 逻辑。
- 状态同步:在客户端渲染开始时,立即将服务端传递过来的状态应用到客户端状态管理库中,避免客户端再次获取相同数据。
- 优化渲染性能
- Redux:
- 使用
shouldComponentUpdate
生命周期函数或 React.memo 来控制组件的重新渲染。例如,通过比较前后 props 中的状态数据来决定是否重新渲染。
class MyComponent extends Component { shouldComponentUpdate(nextProps) { return nextProps.user!== this.props.user; } render() { return <div>{this.props.user.name}</div>; } }
- 使用
- MobX:
- 使用
computed
来缓存计算结果,避免重复计算。例如,计算用户的全名。
class UserStore { firstname = 'John'; lastname = 'Doe'; constructor() { makeObservable(this, { firstname: observable, lastname: observable, fullname: computed }); } get fullname() { return `${this.firstname} ${this.lastname}`; } }
- 利用 MobX 的响应式编程特性,只有当依赖的 observable 数据发生变化时,组件才会重新渲染。
- 使用
- Redux: