观众朋友们,晚上好!欢迎来到今晚的 Nuxt.js 中间件小课堂。我是你们的老朋友,代码界的段子手,今天咱们就来聊聊 Nuxt.js 里那些神秘又重要的“中间人”—— middleware。
咱先打个招呼,今天可不是来听我吹牛的,咱要用通俗易懂的语言,加上一些实战代码,把 Nuxt.js 的中间件彻底搞明白。准备好了吗?系好安全带,发车!
什么是 Middleware?为啥要有它?
想象一下,你是一家夜店的保安。不对,是高级餐厅的领位员。客人来了,你不能啥也不管直接让人进去吧?你得看看人家有没有穿拖鞋,有没有预定,有没有带宠物… 这就是 Middleware 的作用!
在 Nuxt.js 里,Middleware 就像是请求到达页面之前的一道道关卡。它可以拦截请求,进行一些处理,比如:
- 身份验证:检查用户是否已登录,没登录就踢回登录页。
- 权限控制:检查用户是否有权限访问特定页面,没权限就显示 "403 Forbidden"。
- 语言设置:根据用户的 Cookie 或浏览器设置,切换网站语言。
- A/B 测试:根据用户 ID,将用户分配到不同的测试组,展示不同的页面版本。
- 数据预取:在页面渲染之前,先从 API 获取一些数据。
总之,Middleware 让你可以在页面渲染之前,对请求进行各种各样的操作,保证用户看到的是最合适的内容。
Nuxt.js 中 Middleware 的种类
Nuxt.js 提供了三种类型的 Middleware:
- Route Middleware (路由中间件): 只在特定路由上运行。
- Named Middleware (命名中间件): 可在页面和布局中引用,具有可复用性。
- Global Middleware (全局中间件): 会在每个路由上运行。
咱们一个一个来扒。
1. Route Middleware (路由中间件)
这是最简单粗暴的一种。直接在 pages
目录下的 .vue
文件里定义。
// pages/secret.vue
<template>
<h1>欢迎来到秘密基地!</h1>
</template>
<script>
export default {
middleware: function ({ redirect }) {
// 模拟用户未登录
const isLoggedIn = false;
if (!isLoggedIn) {
return redirect('/login');
}
}
}
</script>
这段代码的意思是:访问 secret.vue
页面之前,先执行 middleware
函数。如果 isLoggedIn
是 false
,就跳转到 /login
页面。
是不是很简单?
2. Named Middleware (命名中间件)
这种中间件更灵活。你需要先在 middleware
目录下创建一个 .js
文件,然后在页面或布局中引用它。
// middleware/auth.js
export default function ({ redirect, store }) {
// 模拟用户未登录
const isLoggedIn = store.state.auth.loggedIn; // 假设登录状态存储在 Vuex 中
if (!isLoggedIn) {
return redirect('/login');
}
}
然后在 nuxt.config.js
文件中配置它,这样 Nuxt.js 才知道它的存在。
// nuxt.config.js
export default {
router: {
middleware: ['auth'] // 注册命名中间件
},
modules: [
'@nuxtjs/axios',
'@nuxtjs/auth-next' //如果用nuxt/auth-next 模块,可以不用自己写middleware
],
auth: {
strategies: {
local: {
token: {
property: 'access_token',
global: true,
required: true,
type: 'Bearer'
},
user: {
property: false,
autoFetch: true
},
endpoint: {
login: { url: '/api/auth/login', method: 'post' },
logout: { url: '/api/auth/logout', method: 'post' },
user: { url: '/api/auth/user', method: 'get' }
}
}
},
redirect: {
login: '/login',
logout: '/',
callback: '/login',
home: '/'
}
}
}
最后,在页面或布局中引用它:
// pages/profile.vue
<template>
<h1>欢迎来到个人中心!</h1>
</template>
<script>
export default {
middleware: 'auth' // 引用命名中间件
}
</script>
或者,在布局中使用:
// layouts/default.vue
<template>
<div>
<nuxt />
</div>
</template>
<script>
export default {
middleware: 'auth' // 引用命名中间件
}
</script>
这样,所有使用 default
布局的页面都会先执行 auth
中间件。
3. Global Middleware (全局中间件)
全局中间件会在每个路由上运行,所以要谨慎使用。全局中间件在 middleware
目录下创建,文件名前面加上 .
。
// middleware/.log.js
export default function ({ route }) {
console.log('访问了路由:', route.path);
}
Nuxt.js 会自动注册所有以 .
开头的中间件。
Middleware 的执行顺序
这可是个重点!Middleware 的执行顺序非常重要,它直接影响到你的代码逻辑。
nuxt.config.js
中的router.middleware
: 这是第一个执行的,按照数组顺序依次执行。- 布局组件中的
middleware
: 按照布局的嵌套顺序,由外到内执行。 - 页面组件中的
middleware
: 这是最后执行的。
咱们用一个表格来总结一下:
执行顺序 | 类型 | 位置 |
---|---|---|
1 | 全局中间件 | middleware 目录下,以 . 开头的文件 |
2 | nuxt.config.js |
router.middleware 数组 |
3 | 布局中间件 | 布局组件的 middleware 属性 |
4 | 页面中间件 | 页面组件的 middleware 属性 |
举个例子:
// nuxt.config.js
export default {
router: {
middleware: ['global-config']
}
}
// middleware/.global-config.js
export default function ({ store }) {
store.commit('setConfig', { theme: 'dark' });
}
// layouts/default.vue
<template>
<div>
<nuxt />
</div>
</template>
<script>
export default {
middleware: 'layout-auth'
}
</script>
// middleware/layout-auth.js
export default function ({ redirect, store }) {
if (!store.state.auth.loggedIn) {
return redirect('/login');
}
}
// pages/index.vue
<template>
<h1>首页</h1>
</template>
<script>
export default {
middleware: 'page-data'
}
</script>
// middleware/page-data.js
export default async function ({ store }) {
await store.dispatch('fetchData');
}
在这个例子中,中间件的执行顺序是:
.global-config.js
(全局中间件,设置主题)layout-auth.js
(布局中间件,检查登录状态)page-data.js
(页面中间件,获取数据)
Middleware 的作用域
Middleware 的作用域指的是它能访问哪些变量和函数。
context
对象: 这是最重要的对象,它包含了请求的所有信息,比如route
(路由信息),redirect
(重定向函数),store
(Vuex store),req
(Node.js request 对象),res
(Node.js response 对象) 等等。this
: 在 Route Middleware 中,this
指向的是 Vue 组件实例。但是在 Named Middleware 和 Global Middleware 中,this
是undefined
。所以,尽量避免在 Named Middleware 和 Global Middleware 中使用this
。
Middleware 的一些高级用法
-
使用
async/await
: Middleware 可以是异步的,你可以使用async/await
来处理异步操作。// middleware/fetch-data.js export default async function ({ store }) { await store.dispatch('fetchData'); }
-
返回
Promise
: 如果你的 Middleware 没有使用async/await
,也可以返回一个Promise
。// middleware/fetch-data.js export default function ({ store }) { return store.dispatch('fetchData'); }
-
使用
error
函数: 如果 Middleware 发生错误,可以使用error
函数来显示错误页面。// middleware/error-handler.js export default function ({ error }) { error({ statusCode: 500, message: '服务器错误' }); }
-
跳过后续中间件: 如果你想跳过后续的中间件,可以使用
return false
。但是不推荐这样做,因为它会使代码逻辑变得难以理解。最好还是通过条件判断来控制中间件的执行。
一些最佳实践
- 保持 Middleware 简洁: Middleware 应该只做一些简单的逻辑判断和数据处理,不要在里面写太多的业务代码。
- 避免在 Middleware 中修改
req
和res
对象: 除非你有充分的理由,否则不要在 Middleware 中修改req
和res
对象。 - 使用
async/await
处理异步操作: 这样可以使代码更清晰易懂。 - 充分利用
context
对象:context
对象包含了请求的所有信息,可以帮助你完成各种各样的任务。 - 命名清晰的 Middleware: 命名清晰的 Middleware 可以提高代码的可读性。
- 注释你的 Middleware: 注释可以帮助你和其他人理解 Middleware 的作用。
一个更复杂的例子
咱们来一个更复杂的例子,模拟一个用户登录状态验证,以及权限判断的场景。
// store/auth.js
export const state = () => ({
loggedIn: false,
user: null,
permissions: []
})
export const mutations = {
SET_USER (state, user) {
state.user = user
},
SET_LOGGED_IN (state, loggedIn) {
state.loggedIn = loggedIn
},
SET_PERMISSIONS (state, permissions) {
state.permissions = permissions
}
}
export const actions = {
async fetchUser ({ commit }) {
// 模拟从 API 获取用户信息
await new Promise(resolve => setTimeout(resolve, 500)) // 模拟网络延迟
const user = {
id: 1,
name: '张三',
email: '[email protected]'
}
commit('SET_USER', user)
commit('SET_LOGGED_IN', true)
commit('SET_PERMISSIONS', ['read', 'write']) // 模拟用户权限
},
async logout ({ commit }) {
// 模拟登出
await new Promise(resolve => setTimeout(resolve, 500)) // 模拟网络延迟
commit('SET_USER', null)
commit('SET_LOGGED_IN', false)
commit('SET_PERMISSIONS', [])
}
}
// middleware/auth.js
export default function ({ redirect, store }) {
if (!store.state.auth.loggedIn) {
return redirect('/login')
}
}
// middleware/permission.js
export default function ({ redirect, store, route }) {
const requiredPermission = route.meta.permission // 从路由元数据中获取需要的权限
if (requiredPermission && !store.state.auth.permissions.includes(requiredPermission)) {
return redirect('/forbidden')
}
}
// pages/admin/dashboard.vue
<template>
<h1>管理后台</h1>
</template>
<script>
export default {
middleware: ['auth', 'permission'],
meta: {
permission: 'admin' // 需要 admin 权限才能访问
}
}
</script>
在这个例子中:
store/auth.js
定义了用户的登录状态、用户信息和权限。middleware/auth.js
检查用户是否已登录,未登录就跳转到登录页。middleware/permission.js
检查用户是否拥有访问该页面所需的权限,没有权限就跳转到 "403 Forbidden" 页面。pages/admin/dashboard.vue
页面需要admin
权限才能访问。
总结
Nuxt.js 的 Middleware 是一个非常强大的工具,可以让你在页面渲染之前对请求进行各种各样的操作。掌握 Middleware 的执行顺序和作用域,可以让你更好地控制你的应用程序的行为。
好了,今天的 Nuxt.js 中间件小课堂就到这里。希望大家有所收获!记住,代码不是死的,人是活的,灵活运用 Middleware,让你的 Nuxt.js 应用更加强大! 下课!