Nuxt3 中的路由中间件及应用场景分析

164 阅读5分钟

在Nuxt中的路由中间件是什么呢? 他有什么作用呢?

在Nuxt中的路由中间件是指在进入页面之前所执行的一段代码。 它可以帮助我们在进入页面之前做一些逻辑验证。比如说登录验证,日志记录等功能。

在Nuxt 中有几种中间件呢?

在Nuxt 中有三种中间件。

  • 内联中间价,直接在某个页面定义的中间件,比较适用于某个页面需要在进入前需要做特殊处理的情况
  • 命名路由中间件, 需要在项目根目录下的middleware中定义,适合对某一类页面做特殊处理
  • 全局路由中间件, 需要在项目根目录下的middleware中定义,并且要以.global后缀进行命名,适合用在所有页面都需要在进入前需要处理逻辑的情况

Nuxt中间的使用

我们通过一个小案例来掌握路由中间的使用,假设现在有这样一个需求:

  • 有一个登录页面,一个首页,一个文章列表页面, 文章详情页面,会员页面,帮助中心页面
  • 登录页面和首页所有用户都可以查看,帮助中心只要登录了就能查看,文章列表页面和文章详情页面只有是创作者才可以查看,会员页面只有会员用用户才能查看。

分析这个需求我们可以得出以下方案:

  • 判断登录和未登录,我们可以使用全局路由中间件。
  • 判断是否能查看文章列表页面和文章详情页面我们可以使用命名路由。
  • 判断是否可以查看会员页面我们可以使用全局路由进行判断

根据以上需求分析,我们来开发这个需求:

1. 初始化项目

执行pnpm dlx nuxi@latest init nuxt-middleware

2. 创建目录结构

image.png

3. 头部导航组件

components/Header.vue

<template>
  <div style="width: 100%">
    <nav>
      <NuxtLink to="/" >首页</NuxtLink>
      <NuxtLink to="/login" >登录</NuxtLink>
      <NuxtLink to="/article/list" >文章页面</NuxtLink>
      <NuxtLink to="/member" >会员中心</NuxtLink>
      <NuxtLink to="/about" >帮助中心</NuxtLink>
    </nav>
  </div>
</template>

<script setup>
</script>

<style scoped>
nav{
  display: flex;
  height: 60px;
  box-shadow: 0px 5px 5px rgba(0,0,0, 0.2);
  align-items: center;
}

nav a{
  height: 100%;
  padding: 0 20px;
  text-decoration: none;
  line-height: 60px;
}
nav .router-link-active{
  background: #0f409e;
  color: #fff;
}
</style>

4. 模拟用户列表,和获取当前用户信息的方法

composables/getUser.ts

interface IUser {
  id: number | undefined
  role: number | undefined
}
// 模拟用户的列表
const userList: IUser[] = [
  {
    id: 1,
    role: 1 // 登录了只能查看帮助中心的用户,不能查看文章和会员页面
  },
  {
    id: 2,
    role: 2 // 登录了,只能查看文章和帮助中心的用户,不能查看会员页面
  },
  {
    id: 3,
    role: 0, // 会员用户, 所有页面都可以查看
  }
]
// 获取当前用户信息
export function getUser(): IUser {
  return userList[1]
}

5. 文章模拟数据

data/article.ts

const articleList = [
  {
    id: 1,
    title: '文章一的标题',
    content: '文章一的内容',
  },
  {
    id: 2,
    title: '文章二的标题',
    content: '文章二的内容'
  },
  {
    id: 3,
    title: '文章三的标题',
    content: '文章三的内容'
  }
]
export default articleList

6. 文章权限路由中间件(命名中间件)

middleware/article-auth.ts

const user = getUser()
export default defineNuxtRouteMiddleware((to, from) => {
  if (user.role === 1) { // 说明没有权限查看文章
    return navigateTo('/')
  }
})

可以看到在命名中间件中导出defineNuxtRouteMiddleware 方法,defineNuxtRouteMiddleware 方法是Nuxt 内置的,我们不需要做额外的导入, 在这个函数中判断没有查看文章权限的用户跳转到首页。通过navigateTo 方法进行跳转, to和from和vue-router 中的to,from 差不多, 这里就不做解释了。

7. 判断登录权限的全局路由中间件

middleware/auth.global.ts

