昨天我在思否上看到这个问题:nuxt3 请求 token 要怎么传?我之前也被这个问题困扰过,我猜测不少刚刚从 Vue3(SPA) 转向 Nuxt3(SSR) 开发的同学也会受困于这个问题,所以就把答案扩展一下,写成博文,分享给大家。
Vue3(SPA)里使用 Token
在 Vue3 开发的单页应用(Single Page Applicati,SPA)里使用 Token 比较简单。一般来说,我们会在项目初始化的时候读取 localStorage
,拿到标记用户身份的 token,然后放到内存当中,在请求时放到 header 里面发出。如果找不到 token,就认为用户没有登录,那么就通过路由将用户指引到登录页,或者不需要登录也能看的页面。
使用 Token 可以避免 CSRF 攻击,因为从外部网站无法同时满足访问 localStorage
,和将信息放入 http header 这两个条件。所以在单页应用里,使用 token 验证用户身份非常常见,有着各种开源、强壮、高效的实现。
这个过程相信做过 SPA 的同学都很熟悉,不多说了。
Nuxt3 请求的特点
从 Vue3(SPA) 到 Nuxt3(SSR),主要涉及一个思路的转换。
在 Vue(SPA)里,所有请求都是 后 请求,即完成网页加载、JS 执行完毕后,再发起请求。这些请求,有以下特点:
- 可以认为完全由开发者控制,即开发者确定请求发起的条件,然后在基本确定的时候发起请求。
- 这些请求大部分只请求数据,且有明显的特征,比如 HTTP Header 里包含
Authorization
。 - 这些请求里基本不包含静态资源。
在 Nuxt 里,因为 SSR 的存在,所以请求至少可以分成两类:
- 数据交互类,跟 SPA 里基本一致。
- 页面渲染类,SSR 核心所在。
后者最大的变化是:它会影响到 HTML 的内容,而通常来说,HTML 会被视作静态资源。我们知道,在网络环境里,存在大量缓存节点,假如跟用户相关的敏感数据渲染成 HTML,缓存到 CDN 当中,会是非常严重的安全问题。所以 Nuxt 在 SSR 时,不会携带 cookie;只有当页面渲染完成,由于用户操作而主动发起的请求里,才会携带 cookie。
怎么识别 SSR 请求
Nuxt2 时期,使用 Option API 的时候,有一个很明确的 asyncData()
函数,它会在 SSR 阶段被调用,发起请求获取数据。
Nuxt3 时期,Composition API 会稍嫌模糊一些。这个时候,整个 setup()
函数都会在 SSR 期间执行,而 useFetch
、useAsyncData
则负责在这个阶段发起请求,获取数据。因为这个阶段是在服务器的 node.js 环境里执行的,所以自然无法使用用户的 localStorage
,也无法使用用户本地存储的 token。
但是因为打开网页的请求是由用户发起的,所以其实用户有把自己的 cookie 发给服务器。如果我们希望 SSR 阶段用 cookie 鉴别用户身份,可以手动将 cookie 放在 useFetch
、useAsyncData
产生的请求当中,参考 官方文档 useRequestHeaders。
基本上,假如我们不了解 Nuxt3 的设计思路,简单看了眼文档就上手写代码,大概率所有页面数据相关的请求都会是 SSR 请求。至少我是如此。
使用 server: false
仅在网页里发起请求
当然,Nuxt3 提供了只在网页里发起请求的方法,也就是基本等效于我们之前在 SPA 里发起请求的方法,那就是在 useFetch
的 options
里标记 server: false
:
const { pending, data: posts } = useFetch('/api/comments', {
server: false
})
再演示个更扣题的方式
const { pending, data: posts } = useAsyncData(
'posts',
async () => {
const token = localStorage.getItem('token');
const data = await $fetch('/api/comments', {
headers: {
Authorization: `Bearer ${token}`,
},
});
return data;
},
{
server: false,
}
);
(代码高亮插件还是崩的,大家凑合看,等我回头修……)
详情请看 官方文档。
这样的请求就会在页面完成渲染后,才会发起请求。在这些请求里,如果需要使用 token,就按照以往的方式使用,即可。
server: false
与 lazy: true
的区别
如果大家仔细看 Nuxt3 官方文档,会发现还有一个参数:lazy: true
,以及两个语法糖函数:useLazyFetch
和 useLazyAsyncData
。
这两个函数也是先加载页面,再加载数据。它们与 server: false
的区别在于,后者是把全部请求都放在网页端进行;而 lazy: true
则只在网页跳转的时候起作用。
不使用 lazy: true
的情况下,当我们从 A 页面进入 B 页面时,如果 B 页面里需要加载数据,Nuxt3 就会等待数据加载完成之后,再进行页面渲染。如果你使用一个比较慢的网络,页面切换就会卡卡的,体验非常明显。
如果使用 lazy: true
,页面切换一下就完成了,但是数据可能还没加载到,此时我们需要手动处理加载状态。比如放个转转的菊花在页面中间。但是如果我们直接打开 B 页面,Nuxt3 还是会先把数据请求回来,完成页面渲染,然后呈现完整的 HTML 给我们,这样可以确保 SEO 效果。
而 server: false
,则会彻底放弃这部分数据的 SSR,对于用户身份相关的数据,就比较合适。
总结
其实就跟当年从前端学 Node.js 一样,新技术最大的挑战在于:使用场景不同、运行环境不同,思路需要很大变化,需要不同的理解,不能照搬以前的做法。
Nuxt3 涉及到网页渲染,涉及到网络链路上的缓存层,为了防止出现信息泄漏,它会主动放弃一些数据传输。同时,由于它的一部分运行环境在服务器上,所以有一些用户数据它没法使用。于是在开发 Nuxt3 应用的时候,我们必须搞清楚这些变化,然后作出适当的调整。
希望上面文章对大家有所帮助。如果文中有问题,请不吝指出。如果你对 Vue3、Nuxt3 开发有兴趣,有疑问,欢迎留言评论。
欢迎吐槽,共同进步