面试题答案
一键面试实现思路
- 服务端渲染(SSR)部分:
- 在服务端,使用
react - router - dom
中的StaticRouter
。它可以根据请求的URL匹配对应的路由。例如:
import { StaticRouter } from'react - router - dom'; import React from'react'; import { renderToString } from'react - dom/server'; import App from './App'; export default (req, res) => { const context = {}; const html = renderToString( <StaticRouter location={req.url} context={context}> <App /> </StaticRouter> ); // 如果是动态路由匹配失败,context.url 会被赋值新的URL if (context.url) { res.writeHead(301, { Location: context.url }); res.end(); } else { res.send(` <!DOCTYPE html> <html> <head> <title>SSR with React Router</title> </head> <body> <div id="root">${html}</div> <script src="/client - bundle.js"></script> </body> </html> `); } };
- 在组件中,通过
useParams
钩子获取动态参数id
,然后使用这个id
去请求用户详细数据。例如:
import React, { useEffect, useState } from'react'; import { useParams } from'react - router - dom'; const UserDetails = () => { const { id } = useParams(); const [userData, setUserData] = useState(null); useEffect(() => { const fetchUserData = async () => { const response = await fetch(`/api/users/${id}`); const data = await response.json(); setUserData(data); }; fetchUserData(); }, [id]); if (!userData) { return <div>Loading...</div>; } return ( <div> <h1>{userData.name}</h1> <p>{userData.bio}</p> </div> ); }; export default UserDetails;
- 在服务端,使用
- 客户端渲染部分:
- 在客户端,使用
BrowserRouter
来管理路由。例如:
import React from'react'; import ReactDOM from'react - dom'; import { BrowserRouter } from'react - router - dom'; import App from './App'; ReactDOM.hydrate( <BrowserRouter> <App /> </BrowserRouter>, document.getElementById('root') );
- 同样在组件中使用
useParams
获取参数并加载数据,逻辑与服务端渲染时类似。
- 在客户端,使用
可能遇到的问题及解决方案
- 数据预取一致性问题:
- 问题:服务端和客户端可能因为数据获取时机不同,导致渲染结果不一致。例如,服务端渲染时获取到的数据,在客户端重新获取时可能已经发生了变化。
- 解决方案:可以在服务端渲染时,将获取到的数据作为初始数据传递给客户端。在服务端,将数据添加到HTML的一个全局变量中,例如:
在客户端,组件挂载时优先使用这个初始数据,然后再进行数据更新。例如:const userData = await fetchUserData(id); res.send(` <!DOCTYPE html> <html> <head> <title>SSR with React Router</title> </head> <body> <div id="root">${html}</div> <script> window.__INITIAL_USER_DATA__ = ${JSON.stringify(userData)}; </script> <script src="/client - bundle.js"></script> </body> </html> `);
const UserDetails = () => { const { id } = useParams(); const [userData, setUserData] = useState(window.__INITIAL_USER_DATA__); useEffect(() => { const fetchUserData = async () => { const response = await fetch(`/api/users/${id}`); const data = await response.json(); setUserData(data); }; if (!userData) { fetchUserData(); } }, [id, userData]); if (!userData) { return <div>Loading...</div>; } return ( <div> <h1>{userData.name}</h1> <p>{userData.bio}</p> </div> ); };
- 路由匹配问题:
- 问题:在服务端渲染时,可能出现路由匹配失败的情况,导致返回错误页面或者无法正确渲染。
- 解决方案:通过
StaticRouter
的context
对象来处理重定向或错误。如上面服务端渲染代码中,如果context.url
被赋值,说明路由匹配失败,需要进行重定向。
- SEO 相关问题:
- 问题:搜索引擎爬虫可能无法正确解析JavaScript渲染的数据,影响SEO。
- 解决方案:确保服务端渲染时已经将关键数据渲染到HTML中,这样搜索引擎爬虫可以直接获取到这些数据。同时,可以使用
next - head
等库来优化页面的元数据,提高SEO效果。例如:
import React from'react'; import { useParams } from'react - router - dom'; import Head from 'next - head'; const UserDetails = () => { const { id } = useParams(); const [userData, setUserData] = useState(null); useEffect(() => { const fetchUserData = async () => { const response = await fetch(`/api/users/${id}`); const data = await response.json(); setUserData(data); }; fetchUserData(); }, [id]); if (!userData) { return <div>Loading...</div>; } return ( <div> <Head> <title>{userData.name} - User Details</title> <meta name="description" content={userData.bio} /> </Head> <h1>{userData.name}</h1> <p>{userData.bio}</p> </div> ); }; export default UserDetails;