react koa rematch 如何打造一套服务端渲染架子

本次讲述的内容主要是 react 与 koa 搭建的一套 ssr 框架,是在别人造的轮子上再添加了一些自己的想法和完善一下自己的功能。

本次用到的技术为: react | rematch | react-router | koa

react服务端渲染优势

SPA(single page application)单页应用虽然在交互体验上比传统多页更友好,但它也有一个天生的缺陷,就是对搜索引擎不友好,不利于爬虫爬取数据(虽然听说chrome能够异步抓取spa页面数据了);

SSR与传统 SPA(Single-Page Application - 单页应用程序)相比,服务器端渲染(SSR)的优势主要在于:更好的 SEO 和首屏加载效果。

在 SPA 初始化的时候内容是一个空的 div,必须等待 js 下载完才开始渲染页面,但 SSR 就可以做到直接渲染html结构,极大地优化了首屏加载时间,但上帝是公平的,这种做法也增加了我们极大的开发成本,所以大家必须综合首屏时间对应用程序的重要程度来进行开发,或许还好更好地代替品(骨架屏)。

react服务端渲染流程

组件渲染

首先肯定是根组件的render,而这一部分和SPA有一些小不同。

使用 ReactDOM.render() 来混合服务端渲染的容器已经被弃用,并且会在React 17 中删除。使用hydrate() 来代替。

hydrate与 render 相同,但用于混合容器,该容器的HTML内容是由 ReactDOMServer 渲染的。 React 将尝试将事件监听器附加到现有的标记。

hydrate 描述的是 ReactDOM 复用 ReactDOMServer 服务端渲染的内容时尽可能保留结构,并补充事件绑定等 Client 特有内容的过程。

import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.hydrate(<App />, document.getElementById('app'));

在服务端中,我们可以通过 renderToString 来获取渲染的内容来替换 html 模版中的东西。

const jsx = <StaticRouter location={url} context={routerContext}> <AppRoutes context={defaultContext} initialData={data} /> </StaticRouter> const html = ReactDOMServer.renderToString(jsx); let ret = ` <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1, shrink-to-fit=no, user-scalable=no"> </head> <body> <div>${html}</div> </body> </html> `; return ret;

服务端返回替换后的 html 就完成了本次组件服务端渲染。

路由同步渲染

在项目中避免不了使用路由,而在SSR中,我们必须做到路由同步渲染。

首先我们可以把路由拆分成一个组件,服务端入口和客户端都可以分别引用。

function AppRoutes({ context, initialData }: any) { return ( <Switch> { routes.map((d: any) => ( <Route<InitRoute> key={d.path} exact={d.exact} path={d.path} init={d.init || ''} component={d.component} /> )) } <Route path='https://www.jb51.net/' component={Home} /> </Switch> ); }

(routes.js)

export const routes = [ { path: '/Home', component: Home, init: Home.init, exact: true, }, { path: '/Hello', component: Hello, init: Hello.init, exact: true, } ];

这样我们的路由基本定义完了,然后客户端引用还是老规矩,和SPA没什么区别

import { BrowserRouter as Router } from 'react-router-dom'; import AppRoutes from './AppRoutes'; class App extends Component<any, Readonly<State>> { ... render() { return ( <Router> <AppRoutes/> </Router> ); } }

在服务端中,需要使用将BrowserRouter 替换为 StaticRouter 区别在于,BrowserRouter 会通过HTML5 提供的 history API来保持页面与URL的同步,而StaticRouter 则不会改变URL,当一个 匹配时,它将把 context 对象传递给呈现为 staticContext 的组件。

const jsx = <StaticRouter location={url}> <AppRoutes /> </StaticRouter> const html = ReactDOMServer.renderToString(jsx);

至此,路由的同步已经完成了。

redux同构

在写这个之前必须先了解什么是注水和脱水,所谓脱水,就是服务器在构建 HTML 之前处理一些预请求,并且把数据注入html中返回给浏览器。而注水就是浏览器把这些数据当初始数据来初始化组件,以完成服务端与浏览器端数据的统一。

组件配置

在组件内部定义一个静态方法

class Home extends React.Component { ... public static init(store:any) { return store.dispatch.Home.incrementAsync(5); } componentDidMount() { const { incrementAsync }:any = this.props; incrementAsync(5); } render() { ... } } const mapStateToProps = (state:any) => { return { count: state.Home.count }; }; const mapDispatchToProps = (dispatch:any) => ({ incrementAsync: dispatch.Home.incrementAsync }); export default connect( mapStateToProps, mapDispatchToProps )(Home);

由于我这边使用的是rematch,所以我们的方法都写在model中。

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

转载注明出处:http://www.heiqu.com/52fcf2078a31f12ccaa9217428d14ac8.html