Nuxt.js中间件使用

学习目标

通过本章节的学习,你将能够:

  • 了解Nuxt.js中间件的基本概念
  • 掌握中间件的创建和注册方法
  • 了解中间件的执行顺序
  • 学习全局中间件和页面中间件的区别
  • 掌握路由守卫的实现方法
  • 了解中间件的应用场景

核心知识点讲解

中间件的基本概念

在Nuxt.js中,中间件是在页面渲染之前执行的函数。通过中间件,你可以:

  • 验证用户身份
  • 权限控制
  • 数据预处理
  • 路由重定向
  • 日志记录

中间件的创建和注册

创建中间件文件

中间件文件存放在middleware目录中:

├── middleware/
│   ├── auth.js         # 认证中间件
│   ├── logger.js       # 日志中间件
│   └── redirect.js     # 重定向中间件

基本中间件结构

// middleware/auth.js

export default function ({ store, route, redirect, error }) {
  // 检查用户是否已登录
  const isAuthenticated = store.state.user.loggedIn
  
  // 如果用户未登录且访问的是需要认证的页面,则重定向到登录页
  if (!isAuthenticated && route.path.startsWith('/admin')) {
    return redirect('/login')
  }
  
  // 如果用户已登录但访问的是登录页,则重定向到首页
  if (isAuthenticated && route.path === '/login') {
    return redirect('/')
  }
}

注册中间件

全局中间件

nuxt.config.js中注册全局中间件:

// nuxt.config.js
module.exports = {
  router: {
    middleware: 'auth' // 全局应用auth中间件
  }
}
页面中间件

在页面组件中注册中间件:

<!-- pages/admin/index.vue -->
<template>
  <div>
    <h1>管理后台</h1>
  </div>
</template>

<script>
export default {
  middleware: 'auth' // 仅在当前页面应用auth中间件
}
</script>
布局中间件

在布局组件中注册中间件:

<!-- layouts/admin.vue -->
<template>
  <div class="admin-layout">
    <!-- 布局内容 -->
  </div>
</template>

<script>
export default {
  middleware: 'auth' // 在使用此布局的所有页面应用auth中间件
}
</script>

中间件的执行顺序

Nuxt.js中间件的执行顺序如下:

  1. nuxt.config.js 中定义的全局中间件
  2. 布局组件 中定义的中间件
  3. 页面组件 中定义的中间件

如果多个中间件需要按特定顺序执行,可以在页面组件中使用数组形式指定:

<!-- pages/admin/users.vue -->
<template>
  <div>
    <h1>用户管理</h1>
  </div>
</template>

<script>
export default {
  middleware: ['auth', 'admin'] // 先执行auth中间件,再执行admin中间件
}
</script>

全局中间件vs页面中间件

全局中间件

  • nuxt.config.js中注册
  • 应用于所有页面
  • 适用于需要全局处理的逻辑(如日志记录、基本认证等)

页面中间件

  • 在页面或布局组件中注册
  • 仅应用于特定页面或使用特定布局的页面
  • 适用于需要特定页面处理的逻辑(如页面权限控制、数据预处理等)

路由守卫实现

Nuxt.js中间件可以实现类似Vue Router的路由守卫功能:

全局前置守卫

// middleware/global.js

export default function ({ route, from, store }) {
  console.log('全局中间件执行')
  console.log('当前路由:', route.path)
  console.log('来源路由:', from ? from.path : '无')
  
  // 可以在这里执行全局路由逻辑
}

路由独享守卫

// middleware/admin.js

export default function ({ store, redirect }) {
  // 检查用户是否为管理员
  const isAdmin = store.state.user.role === 'admin'
  
  if (!isAdmin) {
    return redirect('/403')
  }
}

组件内守卫

在Nuxt.js中,可以在页面组件的middleware属性中直接定义函数:

<!-- pages/profile.vue -->
<template>
  <div>
    <h1>用户资料</h1>
  </div>
</template>

<script>
export default {
  middleware({ store, route, redirect }) {
    // 组件内守卫逻辑
    const isAuthenticated = store.state.user.loggedIn
    
    if (!isAuthenticated) {
      return redirect('/login')
    }
  }
}
</script>

