React服务端渲染原理解析与实践

关于服务端渲染也就是我们说的SSR大多数人都听过这个概念,很多同学或许在公司中已经做过服务端渲染的项目了,主流的单页面应用比如说Vue或者React开发的项目采用的一般都是客户端渲染的模式也就是我们说的CSR。

但是这种模式会带来明显的两个问题,第一个就是TTFP时间比较长,TTFP指的就是首屏展示时间,同时不具备SEO排名的条件,搜索引擎上排名不是很好。所以我们可以借助一些工具来进行改良我们的项目,将单页面应用编程服务器端渲染项目,这样就可以解决掉这些问题了。

目前主流的服务器端渲染框架也就是SSR框架有针对于Vue的Nuxt.js和针对React的Next.js这两个。这里我们并不使用这些SSR框架,而是从零开始完整搭建一套SSR框架,来熟悉他的底层原理。

服务器端编写 React 组件

如果是客户端渲染,浏览器首先会向浏览器发送请求,服务器返回页面的html文件,然后html中再向服务器发送请求,服务器返回js文件,js文件在浏览器中执行绘制出页面结构渲染到浏览器完成页面渲染。

如果是服务器端渲染这个流程就不同了,浏览器发送请求,服务器端运行React代码生成页面,然后服务器将生成好的页面返回给浏览器,浏览器进行渲染。这种情况下React代码就是服务器的一部分而不是前端部分了。

这里我们进行代码的演示,首选需要npm init初始化项目,然后安装react,express,webpack,webpack-cli,webpack-node-externals。

我们首先编写一个React的组件。 .src/components/Home/index.js, 因为我们这个js是在node环境执行的所以我们要遵循CommonJS规范,使用require和module.exports进行导入导出。

const React = require('react'); const Home = () => { return <div>home</div> } module.exports = { default: Home };

我们这里开发的Home组件是不能直接在node中运行的,需要借助webpack工具将jsx语法打包编译成js语法,让nodejs可以争取的识别,我们需要创建一个webpack.server.js文件。

在服务器端使用webpack需要添加一个target为node的键值对。我们知道在服务器端如果使用path路径是不需要打包到js中的,如果在浏览器端使用了path是需要打包到js中的,所以在服务器端和在浏览器端需要编译出来的js是完全不同的。所以我们在打包的时候要告诉webpack打包的是服务器端的代码还是浏览器端的代码。

entry入口文件就是我们node的启动文件,这里我们写成./src/index.js,输出的output文件名称为bundle,目录在跟目录的build文件夹中。

const Path = require('path'); const NodeExternals = require('webpack-node-externals'); // 服务端运行webpack需要运行NodeExternals, 他的作用是将express这类node模块不被打包到js里。 module.exports = { target: 'node', mode: 'development', entry: './src/server/index.js', output: { filename: 'bundle.js', path: Path.resolve(__dirname, 'build') }, externals: [NodeExternals()], module: { rules: [ { test: /.js?$/, loader: 'babel-loader', exclude: /node_modules/, options: { presets: ['react', 'stage-0', ['env', { targets: { browsers: ['last 2 versions'] } }]] } } ] } }

安装依赖模块

npm install babel-loader babel-core babel-preset-react babel-preset-stage-0 babel-preset-env --save

接着我们这里基于express模块来编写一个简单的服务。./src/server/index.js

var express = require('express'); var app = express(); const Home = require('../Components/Home'); app.get('*', function(req, res) { res.send(`<h1>hello</h1>`); }) var server = app.listen(3000);

运行webpack使用webpack.server.js配置文件来执行。

webpack --config webpack.server.js

打包之后在我们的目录下会出现一个bundle.js,这个js就是我们打包生成的最终可以运行的代码。我们可以使用node运行这个文件, 就启动了一个3000端口的服务器。我们访问127.0.0.1:3000可以访问这个服务,看到浏览器输出Hello。

node ./build/bundile.js

上面的代码我们运行前会使用webpack进行编译,所以也就支持了ES Modules规范,不再强制使用CommonJS了。

src/components/Home/index.js

import React from 'react'; const Home = () => { return <div>home</div> } export default Home;

/src/server/index.js中我们可以使用Home组件,这里我们首先需要安装react-dom,借助renderToString将Home组件转换为标签字符串,当然这里需要依赖React所以我们需要引入React。

import express from 'express'; import Home from '../Components/Home'; import React from 'react'; import { renderToString } from 'react-dom/server'; const app = express(); const content = renderToString(<Home />); app.get('*', function(req, res) { res.send(` <html> <body>${content}</body> </html> `); }) var server = app.listen(3000);

# 重新打包 webpack --config webpack.server.js # 运行服务 node ./build/bundile.js

这时候页面就显示出了我们React组件的代码。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsgszs.html