前言
服务器端渲染(Server-side rendering,SSR)是近年来前端领域的热门话题之一。与传统的客户端渲染(Client-side rendering,CSR)相比,SSR 可以提供更好的性能、更好的 SEO、更好的用户体验等等。而 React 作为目前最流行的前端框架之一,也提供了相关的 SSR 解决方案。本文将介绍如何使用 Koa+Webpack 搭建 React 服务器端渲染应用。
环境准备
在开始之前,我们需要准备好以下环境:
- Node.js:本文使用 Node.js v12.18.4。
- NPM:本文使用 NPM v6.14.6。
创建项目
首先,我们需要创建一个新的项目,并安装相关依赖。在命令行中执行以下命令:
mkdir koa-webpack-react-ssr cd koa-webpack-react-ssr npm init -y npm install koa koa-static koa-router react react-dom react-router-dom webpack webpack-cli webpack-dev-middleware webpack-hot-middleware @babel/core @babel/preset-env @babel/preset-react babel-loader css-loader style-loader file-loader html-webpack-plugin nodemon --save-dev npm install react-helmet --save npm install react-router-config react-loadable --save
其中,我们安装了以下依赖:
koa
:Koa 框架。koa-static
:Koa 静态文件中间件。koa-router
:Koa 路由中间件。react
、react-dom
、react-router-dom
:React 相关依赖。webpack
、webpack-cli
、webpack-dev-middleware
、webpack-hot-middleware
:Webpack 相关依赖。@babel/core
、@babel/preset-env
、@babel/preset-react
、babel-loader
:Babel 相关依赖。css-loader
、style-loader
、file-loader
:Webpack 加载 CSS、样式、图片等资源的相关依赖。html-webpack-plugin
:Webpack 生成 HTML 文件的插件。nodemon
:Node.js 自动重启工具。react-helmet
:React 处理页面标题、meta 等 SEO 相关信息的插件。react-router-config
:React 路由配置工具。react-loadable
:React 异步加载组件的工具。
配置 Webpack
在项目根目录下创建 webpack.config.js
文件,并添加以下配置:
// javascriptcn.com 代码示例 const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { mode: 'development', entry: './src/client/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'public'), publicPath: '/' }, module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: 'file-loader', }, ], }, ] }, plugins: [ new HtmlWebpackPlugin({ template: './src/client/index.html' }) ], devtool: 'source-map', devServer: { contentBase: path.join(__dirname, 'public'), compress: true, port: 3000, historyApiFallback: true } };
其中,我们配置了以下内容:
entry
:入口文件为./src/client/index.js
。output
:输出文件为public/bundle.js
。module
:使用 Babel 和相关 loader 处理 JS 和 JSX 文件,使用 CSS 和样式 loader 处理 CSS 文件,使用 file loader 处理图片等资源文件。plugins
:使用 HtmlWebpackPlugin 插件生成 HTML 文件。devtool
:开启 source map。devServer
:使用 Webpack Dev Server 进行开发,监听localhost:3000
,并开启 historyApiFallback,以支持 React Router。
配置 Babel
在项目根目录下创建 .babelrc
文件,并添加以下配置:
{ "presets": [ "@babel/preset-env", "@babel/preset-react" ] }
其中,我们配置了使用 @babel/preset-env
和 @babel/preset-react
处理 JS 和 JSX 文件。
编写客户端代码
在项目根目录下创建 src/client
目录,并在其中创建 index.js
文件和 index.html
文件。在 index.js
文件中,编写以下代码:
// javascriptcn.com 代码示例 import React from 'react'; import ReactDOM from 'react-dom'; import { BrowserRouter } from 'react-router-dom'; import { renderRoutes } from 'react-router-config'; import routes from './routes'; ReactDOM.hydrate( <BrowserRouter> {renderRoutes(routes)} </BrowserRouter>, document.getElementById('root') );
其中,我们使用 React Router 和 react-router-config
配置路由,并使用 hydrate
方法渲染页面。
编写服务器端代码
在项目根目录下创建 src/server
目录,并在其中创建 index.js
文件。在 index.js
文件中,编写以下代码:
// javascriptcn.com 代码示例 import Koa from 'koa'; import Router from 'koa-router'; import serve from 'koa-static'; import { matchRoutes } from 'react-router-config'; import { renderToString } from 'react-dom/server'; import { Helmet } from 'react-helmet'; import routes from '../client/routes'; import App from '../client/App'; const app = new Koa(); const router = new Router(); const PORT = process.env.PORT || 3000; router.get('*', async (ctx, next) => { const branch = matchRoutes(routes, ctx.url); const promises = branch.map(({ route, match }) => { const { fetchData } = route.component; return fetchData instanceof Function ? fetchData(match) : Promise.resolve(null); }); const data = await Promise.all(promises); const context = {}; const content = renderToString( <App data={data} location={ctx.url} context={context} /> ); const helmet = Helmet.renderStatic(); if (context.status === 404) { ctx.status = 404; } if (context.url) { ctx.redirect(context.url); } else { ctx.body = ` <!doctype html> <html ${helmet.htmlAttributes.toString()}> <head> ${helmet.title.toString()} ${helmet.meta.toString()} ${helmet.link.toString()} </head> <body ${helmet.bodyAttributes.toString()}> <div id="root">${content}</div> <script>window.__DATA__ = ${JSON.stringify(data)}</script> <script src="/bundle.js"></script> </body> </html> `; } await next(); }); app.use(serve('public')); app.use(router.routes()); app.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); });
其中,我们使用 Koa 和 Koa Router 搭建服务器,使用 React Router 和 react-router-config
配置路由,并使用 renderToString
方法渲染页面。我们还使用 react-helmet
处理页面标题、meta 等 SEO 相关信息,并使用 matchRoutes
方法获取当前路由相关信息。最后,我们使用 hydrate
方法渲染页面,并将数据传递给客户端。
编写路由配置
在 src/client
目录下创建 routes.js
文件,并添加以下代码:
// javascriptcn.com 代码示例 import Home from './pages/Home'; import About from './pages/About'; const routes = [ { path: '/', exact: true, component: Home, fetchData: () => Promise.resolve('Home') }, { path: '/about', exact: true, component: About, fetchData: () => Promise.resolve('About') } ]; export default routes;
其中,我们配置了两个路由,分别是 /
和 /about
,并分别对应了 Home
和 About
两个页面组件,并使用 fetchData
方法获取页面数据。
编写页面组件
在 src/client
目录下创建 pages
目录,并在其中创建 Home.js
和 About.js
文件。在 Home.js
和 About.js
文件中,分别编写以下代码:
// javascriptcn.com 代码示例 import React from 'react'; import { Helmet } from 'react-helmet'; import { Link } from 'react-router-dom'; const Home = ({ data }) => { return ( <> <Helmet> <title>Home</title> <meta name="description" content="This is home page" /> </Helmet> <h1>Home Page</h1> <p>{data}</p> <Link to="/about">Go to About Page</Link> </> ); }; Home.fetchData = () => Promise.resolve('Home Data'); export default Home;
// javascriptcn.com 代码示例 import React from 'react'; import { Helmet } from 'react-helmet'; import { Link } from 'react-router-dom'; const About = ({ data }) => { return ( <> <Helmet> <title>About</title> <meta name="description" content="This is about page" /> </Helmet> <h1>About Page</h1> <p>{data}</p> <Link to="/">Go to Home Page</Link> </> ); }; About.fetchData = () => Promise.resolve('About Data'); export default About;
其中,我们使用 react-helmet
处理页面标题、meta 等 SEO 相关信息,并使用 Link
组件实现页面之间的跳转。我们还使用 fetchData
方法获取页面数据,并将数据传递给服务器端。
运行项目
在命令行中执行以下命令,启动项目:
npm run dev
然后,在浏览器中访问 http://localhost:3000
,即可查看页面效果。
总结
本文介绍了如何使用 Koa+Webpack 搭建 React 服务器端渲染应用。通过本文的学习,我们可以了解到 SSR 的优势,以及如何使用 Koa、Webpack 和 React 等技术,实现 SSR 应用的搭建。同时,我们还了解了 React Router、react-router-config
、react-helmet
等相关技术,以及如何处理页面数据、SEO 等相关问题。希望本文对大家有所帮助。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/657e4453d2f5e1655d914dee