发布时间:2022-11-28 18:30
前段时间一直在研究 react ssr
技术,然后写了一个完整的 ssr
开发骨架。今天写文,主要是把我的研究成果的精华内容整理落地,另外通过再次梳理希望发现更多优化的地方,也希望可以让更多的人少踩一些坑,让更多的人理解和掌握这个技术。
相信看过本文(前提是能对你的胃口,也能较好的消化吸收)你一定会对 react ssr
服务端渲染技术有一个深入的理解,可以打造自己的脚手架,更可以用来改造自己的实际项目,当然这不仅限于 react
,其他框架都一样,毕竟原理都是相似的。
至于为什么要服务端渲染,我相信大家都有所闻,而且每个人都能说出几点来。
在 SPA 模式下,所有的数据请求和 Dom 渲染都在浏览器端完成,所以当我们第一次访问页面的时候很可能会存在“白屏”等待,而服务端渲染所有数据请求和 html内容已在服务端处理完成,浏览器收到的是完整的 html 内容,可以更快的看到渲染内容,在服务端完成数据请求肯定是要比在浏览器端效率要高的多。
有些网站的流量来源主要还是靠搜索引擎,所以网站的 SEO 还是很重要的,而 SPA 模式对搜索引擎不够友好,要想彻底解决这个问题只能采用服务端直出。改变不了别人(搜索yinqing),只能改变自己。
只实现 SSR
其实没啥意义,技术上没有任何发展和进步,否则 SPA
技术就不会出现。
但是单纯的 SPA
又不够完美,所以最好的方案就是这两种体验和技术的结合,第一次访问页面是服务端渲染,基于第一次访问后续的交互就是 SPA
的效果和体验,还不影响 SEO
效果,这就有点完美了。
单纯实现 ssr
很简单,毕竟这是传统技术,也不分语言,随便用 php 、jsp、asp、node 等都可以实现。
但是要实现两种技术的结合,同时可以最大限度的重用代码(同构),减少开发维护成本,那就需要采用 react
或者 vue
等前端框架相结合 node(ssr)
来实现。
本文主要说 ReactSSR技术
,当然 vue
也一样,只是技术栈不同而已。
整体来说 react
服务端渲染原理不复杂,其中最核心的内容就是同构。
node server
接收客户端请求,得到当前的 req url path
,然后在已有的路由表内查找到对应的组件,拿到需要请求的数据,将数据作为 props
、 context
或者store
形式传入组件,然后基于 react
内置的服务端渲染api renderToString()orrenderToNodeStream()
把组件渲染为 html字符串
或者 stream流
, 在把最终的 html
进行输出前需要将数据注入到浏览器端(注水),server 输出(response)后浏览器端可以得到数据(脱水),浏览器开始进行渲染和节点对比,然后执行组件的 componentDidMount
完成组件内事件绑定和一些交互,浏览器重用了服务端输出的 html节点
,整个流程结束。
技术点确实不少,但更多的是架构和工程层面的,需要把各个知识点进行链接和整合。
实现 ssr 很简单,先看一个 node ejs
的栗子。
// index.html
react ssr <%= title %>
<%= data %>
//node ssr
const ejs = require('ejs');
const http = require('http');
http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, {
'Content-Type': 'text/html'
});
// 渲染文件 index.ejs
ejs.renderFile('./views/index.ejs', {
title: 'react ssr',
data: '首页'},
(err, data) => {
if (err ) {
console.log(err);
} else {
res.end(data);
}
})
}
}).listen(8080);
上面我们结合 ejs模板引擎
,实现了一个服务端渲染的输出,html 和 数据直接输出到客户端。
参考以上,我们结合 react组件
来实现服务端渲染直出,使用 jsx
来代替 ejs
,之前是在 html 里使用 ejs
来绑定数据,现在改写成使用 jsx
来绑定数据,使用 react 内置 api 来把组件渲染为 html 字符串,其他没有差别。
为什么react 组件可以被转换为 html字符串呢?
简单的说我们写的 jsx 看上去就像在写 html(其实写的是对象) 标签,其实经过编译后都会转换成 React.createElement
方法,最终会被转换成一个对象(虚拟DOM),而且和平台无关,有了这个对象,想转换成什么那就看心情了。
const React = require('react');
const { renderToString} = require( 'react-dom/server');
const http = require('http');
//组件
class Index extends React.Component{
constructor(props){
super(props);
}
render(){
return {this.props.data.title}
}
}
//模拟数据的获取
const fetch = function () {
return {
title:'react ssr',
data:[]
}
}
//服务
http.createServer((req, res) => {
if (req.url === '/') {
res.writeHead(200, {
'Content-Type': 'text/html'
});
const data = fetch();
const html = renderToString();
res.end(html);
}
}).listen(8080);
ps:以上代码不能直接运行,需要结合babel 使用 @babel/preset-react 进行转换
npx babel script.js --out-file script-compiled.js --presets=@babel/preset-react
在上面非常简单的就是实现了 react ssr
,把 jsx
作为模板引擎,不要小看上面的一小段代码,他可以帮我们引出一系列的问题,这也是完整实现 react ssr
的基石。
双端路由如何维护?