上次我们讲到了为什么使用nuxt
,以及nuxt
的路由如何配置,nuxt
相关的几个路由组件和路由相关hook
的使用。
今天我们继续对路由部分做一下补充,在加入上次提到的其它文件夹中的部分内容的配置以及使用。
路由使用
接下来我将会对nuxt路由的知识点做补充,包括编程式路由,路由中间件,以及路由守卫的介绍。同时在这里将会引出nuxt的middleware
相关知识,以及对前文所说的服务端渲染和客户端渲染内容做一下回忆。
编程导航式路由
上次我们在讲路由的基本配置的时候提到了使用<nuxt-link></nuxt-link>
组件进行路由传参,以及如何传入 query
参数或者params
参数,同时我们了解如何使用useRoute()
这个hook
来获取传递过来的参数。
那么大家很容易想到可以用useRouter()
这个hook
获取路由器实例来和vue-router
一样实现编程式路由。
<div @click="handleRouterPush" class="info">test</div>
<script lang="ts" setup>
const router = useRouter();
const handleRouterPush = () => {
router.push("/details/1");
};
</script>
这里关于如何使用路由器实例router 去实现传参等内容就不一一赘述了,都是vue-router的相关知识。
我们发现在Nuxt里面直接使用router.push确实可以实现路由的切换,传参等操作和普通的vue项目基本没有区别,其实如果拿Nuxt做客户端渲染的话使用router 实例的方式完成项目开发。
但是Nuxt实际上给我们提供了一个编程式路由的API navigateTo
我们先来看一下该API的用法。
<div class="cursor-pointer" @click="handleMoreRoute">更多内容无参</div>
<div class="cursor-pointer" @click="handleMoreRoutePath">更多内容路径参</div>
<div class="cursor-pointer" @click="handleMoreRoutePathQuery">更多内容路径参query</div>
<div class="cursor-pointer" @click="handleMoreRouteName('content')">更多内容name content</div>
<script lang="ts" setup>
const handleMoreRoute = () => {
navigateTo("/more");
};
const handleMoreRoutePath = () => {
navigateTo("/more/1");
};
const handleMoreRoutePathQuery = () => {
navigateTo("/more/?id=1&age=18");
};
const handleMoreRouteName = (name:string) => {
navigateTo({ name ,query:{id:1,age:18}});
};
</script>
我们发现它和router.push
的使用完全一致,那么为什么nuxt
要多此一举提供这样一个API
,问题就回到我们前文说的服务端渲染和客户端渲染上来了,router
示例只能在客户端渲染使用,而navigateTo
确实可以在服务端和客户端都使用的。而在我们的开发过程中,什么时候在做服务端渲染?
在 setup
中加入打印语句,会发现控制台有两次输出,第一次输出前显示‘ssr’,表明此时处于服务端渲染阶段。。
如果我们去查看编辑器的终端
同样可以看见内容的输出,这是服务端渲染时打印的内容。
在Nuxt
里面我们如何判断区别是服务端渲染阶段还是客户端渲染阶段呢?
主要使用的方法有两个,一个是直接 通过import.meta
if (import.meta.server) {
console.log("Server side");
} else {
console.log("client side");
}
if (import.meta.client) {
console.log("client side");
} else {
console.log("Server side");
}
另一种是nuxt配置文件里面 Nuxt 配置中有一个运行时配置 runtimeConfig
可用于对外暴露值
//nuxt.config,ts
export default defineNuxtConfig({
devtools: { enabled: true },
runtimeConfig: {
secretKey: 11111111,
public: {
apiBase: process.env.NUXT_PUBLIC_API_BASE
}
},
})
//index.vue
const runtimeConfig = useRuntimeConfig();//使用 useRuntimeConfig 获取配置里面的值
console.log(runtimeConfig.secretKey, "secretKey");
console.log(runtimeConfig.public.apiBase, "apiBase");
配置完成之后我们打印一下配置信息
我们可以看到在ssr阶段我们可以拿到两个值,而在客户端渲染的阶段我们只能获取到public的值,所以我们可以直接根据能否拿到public以外的值来判断当前是什么端。
回到我们刚刚使用的navigateTo
API ,如果按照下方代码,我们会发现router.push并不会执行,因为在服务端渲染阶段router是不存在,但是将其换成navigateTo
就可以正常实现页面路由的更新,同样在客户端渲染阶段,将下方的navigateTo注释解除,也可以实现跳转。
if (import.meta.server) {
console.log("Server side");
router.push("/details/1");
} else {
// navigateTo("/more");
console.log("client side");
}
更多情况下使用navigateTo
Api
是用户打开外部链接的
// 默认禁止打开外部,需设置'external'参数为'true',不然会报错
// await navigateTo('https://nuxt.com', {
// external: true
// })
那么关于navigateTo
就提及到这里,关于更多属性与方法比如open
就由大家自己根据需要查阅官方文档使用。
路由常用的部分还剩下最后一个知识点也就是我们在Vue
里面常用的路由守卫,在vue
里面我们可以通过beforeEach
添加前置路由守卫,或者其它的方法添加全局解析守卫或者后置守卫。但是在Nuxt
里面实现前置守卫我们需要引入一个新的知识也就是中间件。
middleware和路由中间件
Nuxt
提供了中间件来在导航到特定路线之前运行代码,我们可以通过定义中间件来实现对特定或者全局页面之前需要运行的代码。
那么接下来我会告诉大家在 nuxt
里面如何定义一个中间件以及如何使用中间件。
首先我们可以在页面直接定义中间件并使用,我们可以在下方代码的funtion
里面写任意你想在进入该路由之前执行的代码。
//index.vue
definePageMeta({
middleware: [
function (to) {
console.log("middleware", to.path);
}
]
})
或者我们可以在nuxt
的约定文件夹middleware
下面定义
然后我们可以选择在需要使用的页面里面设置该中间件
//content.vue
definePageMeta({
middleware: ['test']
});
如果希望设置全局的中间件而不是一个个引入,我们只需要在给中间件取名称的时候加上global
就是全局中间件
middleware/
--- once.global.ts
这样中间件就会在全部路由都生效了。
大致了解了如何设置中间件以及在对于页面设置中间件接下来就来看下如何如何在中间件中实现路由守卫吧
通过middleware实现路由守卫
通常我们在项目中设置路由守卫的目的都是为了区分用户,比如登录用户和未登录用户,登录用户里面又有普通用户、vip用户、管理员用户等等。
下面我带大家实现一个简单的中间件守卫
// once.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
// 假设存储 token 的地方
const token = useCookie('token');
const userRole = useCookie('userRole');
// 白名单路由,不需要 token 即可访问
const whiteList = ['/login', '/register'];
if (whiteList.includes(to.path)) {
// 如果是白名单路由,直接放行
return;
}
if (!token.value) {
// 没有 token,重定向到登录页面
return navigateTo('/login');
}
if (userRole.value === 'admin') {
// 管理员权限,可以访问特定的管理页面
if (to.path.startsWith('/admin')) {
return;
} else {
// 非管理页面,重定向到管理员首页
return navigateTo('/admin/dashboard');
}
} else if (userRole.value === 'user') {
// 普通用户权限,不能访问管理页面
if (!to.path.startsWith('/admin')) {
return;
} else {
// 尝试访问管理页面,重定向到用户首页
return navigateTo('/user/dashboard');
}
}
});
具体的权限设计可以根据业务场景来,比如设置在definePageMeta
里或者保存在 pinia
这种全局状态管理器里面。
nuxt其它约定配置的介绍
上文以及前文已经提到了Nuxt
文件目录里面的pages
和middleware
,想必大家看过之后已经能够学会使用Nuxt
路由的配置和客户端中间件的使用了接下来我们继续对其它的约定文件夹做介绍
nuxt自动导入
说完路由相关信息我们就回到组件化开发下内容,在介绍Nuxt
组件化开发之前,我们先来说一下Nuxt
的自动导入,我们在Vue
的页面中使用组件需要导入组件,或者在全局挂载组件才可以使用。
但是Vue
只要在components
文件夹下面定义好组件就可以直接在任意页面和组件使用,不需要再做导入操作,不仅仅是自定义组件,Nuxt特有的组件,vue特有的api,以及路径/composables 路径/utils等等。
参照前文的目录结构图,比如上面提到的middleware
内部定义的内容基本都是会被Nuxt
做自动导入的,下面我们来看如何使用自定义导入来做自定义组件的使用
自定义组件
组件化开发是我们使用vue这类框架的核心,我们看一下在Vue
里面如何实现自定义组件。前面提及了我们需要把组件放到components
文件夹下
components/
--- Header.vue
创建完成后,开始组件内容的编写
定义完成Header
组件后可以在任意页面或者任意组件内容直接使用它
我们来看一下页面效果
组件的定义和使用方式就是如上所述。
组件的命名约定
如果我们不是在components
的根目录文件下定义内容的话,而是存在着嵌套关系比如下方所示
--| top/
----| full/
------| Tip.vue
那么组件名称将会基于路径和文件名以大驼峰方式连起来,比如上面的top/full/Button.vue
注册名称将会是TopFullTip
,使用如下:
布局系统
nuxt
针对页面结构的复用性还提供了layout
配置,前文的layouts
文件夹就是用来完成该项配置的,我们在layouts
文件夹下面新建default.vue
文件作为默认layout
配置。
layouts/
--- default.vue
--- none.vue
当然你可以取任何名字,但是当使用其它命名layout
时需要在页面做相应的配置。
/*default.vue*/
<template>
<div>
<header>头部</header>
<nav>导航栏</nav>
<slot />
<footer>底部</footer>
</div>
</template>
/*none.vue*/
<template>
<div>
<slot />
</div>
</template>
我们查看页面
进入其它路由
发现layout
已经发挥作用,上文提到了设置其它命名layout
,只需要在对应的路由页面配置definePageMeta
设置其layout
为你定义的其它layout
。
上面我定义的none.vue
的layout
就是一个空白的layout
,大家可以根据内容定义不同的layout
在不同页面阶段,双端应用区分移动和pc的不同layout等等。
definePageMeta({
title: 'content',
layout: 'none' //配置你的layout文件名称
})
今天跟大家的讲解内容就到这里了,下一次文章我会告诉大家如何在Nuxt
项目里面引入UI库,以及引入Tailwindcss
,并展开对Nuxt
请求数据方法做介绍。