十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
这篇文章主要介绍了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(, document.getElementById('app'));
在服务端中,我们可以通过 renderToString 来获取渲染的内容来替换 html 模版中的东西。
const jsx =const html = ReactDOMServer.renderToString(jsx); let ret = ` ${html}
服务端返回替换后的 html 就完成了本次组件服务端渲染。
路由同步渲染
在项目中避免不了使用路由,而在SSR中,我们必须做到路由同步渲染。
首先我们可以把路由拆分成一个组件,服务端入口和客户端都可以分别引用。
function AppRoutes({ context, initialData }: any) { return ({ routes.map((d: any) => ( ); }key={d.path} exact={d.exact} path={d.path} init={d.init || ''} component={d.component} /> )) }
(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> { ... render() { return ( ); } }
在服务端中,需要使用将BrowserRouter 替换为 StaticRouter 区别在于,BrowserRouter 会通过HTML5 提供的 history API来保持页面与URL的同步,而StaticRouter 则不会改变URL,当一个 匹配时,它将把 context 对象传递给呈现为 staticContext 的组件。
const jsx =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中。
const Home: ModelConfig= { state: { count: 1 }, reducers: { increment(state, payload) { return { count: payload }; } }, effects: { async incrementAsync(payload, rootState) { await new Promise((resolve) => setTimeout(resolve, 1000)); this.increment(payload); } } }; export default Home;
然后通过根 store 中进行 init。
import { init } from '@rematch/core'; import models from './models'; const store = init({ models: {...models} }); export default store;
然后可以绑定在我们 redux 的 Provider 中。
路由方面我们需要把组件的 init 方法绑定在路由上方便服务端请求数据时使用。
{ routes.map((d: any) => ( key={d.path} exact={d.exact} path={d.path} init={d.init || ''} component={d.component} /> )) }
以上就是客户端需要进行的操作了,因为 SSR 中我们服务端也需要进行数据的操作,所以为了解耦,我们就新建另一个 ServiceStore 来提供服务端使用。
在服务端构建 Html 前,我们必须先执行完当前组件的 init 方法。
import { matchRoutes } from 'react-router-config'; // 用matchRoutes方法获取匹配到的路由对应的组件数组 const matchedRoutes = matchRoutes(routes, url); const promises = []; for (const item of matchedRoutes) { if (item.route.init) { const promise = new Promise((resolve, reject) => { item.route.init(serverStore).then(resolve).catch(resolve); }); promises.push(promise); } } return Promise.all(promises);
注意我们新建一个 Promise 的数组来放置 init 方法,因为一个页面可能是由多个组件组成的,我们必须等待所有的 init 执行完毕后才执行相应的 html 构建。
现在可以得到的数据挂在 window 下,等待客户端的读取了。
let ret = `${html}
那这样就大功告成啦!!!!
我来说一下在做这个的时候遇到的坑
1.不能使用分离 css 的插件 mini-css-extract-plugin ,因为分离 css 和把 css 放置到 style 中会有冲突,引入github大神的一句话
With isomorphic-style-loader the idea was to always include css into js files but render into dom only critical css and also make this solution universal (works the same on client and server side). If you want to extract css into separate files you probably need to find another way how to generate critical css rather than use isomorphic-style-loader.
2.很多文章说到在 service 端的打包中不需要打包 css,那是因为他们使用的是style-loader 的情况,我们如果使用 isomorphic-style-loader, 我们也需要把 css 打包一下,因为我们在服务端中毕竟要触发 withStyle。
感谢你能够认真阅读完这篇文章,希望小编分享的“react koa rematch怎么打造一套服务端渲染架子”这篇文章对大家有帮助,同时也希望大家多多支持创新互联,关注创新互联行业资讯频道,更多相关知识等着你来学习!