注:本文不考虑 umi 、vite 等情况。
场景:运营后台,必须获取一个有效用户数据结构才能进入,且没有登入功能(登入功能在用户中心),用户数据通过一个接口取得。
基于 vue 的 nuxtjs 框架,直接就有 middleware 这种东西,可以做路由拦截的同时,也可以做数据预载,比如在进入某个页面组件之前,先获取这个组件的数据,完事后直接塞到组件的 props 里面去;缺点就是如果这个接口响应时间太长,就会有明显的“卡屏”感受,一般也是会单独做一个加载的提示组件告诉用户没卡。在使用 nuxtjs 的时候,直接在根组件上获取数据,等待完成后再进入具体的页面组件,体验非常接近传统的服务端渲染,用户打开页面的时候,他需要的一切都已经准备妥当。
在我接触到的范围内,nextjs 已经这么多年了,但没有加上路由守护功能,无法在首次加载时做路由拦截,比如没有获取到有效数据则重定向到用户中心。且 nextjs 不能像 nuxtjs 那样在 App 组件上使用 vuex(recoil),也就不能在定义 atom 的时候获取数据。
经过 3 个小时的测试,我给出这样一个办法:
/* _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 之外,再提供一个包裹层,把这个包裹层当 "App" 组件用,这样就可以在 "App" 组件上使用 recoil 了,并且在这里请求用户数据写到 recoil 里面去,整个网站都将获得对用户数据的访问能力。
以上代码已经在生产环境跑了一周了,除了部分用户有些不习惯(之前是直接进入登入页,现在需要多点一次)以外没有其它任何问题。
大概是需求太奇葩?我来回翻 nextjs 的 issue 只找到几个与我有类似需求的情况,当然都被无视掉了。单位的钱不好赚啊。