react同构实践之实现自己的同构模板

一开始想学学服务端渲染,脑海中第一个浮现出来的就是next.js这种成熟的方案。看了一两天,有趣,优雅,但是封装好了,原理不甚清楚,也感觉无法灵活嵌合到老项目上去。于是看各种资料,想整理出同构的线索,一步一步地实现自己的同构模板。相关代码可查看我的GitHub。感谢阅读!!

TODO List

数据:如何保持前后端应用状态一致

路由:路由在服务端和客户端中的匹配方案

代码:同构,哪些地方可以共享,哪些地方需要差异化

静态资源:服务端如何引入css/图片等

ssr直出资源:服务端在渲染路由页面时如何匹配css/chunks资源

打包方案:服务端和浏览器端如何写各自的webpack配置文件

SEO: head头处理方案

同构的基础

正常的网页运行,需要生成dom,在dom树loaded之后由js绑定相关的dom事件,监听页面的交互。服务端并不具备dom的执行环境,因而所有的服务端渲染其实都是返回了一个填充了初始数据的静态文本。在react中,除了常用的render这个用于生成dom的方法,还提供了renderToString,renderToStaticMarkup方法用来生成字符串,由于VitualDOM的存在,结合这些方法就可以像以前的字符串模板那样生成普通的字符串,返回给客户端接管,再接着进行事件相关的绑定。最新的React v16+使用hydrate和ssr配套,能让客户端把服务端的VitualDOM渲染出来后得以复用,客户端加载js后不会重刷一边,减小了开销,也避免浏览器重刷dom时带来的闪屏体验。而react的组件,还是和往常写spa一样编写,前后端共享。不同的只是入口的渲染方法换了名字,且客户端会挂载dom而已。

// clinet.js ReactDom.hydrate(<App />, document.getElementById('app')) // server.js const html = ReactDom.renderToString(<App />)

同构后网站运行流程图

盗用一张图,来自阿里前端。乍一看,ssr与csr的区别就在于2 3 4 5,spa模式简单粗暴地返回一个空白的html页面,然后在11里才去加载数据进行页面填充,在此之前,页面都处于空白状态。而ssr则会根据路由信息,提前获取该路由页面的初始数据,返回页面时已经有了初步的内容,不至于空白,也便于搜索引擎收录。

react同构实践之实现自己的同构模板

路由匹配

浏览器端的路由匹配还是照着spa来做应该无需费心。略过了...

服务端的路由需要关注的,一个是后端服务的路由(如koa-router)匹配的问题,一个是匹配到react应用后react-router路由表的匹配问题。

服务端路由,可通过/react前缀来和api接口等其他区别开来,这种路由匹配方式甚至能让服务端渲染能同时支持老项目诸如ejs等的模板渲染方式,在系统升级改造方面可实现渐进式地升级。

// app.js文件(后端入口) import reactController from './controllers/react-controller' // API路由 app.use(apiController.routes()) // ejs页面路由 app.use(ejsController.routes()) // react页面路由 app.use(reactController.routes()) // react-controller.js文件 import Router from 'koa-router' const router = new Router({ prefix: '/react' }) router.all('https://www.jb51.net/', async (ctx, next) => { const html = await render(ctx) ctx.body = html }) export default router

react-router专供了给ssr使用的StaticRouter接口,称之为静态的路由。诚然,服务端不像客户端,对应于一次网络请求,路由就是当前的请求url,是唯一的,不变的。在返回ssr直出的页面后,页面交互造成地址栏的变化,只要用的是react-router提供的方法,无论是hash方式,还是history方式,都属于浏览器端react-router的工作了,于是完美继承了spa的优势。只有在输入栏敲击Enter,才会发起新一轮的后台请求。

import { StaticRouter } from 'react-router-dom' const App = () => { return ( <Provider store={store}> <StaticRouter location={ctx.url} context={context}> <Layout /> </StaticRouter> </Provider> ) }

应用状态数据管理

以往的服务端渲染,需要在客户端网页下载后马上能看到的数据就放在服务器提前准备好,可延迟展示,通过ajax请求的数据的交互逻辑放在页面加载的js文件中去。

换成了react,其实套路也是一样一样的。但是区别在于:

传统的字符串模板,组件模板是彼此分离的,可各自单独引入数据,再拼装起来形成一份html。而在react的ssr里,页面只能通过defaultValue和defaultProps一次性render,无法rerender。

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

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