写在前面
前段时间一直在研究react ssr 技术,也很想自己写一个 ssr开发骨架,有了自己的骨架后就不需要在用 ejs 模板了,直接用 jsx 就可以了,技术栈也就统一了,那真的很爽。
基于我之前了解的一点点ssr 原理就直开干,在实现的过程中的坑还真不少,但是也没有什么太难得东西,主要是我采用的是 react router5 ,对这个新版的路由使用不太熟悉,又和 react router3的差别较大,导致耗费的时间多了点。另外一个就是动态路由(路由分包)的处理,这个需要在 node 端和浏览器端都需要做处理,才能保证最终渲染的节点对比正确,不然会导致浏览器端会重新渲染。另外一个就是要自己实现工程化,这个也是比较繁琐的。
其他的就是数据脱水注水,组件查找,以及一些基础能力的支持(方便使用者开发)。
从开始想做到现在开发完,都是在不忙的时候写写,差不多过去了2个月了。现在这个 ssr 骨架基本完成,我也写了一个 demo可以看看。
krs - ssr 骨架介绍
krs几个特性介绍
最清凉(轻量)的 react ssr 应用开发骨架
上手快速: 都是你熟悉的事物,基于 koa2 react16 ssr 搭建
双模式无缝切换: 支持SSR/CSR两种渲染模式,只需更改配置属性即可,也可以对组件设置按需渲染模式
路由分治管理: 你写你的路由,我写我的路由,krs 自动合并,不再需要维护整个路由表
路由动静结合: 支持组件的按需加载设置,A 路由动态吧,B 路由静态
伪 pwa 支持: 访问过的路由中的 state 可按需设置本地缓存,页面二次访问可无接口请求
开放: 代码完全开放,纯白盒,完全可以作为个人的
ssr
学习参考资源
快速开始
//创建项目
$ npm install mmkrs-cli -g
$ mmkrs -i ---> select project ---> <Your Project Name>
$ cd <Your Project Name>
$ npm i
$ npm run dev //本地开发监听模式
$ open http://<Your local ip>:8808
//快速创建页面
$ cd <Your Project Name>
$ mmkrs -i -----> select page ----> <Your pageName>
$ open http://<Your local ip>:8808/<Your pageName>
//结束
路由配置
如果你想配置一个页面的路由地址,那该如何配置呢?
为了方便维护和扩展,krs 把路由进行了分治管理,每个页面的路由都是独立的,只需要单独的配置即可,避免了多人维护带来的冲突和一系列的问题。
-
在
src/pages
目录下创建一个页面目录 如:detail -
在
detail/
内创建入口组件 -
在
detail/config
内创建route.js
这就是当前页面的路由配置文件
import React from 'react';
import BaseBundle from '../../../routes/route-base-bundle';
const LazyPageCom = (props) => (
<BaseBundle load={() => import(/*webpackChunkName:"chunk-detail"*/'../index')}>
{(CompIndex) => <CompIndex {...props} />}
</BaseBundle>
);
export default [
{
path:'/detail',
component: LazyPageCom,
exact:true
},
{
path:'/detail/:id',
component: LazyPageCom
}
]
你只需要修改 webpackChunkName
的名称和 export
导出的参数即可,当然也可以对当前页面配置多个路由,默认已经支持了路由按需,所以如果不需要的话可以直接指向原始组件即可。
数据预取
需要继承一个 krs 的基础组件,为我们封装了一些基础数据获取和存储功能
声明静态数据预取方法 static async getInitialProps,数据的获取就是从这个方法拿到的,这是一个同构方法 node 端和浏览器端都会调用
设置 static async getInitialProps 的返回数据,返回数据有一个固定的格式,下面代码会说明
componentDidMount内是否需要做数据的更新,如果需要更新可以调用getInitialProps方法
import React,{useContext} from 'react';
import { Link } from 'react-router-dom';
import RootContext from '../../app/route-context';//自定义 context
import KrsPageBase from '../../krs-base/common/components/krs-page-base';//基础组件 页面组件都需要继承
import fetch from '../../common/fetch';//内置的 fech 模块
export default class Index extends KrsPageBase{
constructor(props,context){
super(props,context);
}
enableSpaDataCache=true;//开启 伪 pwa 数据缓存
//得到 context 对象
static contextType = RootContext;
//基础参数的带入
//opt={query:{},params:{}}
static async getInitialProps(krsOpt){//数据预取
if(__SERVER__){
//如果是服务端渲染的话 可以做的处理
}
const fetch1= fetch.postForm('/fe_api/a', {
data: { a: 4000 }
});
const fecth2= fetch.postForm('/fe_api/b', {
data: { c: 2000 }
});
const resArr =await fetch.multipleFetch(fetch1, fecth2);
//返回数据固定格式 page 代表页面信息,支持 seo 的设置
//fetchData是接口返回的数据
return {
page:{
tdk: {
title: 'ksr 框架',
keyword: 'ssr react',
description: '我是描述'
}
},
fetchData: resArr
}
}
componentDidMount(){
//数据更新 参考
//this.isSSR 标识当前页面是否是 ssr 输出
//this.hasSpaCacheData标识是否有伪 pwa 的缓存数据
if (!this.isSSR && !this.hasSpaCacheData){// 页面如果是客户端的需要重新获取数据
Index.getInitialProps(this.props.krsOpt).then(data=>{
this.setState({
...data
},()=>{
document.title=this.state.page.tdk.title;
});
});
}
}
render(){
const {page,fetchData}=this.state;//获得数据
//参考代码,需要对数据做边界容错处理
return <div className="detailBox">
<div>
{
page && <div><span>title:{page.tdk.title}</span>
<span>ky:{page.tdk.keyword}</span>
</div>
}
</div>
{
res && res.data.map(item=>{
return <div key={item.id}>{item.keyId}:{item.keyName}---{item.setContent}</div>
})
}
</div>
}
}
快捷键
cd 项目目录
mmkrs-cli -i --->select page ---> 输入 pagename
操作完后就可以看到你配置的页面路由已生效。
生产环境构建
npm run build
然后可以本地模拟查看:npm run build:start
生产环境部署
这个很简单,只需要运行 根目录的 app.js
即可
pm2 start app.js
更多自定义配置
/src/config/project-config.js
内进行配置
Demo 演示(有点丑,别介意)
http://demo.krs.bigerfe.com 最好在 pc 上访问
项目 github 地址:
https://github.com/Bigerfe/koa-react-ssr