React和Express.js是现代web开发中最受欢迎的框架之一。其中React是一种用于构建用户界面的JavaScript库,而Express.js则是一个基于Node.js的Web应用程序框架。将两者结合使用可以创建出非常流畅的Web应用程序,但是这些应用程序有一个共同的问题:客户端渲染。
客户端渲染在某些方面是很好的,因为它使得前端极其灵活和快速,可以实现非常细致地动画和响应式设计。但是,在某些情况下,它会对SEO和性能产生负面影响。这时服务器端渲染(SSR)就派上用场了。
本文将介绍如何使用React和Express.js来实现服务器端渲染,以帮助您在web应用程序中获得最佳的性能和SEO效果。
什么是服务器端渲染?
服务器端渲染是指在服务器端生成HTML和CSS,并将其发送到客户端浏览器。这意味着在加载页面时,已经有一个完整的HTML页面可供搜索引擎索引,并且用户可以立即看到一个近乎完整的页面内容而不需要等待JavaScript脚本的加载和执行。
为什么需要服务器端渲染?
SEO优化:搜索引擎会爬取网站的HTML,CSS和JavaScript,然后将其存储在它们的数据库中以供索引。服务器端渲染可以使得搜索引擎抓取到最完整的HTML代码,从而提高网站的SEO。
首页加载性能:现代web应用程序通常是前端框架驱动的,因此大多数JS框架创建DOM和渲染控制都是用JavaScript来完成。当JavaScript加载和初始化完成之前,页面会是空的或者至少有一段无意义的文本,在此期间用户无法理解网站内容。当大量用户访问网站时,这样的情况就会成为首屏闪烁导致用户体验不佳的根本原因。
服务端生成UI:服务器端渲染现在在动态内容生成和单页应用上已经成为一个标配。它允许在每个页面中实现动态数据和代码切换以及在不提供服务器代码的情况下处理交互。
准备工作
在开始实现服务器端渲染之前,需要安装几个依赖项。首先应该有一个现成的React应用程序,并且需要添加以下依赖项:
yarn add express react react-dom babelify
其中babelify是一种babel转换工具,它可以用于将jsx和ES6语法转换为浏览器可理解的代码。
创建Express.js服务器
在项目根目录中,创建一个名为
server.js
的新文件。从Express导入
express
;从React和ReactDOM导入
React
和ReactDOM
;从文件系统模块(“fs”)导入
fs
;使用
express()
创建应用程序,将其存储在该变量中。设置Express应用程序的渲染引擎可以使用JavaScript(使用
express-react-views
作为视图引擎)
// javascriptcn.com 代码示例 const express = require('express'); const React = require('react'); const ReactDOMServer = require('react-dom/server'); const fs = require('fs'); const app = express(); app.set('views', __dirname + '/views'); app.set('view engine', 'jsx'); app.engine('jsx', require('express-react-views').createEngine());
从组件生成静态HTML
- 创建一个React组件,在应用程序的根目录中创建一个名为
HelloMessage.jsx
的新文件,并添加以下代码:
// javascriptcn.com 代码示例 import React from 'react'; class HelloMessage extends React.Component { render() { return ( <html> <head> <title>Hello React!</title> </head> <body> <div>Hello {this.props.name}!</div> </body> </html> ); } } module.exports = HelloMessage;
- 在服务器端
/
路径上渲染组件,并将其发送回客户端浏览器。在server.js
文件中添加以下代码:
const HelloMessage = require('./HelloMessage'); app.get('/', (req, res) => { const name = req.query.name || 'World'; const html = ReactDOMServer.renderToStaticMarkup(<HelloMessage name={name} />); res.send(html); });
上面的代码创建一个名为HelloMessage
的React组件,该组件将从URL查询参数中获取“name”值,并在渲染时将其添加到你好文本中。
用Webpack打包React组件
下一步是将React代码应用于浏览器。为此,我们将使用Webpack打包我们的代码。Webpack是一个强大的JavaScript模块打包器,它可以处理CSS、图片和其他资产,还可以将代码优化为更小的文件,并将代码交给客户端浏览器进行处理。
- 首先,安装Webpack和一些相应的依赖项:
yarn add webpack webpack-cli webpack-node-externals babel-loader @babel/preset-react @babel/preset-env
Webpack需要知道我们想要打包的React代码,并为其生成浏览器可用的JavaScript模块。我们还需要告诉Webpack如何处理jsx和ES6代码,这是使用babel-loader完成的。
- 在根目录中创建一个新文件
.babelrc
,其中包含以下JSON代码:
{ "presets": ["@babel/preset-env", "@babel/preset-react"] }
上述代码告诉Babel如何处理ES6和jsx代码。
- 在根目录中创建一个名为webpack.config.js的新文件,并添加以下代码:
// javascriptcn.com 代码示例 const path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { entry: './server.js', output: { filename: 'server.bundle.js', path: path.join(__dirname, '/dist') }, target: 'node', externals: [nodeExternals()], module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] } };
这是让我们告诉Webpack如何打包我们的React代码的配置文件。
更新server.js
- 在
server.js
文件最上面添加以下代码:
require('@babel/register')({ presets: ['@babel/preset-env', '@babel/preset-react'] });
此代码告诉Node.js使用Babel处理服务器代码和React代码。
- 添加Webpack和必需的依赖项:
const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.config.js'); const compiler = webpack(config);
在应用程序中配置Webpack的热重载和开发服务器,以便在进行更改时自动更新浏览器。这是使用webpack-dev-middleware
完成的。
app.use(webpackDevMiddleware(compiler, { serverSideRender: true }));
最后,在server.js
中添加以下代码:
// javascriptcn.com 代码示例 app.get('/', (req, res) => { const name = req.query.name || 'World'; const html = ReactDOMServer.renderToStaticMarkup(<HelloMessage name={name} />); res.send(html); }); app.get('*', (req, res) => { const name = req.query.name || 'World'; const html = ReactDOMServer.renderToStaticMarkup(<HelloMessage name={name} />); res.render('index', { title: 'React SSR', name: name, html: html }); }); app.listen(3000, () => console.log('Server is listening on port 3000'));
我们通过两个路由来演示服务器端渲染的优点。其中一个在我们刚刚演示过的地方,这是SSR服务器端渲染,我们将HelloMessage
组件渲染为HTML并将其发送到客户端浏览器。
第二个路由在所有其他路径上都运行,其中我们设置一个*
通配符。在这个路由上,我们使用res.render()
从服务器端渲染我们的React组件。
总结
通过使用React和Express.js实现服务器端渲染,我们可以大大提高我们的应用程序的性能和SEO效果。在本文中,我们了解了服务器端渲染的优点,并演示了实现服务器端渲染的步骤。现在,我们可以为React和Express.js应用程序添加服务器端渲染,并开始构建更快速和更具SEO的Web应用程序。
完整代码如下所示:
// javascriptcn.com 代码示例 // HelloMessage.jsx import React from 'react'; class HelloMessage extends React.Component { render() { return ( <html> <head> <title>Hello React!</title> </head> <body> <div>Hello {this.props.name}!</div> </body> </html> ); } } module.exports = HelloMessage; // server.js const express = require('express'); const React = require('react'); const ReactDOMServer = require('react-dom/server'); const fs = require('fs'); const webpack = require('webpack'); const webpackDevMiddleware = require('webpack-dev-middleware'); const config = require('./webpack.config.js'); const compiler = webpack(config); require('@babel/register')({ presets: ['@babel/preset-env', '@babel/preset-react'] }); const app = express(); app.set('views', __dirname + '/views'); app.set('view engine', 'jsx'); app.engine('jsx', require('express-react-views').createEngine()); app.use( webpackDevMiddleware(compiler, { serverSideRender: true }) ); const HelloMessage = require('./HelloMessage'); app.get('/', (req, res) => { const name = req.query.name || 'World'; const html = ReactDOMServer.renderToStaticMarkup(<HelloMessage name={name} />); res.send(html); }); app.get('*', (req, res) => { const name = req.query.name || 'World'; const html = ReactDOMServer.renderToStaticMarkup(<HelloMessage name={name} />); res.render('index', { title: 'React SSR', name: name, html: html }); }); app.listen(3000, () => console.log('Server is listening on port 3000')); // webpack.config.js const path = require('path'); const nodeExternals = require('webpack-node-externals'); module.exports = { entry: './server.js', output: { filename: 'server.bundle.js', path: path.join(__dirname, '/dist') }, target: 'node', externals: [nodeExternals()], module: { rules: [ { test: /\.js$/, exclude: /node_modules/, use: { loader: 'babel-loader' } }, { test: /\.jsx$/, exclude: /node_modules/, use: { loader: 'babel-loader' } } ] } };
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652c8f967d4982a6ebe3c55d