深入理解 Nuxt.js 中 `middleware` (中间件) 的执行顺序和作用域,以及它们在服务器端和客户端导航中的区别。

各位观众,晚上好! 今天咱们来聊聊 Nuxt.js 里的“中间件 (Middleware)”这玩意儿。 别一听“中间件”就觉得高深莫测,其实它就像咱们生活中的保安,负责检查进出小区的每个人,确保安全和秩序。 在 Nuxt.js 里,中间件负责在页面渲染前后执行一些逻辑,比如权限验证、数据预取等等。 好了,废话不多说,咱们直接进入正题。

一、 什么是Nuxt.js中间件?

简单来说,Nuxt.js 中间件就是你在路由导航发生时,可以在特定时机执行的一段代码。 它们可以用于:

  • 权限验证: 检查用户是否登录,是否有权限访问某个页面。
  • 数据预取: 在页面渲染前,从 API 获取数据。
  • 日志记录: 记录用户的访问行为。
  • 重定向: 根据特定条件,将用户重定向到其他页面。
  • 修改上下文: 修改 context 对象,向组件传递额外的信息。

二、 中间件的种类和定义方式

Nuxt.js 提供了几种定义中间件的方式:

  1. 路由中间件 (Route Middleware): 只能用于特定的路由。
  2. 命名中间件 (Named Middleware): 可以在 nuxt.config.js 中定义,并在组件中或路由配置中使用。
  3. 全局中间件 (Global Middleware): 应用于整个应用的每个路由。

让我们用代码来演示一下:

1. 路由中间件 (Route Middleware):

这种中间件直接在 pages 目录下的页面组件中定义。

// pages/secret.vue
<template>
  <h1>Secret Page</h1>
</template>

<script>
export default {
  middleware: 'auth', // 使用名为 'auth' 的中间件
  async mounted() {
    console.log('秘密页面挂载了!');
  }
}
</script>

上面这段代码中,middleware: 'auth' 表示当前页面使用了名为 auth 的中间件。 但是,这个 auth 中间件是从哪里来的呢? 别急,咱们稍后会讲到。

2. 命名中间件 (Named Middleware):

这种中间件需要在 middleware 目录中定义,文件名就是中间件的名字。

// middleware/auth.js
export default function ({ store, redirect }) {
  // 如果用户未登录
  if (!store.state.auth.loggedIn) {
    return redirect('/login')
  }
}

在这个例子中,我们定义了一个名为 auth 的中间件。它检查用户是否登录,如果未登录,则重定向到 /login 页面。

现在,回到 pages/secret.vue 页面,它就可以使用这个 auth 中间件了。

3. 全局中间件 (Global Middleware):

全局中间件需要在 nuxt.config.js 中配置。

// nuxt.config.js
export default {
  middleware: ['global-middleware'], // 注册全局中间件
  plugins: [],
}

然后,在 middleware 目录下创建一个 global-middleware.js 文件:

// middleware/global-middleware.js
export default function ({ route }) {
  console.log('全局中间件:当前路由是', route.path)
}

这个全局中间件会在每次路由改变时执行,并打印当前路由的路径。 需要注意的是,全局中间件的执行顺序取决于它们在 nuxt.config.js 中的注册顺序。

三、 中间件的执行顺序

中间件的执行顺序非常重要,它决定了你的应用程序的行为。 Nuxt.js 中间件的执行顺序如下:

  1. nuxt.config.js 中配置的 全局中间件,按照它们在数组中的顺序执行。
  2. 匹配路由的 布局中间件(如果存在)。
  3. 匹配路由的 页面中间件

可以用一个表格来总结一下:

类型 位置 执行时机 顺序
全局中间件 nuxt.config.js 每个路由 按照在 nuxt.config.js 中的声明顺序
布局中间件 布局组件 (layouts/default.vue) 匹配布局的每个路由 在页面中间件之前
页面中间件 页面组件 (pages/index.vue) 匹配页面的每个路由 在布局中间件之后

举个栗子:

假设我们有以下配置:

  • nuxt.config.js:

    export default {
      middleware: ['global1', 'global2'],
    }
  • middleware/global1.js:

    export default function ({ route }) {
      console.log('全局中间件 1', route.path);
    }
  • middleware/global2.js:

    export default function ({ route }) {
      console.log('全局中间件 2', route.path);
    }
  • layouts/default.vue:

    <template>
      <div>
        <nuxt/>
      </div>
    </template>
    
    <script>
    export default {
      middleware: 'layoutMiddleware',
    }
    </script>
  • middleware/layoutMiddleware.js:

    export default function ({ route }) {
      console.log('布局中间件', route.path);
    }
  • pages/index.vue:

    <template>
      <h1>Index Page</h1>
    </template>
    
    <script>
    export default {
      middleware: 'pageMiddleware',
    }
    </script>
  • middleware/pageMiddleware.js:

    export default function ({ route }) {
      console.log('页面中间件', route.path);
    }

当用户访问 / 页面时,控制台的输出顺序将是:

  1. 全局中间件 1 /
  2. 全局中间件 2 /
  3. 布局中间件 /
  4. 页面中间件 /

四、 中间件的作用域