中间件的高级用法

异步中间件

// middleware/fetch-data.js

export default async function ({ store, route }) {
  // 异步获取数据
  await store.dispatch('fetchData', route.params.id)
  
  // 数据获取完成后继续渲染页面
}

动态中间件

<!-- pages/[id].vue -->
<template>
  <div>
    <h1>动态页面</h1>
  </div>
</template>

<script>
export default {
  middleware({ route }) {
    // 根据路由参数动态选择中间件
    if (route.params.id === 'admin') {
      return 'admin'
    } else {
      return 'auth'
    }
  }
}
</script>

中间件应用场景

  1. 认证和授权

    • 检查用户是否已登录
    • 验证用户权限
    • 保护敏感页面
  2. 数据预处理

    • 在页面渲染前获取必要的数据
    • 验证路由参数
    • 准备页面所需的状态
  3. 路由控制

    • 路由重定向
    • 路由拦截
    • 404处理
  4. 性能监控

    • 记录页面加载时间
    • 监控API调用
    • 错误跟踪
  5. 国际化

    • 根据用户语言设置重定向
    • 加载对应语言的资源

实用案例分析

案例一:认证中间件

场景:在项目中实现用户认证,保护需要登录才能访问的页面。

解决方案

// middleware/auth.js

export default function ({ store, route, redirect, app }) {
  // 白名单路由,不需要认证
  const whiteList = ['/login', '/register', '/404', '/500']
  
  // 检查当前路由是否在白名单中
  const isInWhiteList = whiteList.includes(route.path)
  
  // 检查用户是否已登录
  const isAuthenticated = store.state.user.loggedIn
  
  // 如果用户未登录且不在白名单中,则重定向到登录页
  if (!isAuthenticated && !isInWhiteList) {
    // 保存当前路由,登录后可重定向回该页面
    store.commit('user/setRedirectPath', route.fullPath)
    return redirect('/login')
  }
  
  // 如果用户已登录但在登录页,则重定向到首页或之前保存的页面
  if (isAuthenticated && route.path === '/login') {
    const redirectPath = store.state.user.redirectPath || '/'
    store.commit('user/setRedirectPath', '')
    return redirect(redirectPath)
  }
}

使用方法

// nuxt.config.js
module.exports = {
  router: {
    middleware: 'auth' // 全局应用auth中间件
  }
}
<!-- pages/login.vue -->
<template>
  <div class="login-page">
    <h1>登录</h1>
    <form @submit.prevent="handleLogin">
      <div>
        <label>用户名</label>
        <input v-model="form.username" type="text" required>
      </div>
      <div>
        <label>密码</label>
        <input v-model="form.password" type="password" required>
      </div>
      <button type="submit">登录</button>
    </form>
  </div>
</template>

<script>
export default {
  data() {
    return {
      form: {
        username: '',
        password: ''
      }
    }
  },
  methods: {
    async handleLogin() {
      try {
        // 登录逻辑
        await this.$store.dispatch('user/login', this.form)
        // 登录成功后,中间件会自动重定向
      } catch (error) {
        console.error('登录失败:', error)
      }
    }
  }
}
</script>

案例二:权限控制中间件

场景:在管理后台中实现基于角色的权限控制,确保不同角色只能访问对应的页面。

解决方案

// middleware/permission.js

// 定义路由权限映射
const permissionMap = {
  '/admin/dashboard': ['admin', 'editor'],
  '/admin/users': ['admin'],
  '/admin/products': ['admin', 'editor'],
  '/admin/settings': ['admin']
}

export default function ({ store, route, redirect }) {
  // 检查用户是否已登录
  const isAuthenticated = store.state.user.loggedIn
  if (!isAuthenticated) {
    return redirect('/login')
  }
  
  // 获取用户角色
  const userRole = store.state.user.role
  
  // 检查当前路由是否需要权限
  const requiredRoles = permissionMap[route.path]
  
  // 如果路由需要权限且用户角色不在允许列表中,则重定向到403页面
  if (requiredRoles && !requiredRoles.includes(userRole)) {
    return redirect('/403')
  }
}

