Next App - ssr redux
背景
最近在开发一个新项目的时候, 想尝试使用next.js
来实现服务端渲染, 但最终碰到一个比较棘手的问题, 最终还是使用回了create-react-app
客户端渲染, 这里记录下碰到的问题, 便于以后如果有解决方案了, 可以直接使用.
问题
在页面中, 用户信息和部分权限状态以前是保存在store
中的, 这样便于全局统一使用. 在csr
的情况下, 只需要在root
组件中, 统一处理相关加载和更新请求并保存数据变化到store
中即可. 但是在ssr
的场景下, 需要优化首屏渲染, 而且这部分数据也是可以通过server
端的请求写入store
的, 于是尝试下在服务端使用redux
并保存到客户端
同步store
数据
在服务端使用redux
生成store
数据并同步到客户端有两种方案:
获取server-store
单例
因为可能多个组件同时需要用到store
, 所以我们将store
实例化为单例, 并绑定在req
上, 保证唯一实例
1 | import { IncomingMessage } from 'http'; |
通过_app.tsx
全局同步
1 | App.getInitialProps = async (appCtx: AppContext) => { |
通过page.tsx
页面同步
1 | export const getServerSideProps: GetServerSideProps = async (ctx) => { |
上面两个方案都能实现首屏渲染的时候能正确获取到store
里的数据, 但是都有一些比较严重的问题:
- 通过
_app.tsx
全局同步- 每次页面切换, 都会看见有一个请求:
/_next/data/development/xxx.json
, 里面会携带storeState
, 是一份完整的数据, 这样实际上会出现的效果是: 服务端的store
数据会完全覆盖客户端store
数据, 即使做好相关数据的reducer
, 也会造成每个请求都会额外携带很多无用的信息, 而store
里如果保存了比较大的数据, 也会造成性能影响. - 如果客户端修改了
store
暂存到本地的数据, 或者某个特殊页面想额外获取一部分新的需要保存在store
中的数据, 在_app.tsx
里就很难处理 - 会导致这个问题: 静态优化失效
- 每次页面切换, 都会看见有一个请求:
- 通过
page.tsx
页面同步- 可以实现不同页面加载不同
store
的数据, 但是同样存在一个问题: 某些基础信息, 每个页面都需要, 维护起来就比较麻烦, 但相比起_app.tsx
好一点 - 同样也存在每次进入页面, 都会请求一次完整的数据, 即使客户端本地已经有了这份数据
- 某些依赖客户端动作修改
store
的功能无法实现: 如瀑布流加载/滚动加载更多等
- 可以实现不同页面加载不同
本质原因
碰到这个问题后思考了一下, 引起这个问题的本质原因是: 服务端无法实时获得客户端状态
比如上面出现的问题:
- 本地修改
store
无法同步到服务端 - 客户端需要额外参数请求的接口, 服务端无法获取
- 每个页面都需要请求一次完整的
store
数据
其实本质上都是一个原因: 服务端无法获取客户端store
当前数据
如果能解决这个问题, 那么后端可以根据store
的当前状态, 请求各自需要的数据, 并通过redux
同步派发到页面中, 就不会造成数据请求过多等性能问题. 而传统的csr
方式不会存在这个问题, 是因为在请求对应数据url
的时候, 相关状态已经通过请求参数传递过去了
解决方案
既然能确定问题, 那就有解决方案, 虽然不够优雅
- 通过
cookie
来解决- 如果
store
数据量不大, 将store
状态完整的通过cookie
传递, 虽然数据量不大, 但也会造成流量问题 - 将部分首屏渲染依赖数据通过
cookie
传递, 其他页面数据各自通过store
传递, 比上一种方案好一点, 但是开发起来非常麻烦, 并且cookie
到store
的数据同步也不是很简单的事情
- 如果
- 开源方案: next-redux-wrapper, 它实际上上做的事情和上面的方案是一样的, 其中提到
Persist
可以使用next-redux-cookie-wrapper来解决, 实际上也是使用cookie
来解决, 但是优化了数据传输部分
小结
最终由于这个状态同步的问题, 可以看出ssr
不太适合这种类型的项目, 更适合页面结构比较简单, 没有太多状态信息的应用, 但是这次研究仍然是非常有意义的, 彻底梳理清楚了和ssr
和csr
的优缺点, 可以为以后的项目选型提供参考
Next App - ssr redux