
注:本文不考虑 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 只找到几个与我有类似需求的情况,当然都被无视掉了。单位的钱不好赚啊。