使用方法

<!-- pages/admin/users.vue -->
<template>
  <div>
    <h1>用户管理</h1>
  </div>
</template>

<script>
export default {
  middleware: ['auth', 'permission'] // 先执行认证中间件,再执行权限中间件
}
</script>

案例三:日志中间件

场景:在项目中实现访问日志记录,记录用户的访问行为。

解决方案

// middleware/logger.js

export default function ({ route, from, req, res }) {
  // 获取客户端IP
  const clientIP = req ? req.connection.remoteAddress : 'localhost'
  
  // 获取用户代理
  const userAgent = req ? req.headers['user-agent'] : navigator.userAgent
  
  // 构建日志信息
  const logInfo = {
    timestamp: new Date().toISOString(),
    path: route.path,
    from: from ? from.path : 'null',
    ip: clientIP,
    userAgent: userAgent
  }
  
  // 打印日志
  console.log('访问日志:', logInfo)
  
  // 可以将日志保存到数据库或日志文件
  // saveLog(logInfo)
}

使用方法

// nuxt.config.js
module.exports = {
  router: {
    middleware: 'logger' // 全局应用logger中间件
  }
}

案例四:国际化中间件

场景:在项目中实现国际化,根据用户的语言设置或路由参数切换语言。

解决方案

// middleware/i18n.js

export default function ({ store, route, redirect, app }) {
  // 从路由参数获取语言
  const langFromRoute = route.params.lang
  
  // 支持的语言列表
  const supportedLanguages = ['zh', 'en']
  
  // 如果路由参数中有语言且在支持列表中,则设置语言
  if (langFromRoute && supportedLanguages.includes(langFromRoute)) {
    store.commit('i18n/setLocale', langFromRoute)
    app.i18n.locale = langFromRoute
  } 
  // 如果路由参数中有语言但不在支持列表中,则重定向到默认语言
  else if (langFromRoute && !supportedLanguages.includes(langFromRoute)) {
    return redirect('/zh' + route.path.replace(`/${langFromRoute}`, ''))
  }
  // 如果路由参数中没有语言,则使用默认语言
  else {
    const defaultLang = 'zh'
    if (!route.path.startsWith(`/${defaultLang}`)) {
      return redirect(`/${defaultLang}${route.path}`)
    }
  }
}

使用方法

// nuxt.config.js
module.exports = {
  router: {
    middleware: 'i18n' // 全局应用i18n中间件
  }
}
<!-- pages/[lang]/index.vue -->
<template>
  <div>
    <h1>{{ $t('home.title') }}</h1>
    <p>{{ $t('home.description') }}</p>
    <div class="language-switcher">
      <nuxt-link to="/zh">中文</nuxt-link>
      <nuxt-link to="/en">English</nuxt-link>
    </div>
  </div>
</template>

<script>
export default {
  // 中间件已在全局注册
}
</script>

总结

本章节详细介绍了Nuxt.js的中间件使用,包括:

  1. 中间件的基本概念:中间件的作用和优势
  2. 中间件的创建和注册:如何创建中间件文件以及在不同位置注册中间件
  3. 中间件的执行顺序:全局中间件、布局中间件和页面中间件的执行顺序
  4. 全局中间件vs页面中间件:两种中间件的区别和使用场景
  5. 路由守卫实现:如何使用中间件实现路由守卫功能
  6. 中间件的高级用法:异步中间件和动态中间件
  7. 中间件应用场景:认证和授权、数据预处理、路由控制、性能监控和国际化

通过合理使用中间件,可以实现复杂的业务逻辑,提高应用的安全性和可维护性。在实际项目中,应根据具体需求选择合适的中间件类型,并遵循最佳实践,确保中间件的可靠性和性能。

练习

  1. 创建一个Nuxt.js项目,添加认证中间件
  2. 实现基于角色的权限控制中间件
  3. 创建日志中间件,记录用户访问行为
  4. 实现国际化中间件,支持多语言切换
  5. 测试不同中间件的执行顺序

拓展阅读

« 上一篇 Nuxt.js插件机制 下一篇 » Nuxt.js基础项目实战