const user = getUser()
export default defineNuxtRouteMiddleware((to, from) => {
  const allUserPage = ['/login', '/']
  if (!user.id && !allUserPage.includes(to.path)) {
    return navigateTo('/login')
  }
})

可以看到我们的文件命名带了.global 说明是全局中间件。全局中间也是通过defineNuxtRouteMiddleware 方法定义,和命中间件的不同的是文件命名不同, 所有的路由都会经过这个中间件。

8. 首页

pages/index.vue

<template>
  <div>
    <p>首页</p>
  </div>
</template>

<script setup>
</script>

<style scoped>

</style>

9. 登录页面

pages/login.vue

<template>
  <div>
    <p>登录页面</p>
  </div>
</template>

<script setup>
</script>

<style scoped>

</style>

11. 帮助中心页面

pages/about.vue

<template>
  <div>
    <p>帮助中心</p>
  </div>
</template>

<script setup>
</script>

<style scoped>

</style>

12. 文章列表页面

pages/article/list.vue

<template>
  <div>
    <ul class="article-list">
      <li v-for="item in articleList" :key="item.id">
        <NuxtLink :to="'/article/'+item.id">{{ item.title }}</NuxtLink>
      </li>
    </ul>
  </div>
</template>

<script setup>
import articleList from '~/data/article';

definePageMeta({
  middleware: [
    'article-auth'
  ]
})
</script>

<style scoped>
.article-list li {
  margin-top: 10px;
}
.article-list a {
  text-decoration: none;
}
</style>

在这里通过definePageMeta 方法中的middleware 来使用命名中间件。

13. 文章详情页面

pages/article/[[detail.vue]]

<template>
  <div>
    <h1>{{ articleDetail.title }}</h1>
    <p>{{  articleDetail.content }}</p>
  </div>
</template>

<script setup>
import articleList from '~/data/article';

definePageMeta({
  middleware:[
    'article-auth'
  ]
})

const route = useRoute()
const articleId = parseInt(route.params.detail)

const articleDetail = articleList.find(item => item.id === articleId)
</script>

<style scoped>

</style>

14. 会员页面

pages/member.vue

<template>
  <div>
    <p>会员页面</p>
  </div>
</template>

<script setup>
const user = getUser()
definePageMeta({
  middleware: [
    function () {
      console.log(user)
      if (user.role !== 0) {
        return abortNavigation()
      }
    }
  ],
})
</script>

<style scoped>

</style>

可以看到在会员页面中使用definePageMeta方法的 middleware中定义一个函数来实现内联中间件。到这里我们的这个小需求就开发完了。来查看下运行效果。

15. App.vue

<template>
  <div>
    <Header></Header>
    <NuxtPage class="page-wraper"></NuxtPage>
  </div>
</template>
<style>
*{
  margin: 0;
  padding: 0;
}

.page-wraper {
  padding: 30px;
}
</style>

16 .效果演示

16.1未登录情况

将composables/getUser.ts 中的getUser 方法修改下:

// 获取当前用户信息
export function getUser(): IUser {
  return {} as IUser
}

演示效果:

weidenglu.gif

16.2 只有查看帮助中心权限的用户登录

将composables/getUser.ts 中的getUser 方法修成:

// 获取当前用户信息
export function getUser(): IUser {
  return userList[0]
}

演示效果:

bangzhuzhongxin.gif

可以看到用户登录了只能查看帮助中心,看不了文章和会员页面,符合我们的预期。

16.3 只有查看帮助中心和文章的权限的用户登录

将composables/getUser.ts 中的getUser 方法修成:

// 获取当前用户信息
export function getUser(): IUser {
  return userList[1]
}

演示效果:

article-middleware.gif

可以看到除了会员中心不能查看之外其他页面都能查看,符合我们的预期。

16.4 可以查看会员中心的用户登录

将composables/getUser.ts 中的getUser 方法修成:

// 获取当前用户信息
export function getUser(): IUser {
  return userList[2]
}

演示效果:

huiyuan2.gif

可以看到会员用户所有的页面都能查看,符合我们的预期

总结

本篇介绍了Nuxt3中的三种中间件,内联中间件命名中间件全局中间件,通过一个小案例的分析和开发掌握了这三种中间件的应用场景,以及一些需要注意的问题。