同构模板是指在服务器端和客户端都能够渲染的模板,也称为“服务器端渲染”(Server-side Rendering, SSR)。在 React 中,同构模板可以通过将 React 组件在服务器端渲染为 HTML 字符串,然后在客户端再次渲染为 React 组件来实现。这样可以提高网站的首屏加载速度和 SEO 优化效果。
以下是几种常见的实现同构模板的方法:
-
React 服务器端渲染模块(React Server Rendering Module):React 官方提供了一个名为 ReactDOMServer 的模块,可以将 React 组件渲染为 HTML 字符串。可以在服务器端调用该模块,将组件渲染为 HTML 字符串,然后将字符串传输到客户端。
以下是一个使用 React 服务器端渲染模块的示例代码,它将组件渲染为 HTML 字符串,然后将字符串传输到客户端:
import ReactDOMServer from 'react-dom/server';
const App = () => {
return <div>Hello, World!</div>;
};
const html = ReactDOMServer.renderToString(<App />);
console.log(html);
-
Next.js:Next.js 是一个基于 React 的服务器端渲染框架。它提供了一套完整的解决方案,包括服务器端渲染、代码分割、静态资源预加载等。使用 Next.js 可以轻松地实现同构模板。
以下是一个使用 Next.js 的示例代码,它实现了服务器端渲染和客户端渲染的同构模板:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { hydrate } from 'react-dom';
import { useRouter } from 'next/router';
const IndexPage = () => {
const router = useRouter();
return (
<div>
<h1>Next.js</h1>
<button onClick={() => router.push('/about')}>Go to About</button>
<App />
</div>
);
};
if (typeof window !== 'undefined') {
ReactDOM.render(<IndexPage />, document.getElementById('root'));
} else {
hydrate(<IndexPage />, document.getElementById('root'));
}
-
Gatsby:Gatsby 是一个基于 React 的静态网站生成器。它使用 GraphQL 查询数据,并将数据预取到页面中。Gatsby 支持服务器端渲染和静态网站生成,可以帮助开发者实现同构模板。
以下是一个使用 Gatsby 的示例代码,它实现了服务器端渲染和静态网站生成的同构模板:
import React from 'react';
import { graphql } from 'gatsby';
const BlogPost = ({ data }) => {
const post = data.markdownRemark;
return (
<div>
<h1>{post.frontmatter.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.html }} />
</div>
);
};
export const query = graphql`
query BlogPostQuery($slug: String!) {
markdownRemark(fields: { slug: { eq: $slug } }) {
frontmatter {
title
}
html
}
}
`;
export default BlogPost;
-
create-react-app:create-react-app 是一个用于创建 React 应用程序的脚手架工具。它提供了服务器端渲染的支持,可以通过配置实现同构模板。
以下是一个使用 create-react-app 的示例代码,它通过配置实现了服务器端渲染的同构模板:
// server.js
const express = require('express');
const React = require('react');
const ReactDOMServer = require('react-dom/server');
const { StaticRouter } = require('react-router-dom');
const App = require('./src/App').default;
const app = express();
app.use(express.static('build'));
app.get('*', (req, res) => {
const context = {};
const html = ReactDOMServer.renderToString(
<StaticRouter location={req.url} context={context}>
<App />
</StaticRouter>
);
if (context.url) {
res.redirect(context.url);
} else {
res.send(`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>React SSR Example</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/static/js/main.js"></script>
</body>
</html>
`);
}
});
app.listen(3000, () => {
console.log('Server is listening on port 3000');
});
在上面这个示例中,我们首先使用 'StaticRouter' 将路由信息传递给 React 组件,然后使用 'ReactDOMServer.renderToString' 方法将 React 组件渲染为 HTML 字符串。最后,将生成的 HTML 字符串和客户端代码一起发送到客户端,客户端代码通过使用 'ReactDOM.hydrate' 方法将 HTML 字符串转换回 React 组件,从而完成客户端渲染的工作。注意,这里使用了 'build' 目录中已经打包好的客户端代码。