NEXT.js 静态生成模式实现预载数据再渲染
NEXT.js 静态生成模式实现预载数据再渲染

运营后台场景,必须获取一个有效用户数据结构才能进入,且没有登入功能,用户数据通过外部用户中心接口取得。

基于 vue 的 nuxt 框架,直接就有 middleware 这种东西,可以做路由拦截的同时,也可以做数据预载,比如在进入某个页面组件之前,先获取这个组件的数据,完事后直接塞到组件的 props 里面去;缺点就是如果这个接口响应时间太长,就会有明显的卡屏感受,一般也是会单独做一个加载的提示组件告诉用户没卡。在使用 nuxt 的时候,直接在根组件上获取数据,等待完成后再进入具体的页面组件,体验非常接近传统的服务端渲染,用户打开页面的时候,他需要的一切都已经准备妥当。

在我接触到的范围内,next 已经这么多年了,但没有加上路由守护功能,无法在首次加载时做路由拦截,比如没有获取到有效数据则重定向到用户中心。且 next 不能像 nuxt 那样在根组件上使用状态管理,也就不能在定义 atom 的时候获取数据(其实是可以的,但是生成时固定数据)。

经过 1 个小时的测试,我想出这样一个办法:

javascript/* _app.js */
// 此组件内不能使用 recoil 的 hook

import {RecoilRoot} from 'recoil';
import NextWrap from '_wrap';

export default function App({Component,pageProps,router}){
  return <RecoilRoot>
    <NextWrap Component={Component} pageProps={pageProps} router={router} />
  </RecoilRoot>;
};


/* _wrap.js */
import {useRecoilState} from 'recoil';
import sessionAtom from 'store/user/session';
import {useEffect, useState} from 'react';

export default function NextWrap({Component,pageProps,router}){
  const [session,setSession]=useRecoilState(sessionAtom);
  const [hasError,setHasError]=useState(false);

  if(session.id<1 || !session.username){
    fetch(usercenter).then(function(data){
      setSession({id:data.id,username:data.username});
    }).catch(function(exception){
      setHasError(true);
    });
    // error-alert-component 组件上可以显示到用户中心的链接,由用户主动跳转过去
    return hasError ? <error-alert-component /> : <loading-data-component />;
  }

  return <Component {...pageProps} router={router} />;
};

在 App 之内,在 Pages 之外,再提供一个包裹层,把这个包裹层当根组件用,这样就可以在根组件上使用 recoil 了,并且在这里请求用户数据写到 recoil 里面去,整个网站都将获得对用户数据的访问能力。

以上代码已经在生产环境跑了一周了,除了部分用户有些不习惯(之前是如果没登入的话直接进入用户中心登入页,现在需要手动多点一次)以外没有其它任何问题。

大概是需求太奇葩,我来回翻 next 的 issue 只找到几个与我有类似需求的情况,当然都被无视掉了。单位的钱不好赚啊。

作者
ragnaroks
发布时间
2021-09-06
创作协议