中间件的作用域指的是中间件可以访问哪些变量和函数。 Nuxt.js 中间件可以访问一个 context 对象,它包含了以下属性:

  • store: Vuex store 实例。
  • route: 当前路由对象。
  • params: 路由参数。
  • query: 查询参数。
  • req: Node.js 请求对象 (仅在服务器端可用)。
  • res: Node.js 响应对象 (仅在服务器端可用)。
  • redirect: 用于重定向的函数。
  • error: 用于显示错误页面的函数。
  • app: Vue 应用程序实例。
  • $axios: Axios 实例 (如果使用了 @nuxtjs/axios 模块)。
  • env: 环境变量 (nuxt.config.js 中定义的 env 属性)。
  • isDev: 指示是否处于开发模式。
  • isHMR: 指示是否启用了热模块替换 (HMR)。
  • payload: 用于在服务器端和客户端之间传递数据的对象。

通过 context 对象,中间件可以访问应用程序的各个方面,并执行各种操作。

五、 服务器端和客户端导航的区别

Nuxt.js 是一个通用框架,这意味着它可以在服务器端和客户端运行。 中间件的执行行为在服务器端和客户端略有不同:

  • 服务器端: 中间件在服务器端执行,用于预取数据、验证权限等。 在服务器端执行的中间件可以访问 reqres 对象,这意味着它们可以读取请求头、设置响应头等。 服务器端执行的中间件对于 SEO 和首屏加载速度至关重要。

  • 客户端: 中间件在客户端执行,用于处理路由切换、更新 UI 等。 在客户端执行的中间件可以访问 window 对象,这意味着它们可以操作 DOM、使用浏览器 API 等。

重要提示:

  • 服务器端中间件 只能在服务器端运行,不能访问 windowdocument 等浏览器对象。
  • 客户端中间件 可以在客户端运行,但不能直接访问 reqres 对象。

为了更好地理解服务器端和客户端导航的区别,咱们来看一个例子:

// middleware/server-client.js
export default function ({ route, req, res }) {
  if (process.server) {
    console.log('服务器端:', route.path, req.headers['user-agent']);
  }
  if (process.client) {
    console.log('客户端:', route.path, window.innerWidth);
  }
}

这个中间件在服务器端打印路由路径和 User-Agent,在客户端打印路由路径和窗口宽度。

六、 实际应用场景

让我们看一些实际应用场景,来巩固我们对中间件的理解。

1. 权限验证:

// middleware/auth.js
export default function ({ store, redirect, route }) {
  const isAuthenticated = store.state.auth.loggedIn;
  const requiresAuth = route.meta.requiresAuth !== false; // 默认需要登录

  if (requiresAuth && !isAuthenticated) {
    return redirect('/login?redirect=' + route.path);
  }
}

// pages/profile.vue
<template>
  <h1>Profile Page</h1>
</template>

<script>
export default {
  middleware: 'auth',
  meta: { requiresAuth: true } // 明确声明需要登录
}
</script>

在这个例子中,auth 中间件检查用户是否登录。 如果用户未登录,并且页面需要登录才能访问 (通过 route.meta.requiresAuth 属性判断),则重定向到 /login 页面,并将当前页面作为 redirect 参数传递。

2. 数据预取:

// middleware/fetch-data.js
export default async function ({ store, params }) {
  if (!store.state.post) {
    await store.dispatch('fetchPost', params.id);
  }
}

// pages/posts/_id.vue
<template>
  <h1>{{ post.title }}</h1>
  <p>{{ post.content }}</p>
</template>

<script>
export default {
  middleware: 'fetch-data',
  computed: {
    post() {
      return this.$store.state.post;
    }
  }
}
</script>

// store/index.js
export const actions = {
  async fetchPost({ commit }, id) {
    const response = await this.$axios.$get(`/api/posts/${id}`);
    commit('setPost', response);
  }
};

export const mutations = {
  setPost(state, post) {
    state.post = post;
  }
};

export const state = () => ({
  post: null
});

在这个例子中,fetch-data 中间件在页面渲染前,从 API 获取文章数据,并将其存储到 Vuex store 中。 这样可以避免页面加载后再次发起请求,提高性能。

3. 重定向:

// middleware/redirect-mobile.js
export default function ({ redirect, req }) {
  if (process.server) {
    const userAgent = req.headers['user-agent'];
    const isMobile = /Mobile|iP(hone|ad|od)|Android/i.test(userAgent);

    if (isMobile) {
      return redirect('/mobile');
    }
  }
}

// nuxt.config.js
export default {
  middleware: ['redirect-mobile'],
}

这个全局中间件检测用户是否使用移动设备访问,如果是,则重定向到 /mobile 页面。 注意,这个中间件只在服务器端执行,因为我们需要访问 req 对象来获取 User-Agent。

七、 总结

Nuxt.js 中间件是一个强大的工具,可以让你在路由导航发生时执行各种操作。 掌握中间件的种类、执行顺序和作用域,可以帮助你构建更灵活、更高效的 Nuxt.js 应用程序。

希望今天的讲座对大家有所帮助! 如果大家还有什么问题,欢迎随时提问。

最后,别忘了多写代码,多实践,才能真正掌握 Nuxt.js 中间件的精髓! 祝大家编程愉快!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注