返回

Nuxt3: SWR/静态路由失效处理

vue.js

基于用户操作的SWR/静态路由失效处理

在Nuxt 3应用中,混合渲染结合了服务器端渲染(SSR)的优势与静态站点生成(SSG)的效率。SWR 和静态路由用于缓存,可以提升性能与SEO, 但需要解决动态内容更新问题。本文将探讨如何在用户更新操作后使 SWR 或静态缓存失效,以确保内容与时俱进。

问题分析

假定 /edit-profile 为用户编辑个人资料页面。用户的公开资料页面如 /profile/sam-james ,需要尽可能提高SEO和性能,使用静态生成或SWR缓存。现在,如果“Sam James”编辑了他的个人资料,我们需立即失效对应的 /profile/sam-james 页面的缓存,使得下一次访问时获取最新内容。

核心在于用户更新资料后,如何触发服务器端缓存失效机制,即“按需失效”。

方案一:基于服务端事件的失效

一种解决办法是当用户修改资料时,触发服务器事件通知。服务器收到事件后,会主动清除相关页面的SWR或静态缓存。

实现步骤:

  1. /edit-profile 页面,成功更新用户信息后,发出服务器事件。 可以使用useFetch或者一个服务器API route发送请求到你的服务端。

    // components/ProfileEdit.vue
    <script setup>
    async function handleProfileUpdate(){
         //  提交表单或其他逻辑来更新个人资料
          await $fetch('/api/invalidate-cache',{ method:"POST",body:{username:"sam-james"}})
          // 通知用户
    }
    
    </script>
    
  2. 创建一个 API route 处理请求,接收要失效的 URL,并清除对应缓存。使用useRuntimeConfig 获取Nuxt运行环境配置。可以通过访问 this.nuxt的上下文访问服务器实例方法。 使用 this.nuxt.clearRouteCache(path) API来清除Nuxt的静态和swr路由缓存。

 // server/api/invalidate-cache.ts
export default defineEventHandler(async (event)=>{
     const { username } = await readBody(event);
    const path = `/profile/${username}`

     // 在这里可以做更精确的缓存失效判断和验证操作
     // 例如 检查当前用户是否拥有修改这个缓存的权限等。

     event.context.nuxt.clearRouteCache(path);

    return  {success:true,path,message:"缓存清除成功!"}

})
 ```
*   **注意:**  可以将 `/profile/${username}`  动态构造,从请求体中读取参数,实现更加通用的失效机制。
 * **安全提示:**  严格校验请求的来源,以及参数,避免非法清除缓存。例如,可以通过验证用户的 session 或者 API 密钥来控制访问。

**解释:**  当用户修改资料时,`/edit-profile`页面会调用 `/api/invalidate-cache` 发出请求,服务器处理请求,清理对应的缓存。当下次用户访问 `/profile/sam-james` 时,会重新从服务器获取最新内容。

## 方案二: 基于中间件的校验

在特定情况下,可以通过中间件的方式来实现更加精确地控制路由缓存,当用户访问相关路由的时候,中间件校验一些条件决定路由是否可以继续访问。 此方式可能更为轻量,不用直接清空整个缓存。

**实现步骤:** 

1. 创建一个名为 `checkProfile` 的中间件,根据用户和路径检查页面是否应该缓存或使用 SWR。`useRoute` 获取当前路由。 `useCookie` 帮助读取或更新cookie值。 使用 `navigateTo` 可以实现重定向,

 ```typescript
  // middleware/checkProfile.ts
 export default defineNuxtRouteMiddleware(async (to) => {
   const userCookie= useCookie('profileVersion',{ path: "/" , httpOnly: false }); // 这里可以是用cookie或者用户相关存储判断。

  const route = useRoute()

   if(!to.fullPath.startsWith("/profile/")){
      return ;
   }


  const userName  = route.params.user
  // const profileVersion = fetch profile Version of specific user. You need fetch it before route rendering to compare with stored Version of the page, If different redirect to static page rendering or invalid cache

  const serverRes =  await $fetch(`/api/check-version`, {params:{userName: userName} })


   //检查缓存中的版本号 与最新的版本号对比
 if (serverRes.version!=userCookie.value) {
       userCookie.value=serverRes.version
    return  navigateTo({path:to.fullPath})
     // or  if want not display static content of older Version:return abortNavigation();
  }
     //  allow current to access /profile/
     //return;

})
 ```
2.  创建一个API路由获取指定的用户profile version.这个api 不可以设置缓存策略. 在这个API Route中获取数据,例如访问DB中的数据,并返回用户的profile version 。 如果没有版本号 可以使用一个随机数标识。 并在用户修改个人资料后,更新这个用户的版本号信息。

     ```typescript
 // server/api/check-version.ts
 export default defineEventHandler(async (event) => {

 const  userName = getQuery(event)?.userName;

     if (!userName) {
         throw createError({ statusCode: 400, statusMessage: "需要参数: userName" })
     }


 // fetch version for user or do update
const profileVersion  = Math.random()// can use timestamp of profile updated date of fetch it from db
     // fetch and get updated date. timestamp of user table when changed data
       return {
           version : profileVersion ,
            userName
       };
 })
     ```

3. 在`nuxt.config.ts`中将路由规则配置应用中间件。
 ```typescript
    //nuxt.config.ts
     export default defineNuxtConfig({
        routeRules: {
             "/edit-profile": { ssr: false },
             "/profile/**": { swr: true },

         },
         router:{
            middleware:["checkProfile"]
         }
     });

 ```

**解释:**  每次访问 `/profile/` 开头的页面时,中间件会先检查 Cookie 中的用户 `profileVersion `是否和服务器返回的用户 `profileVersion `一致。如果不一致,重定向该路由到相同路由,服务器端渲染此路由,刷新 `profileVersion` cookie。如果`profileVersion`一致,则直接允许访问,则路由直接读取缓存数据。

此方式不用直接清楚服务器的静态缓存。控制粒度更为精确。需要额外的 Cookie 来记录当前访问页面时使用的版本信息。此cookie 为Http only为true,只能用作检查,并避免暴露。
**安全提示:**  中间件确保逻辑的正确性,以及服务器API Route 需要防止未授权请求。

## 总结

在Nuxt 3中使用混合渲染处理 SWR/静态路由失效需要精细的控制,可以通过服务器事件和中间件的方式。 基于服务器事件,可以方便的处理缓存失效,而基于中间件的方式,提供了更细粒度的校验与处理。两种方案可以根据实际需求进行选择。选择何种方案应该根据具体应用需求进行衡量,并进行必要的安全防护,例如身份校验和防范非授权访问。