同構模闆是指在伺服器端和用戶端都能夠渲染的模闆,也稱為“伺服器端渲染”(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' 目錄中已經打包好的用戶端代碼。