nuxt基础
内置组件
nuxt为我们提供了一些内置组件,可以直接使用不用导入,其中常用的如下
SEO组件
- Html
- Body
- Head
- Title
- Meta
- Style
- Link
- NoScript
- Base
SEO组件可以更加方便的让我们再页面中添加利于seo的元素
NuxtWelcome
欢迎页面组件,该组件是nuxt/ui的一部分
NuxtPage
该组件时Nxut自带的页面的占位组件,是对router-view的封装,另外nuxt采用的是约定式路由,即不需要我们手动配置路由,nuxt会根据我们在pages文件夹中的文件以及app文件夹(开发中),具体的我们会在下边介绍。
NuxtLink
该组件相当于vue-router中的router-link组件,用于页面跳转
组件属性
- to: 表示要跳转的路径
- href: to的别名
- replace: boolean 默认为false,是否替换当前路由
- activeClass: 激活连接时的生效的类名
- target: 和a标签的target一样
ClientOnly
该组件的默认插槽的内容只会再客户端渲染,而fallback插槽的内容只在服务器端渲染
在服务端渲染期间,fallback内容会被渲染,一般会显示加载信息
<template>
<div>home</div>
<NuxtLink to="/">home</NuxtLink>
<div>
<ClientOnly>
<!--
在服务端渲染期间,fallback内容会被渲染,一般会显示加载信息
-->
<template #fallback>
<p>该内容只会再服务器端渲染</p>
</template>
</ClientOnly>
<ClientOnly>
<template #default>
<p>该内容会再客户端渲染</p>
</template>
</ClientOnly>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
(/pages/home/index.vue)
全局样式
安装sass
yarn add sass -D
新建assets文件夹,并在该文件夹中新建css文件夹,新建global.scss文件用于写一些全局样式,新建value.scss用于保存全局变量
在nuxt.config.ts中配置
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
//全局引入样式
css: ["@/assets/css/global.scss"],
vite: {
css: {
preprocessorOptions: {
scss: {
// 每个文件中都会加上这句话
additionalData: "@use '@/assets/css/value.scss' as *;",
},
},
},
},
});
之后我们在/pages/index.vue中使用,就可以看到效果了
静态资源
nuxt中提供两个目录用于存放静态资源,比如:stylesheets、fonts或img
- public
- 可以通过**/**来获取到public目录中的文件
例如:
<img
class="imgSty"
src="/01.jpg"
alt=""
/>
- asstes
- 可以通过**~/assets/或者@/assets/**路径径引用assets目录中的文件
例如:
<img
src="@/assets/img/01.jpg"
class="imgSty"
alt=""
/>
/pages/index.vue
pages
新建页面
- nuxt项目的页面一般是在pages目录下创建的,不过我们在新初始化的项目中官方默认给的是app目录,这两个目录使用方法差不多,并且app目录在最新版官方文档中并没有出现,表示正在实验阶段,后续语法可能会改变,所以就不过多赘述
- nuxt的页面路由也称为文件系统路由,我们不需要额外配置,直接根据/文件名的方式就可以访问,如果是文件夹,文件夹中的index.vue可以通过/文件夹名的方式访问,其他文件通过/文件夹名/文件名的方式访问
nuxt为我们提供了命令的方式去创建页面
# 创建home页面
npx nuxi add page home
NuxtLink
<NuxtLink to="/">home</NuxtLink>
编程式路由导航
nuxt3除了可以通过NuxtLink内置组件进行导航,也支持编程导航:navigateTo、useRouter
不过使用编程导航不利于SEO
navigateTo
基本使用
navigateTo(url,options?);
- url表示姚导航的连接,可以是字符串也可以是路由对象
- 路由对象:
- path:导航的连接
- query:query参数
- 路由对象:
- options导航配置,可选
- replace:默认是false,是否替换当前页面
- external:默认是false,是否允许导航到外部连接
- …
// 无参
navigateTo("/about", {
replace: false,
external: false,
});
// 有参(可以传递query参数或者params参数)
navigateTo({
path: "/home",
query: {
type: "phone",
},
});
/pages/index.vue
获取参数
const route = useRoute();
console.log(route.query);
//console.log(route.params);
/pages/home/index.vue
useRouter
常用API
const router=useRouter();
// 页面返回,类似于router.go(-1);
router.back();
// 页面前进,类似于router.go(1)
router.forward();
// 页面前进或者返回(接收一个数字)
router.go(1);
// 以push方式导航到新页面
router.push({path:"/home"});//建议使用navigateTo代替
// 以replace方式导航到新页面
router.replace({path:"/home"});//建议使用navigateTo代替
// 路由全局守卫,每次导航前执行,用于全局监听,接收一个回调函数
router.beforeEach();
// 路由守卫,每次导航后执行,用于全局监听,接收一个回调函数
router.afterEach();
获取参数
获取参数和navigateTo获取参数类似
动态路由
我们可以使用[]方括号的形式来编写动态路由,方括号里边存放是动态路由的参数
基本使用
pages/detail/[id].vue => /detail/:id
pages/detail/user-[id].vue => /detail/user-:id
pages/detail/[role]/[id].vue => /detail/:role/:id
获取参数
通过route.params获取传递的参数
const route = useRoute();
const id = route.params.id;
404页面
nuxt为我们提供了404页面,但是并不适用于页面展示,我们可以自己定义404页面,通过方括号内添加三个点即表示捕获所有不匹配路由
例如:[…not].vue(not是一个名字,这个可以自定义)
pages目录中新建[…not].vue
嵌套路由
再文件夹中创建一个与一级路由同名的文件夹就可以使用嵌套二级路由
- pages
---| parent/
----| index.vue
---| parent.vue
访问路径/parent/parent
页面布局
我们的网站中经常会遇到每个页面中都包含一样的页头以及页脚
我们就可以使用layout来进行布局
再跟目录新建layouts文件夹
页面布局可以使用两种布局方式一种是默认布局,一种是自定义布局
默认布局
在layouts目录下default.vue
然后再app.vue中通过NuxtLayout包裹
<!-- default.vue -->
<template>
<div class="layout">
<div>页头</div>
<slot></slot>
<div>页脚</div>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
<!-- app.vue -->
<template>
<div>
<NuxtLayout>
<!-- 默认欢迎页 -->
<!-- <NuxtWelcome /> -->
<!-- 占位组件 -->
<NuxtPage></NuxtPage>
</NuxtLayout>
</div>
</template>
自定义布局
在layouts中新建my-layout.vue,然后通过name属性指定布局类型(命名格式不能使用大驼峰)
<!-- my-layout.vue -->
<template>
<div>
<div>我是自定义页头</div>
<slot></slot>
</div>
</template>
<script setup lang="ts"></script>
<style scoped></style>
<!-- app.vue -->
<template>
<div>
<NuxtLayout name="my-layout">
<!-- 默认欢迎页 -->
<!-- <NuxtWelcome /> -->
<!-- 占位组件 -->
<NuxtPage></NuxtPage>
</NuxtLayout>
</div>
</template>
页面中指定布局方式
我们也可以使用definePageMeta来指定当前页面布局模板,
使用时NuxtLayout不能指定布局方式否则不生效
definePageMeta({
layout: "my-layout",
});
/pages/about/index.vue
生命周期
获取数据
我们在之前项目中,我们一般使用axios去请求数据,但是在nuxt中,官方更加推荐我们使用useFetch而不是axios
封装
import type { AsyncData, UseFetchOptions } from "nuxt/dist/app/composables";
const BASE_URL = "";
type Methods = "GET" | "POST";
class MyRequest {
request<T = any>(
url: string,
method: Methods,
data?: any,
options?: UseFetchOptions<T>
): Promise<AsyncData<T, Error>> {
return new Promise((resolve, reject) => {
const newOptions: UseFetchOptions<T> = {
baseURL: BASE_URL,
method: method,
...options,
};
if (method === "GET") {
newOptions.query = data;
}
if (method === "POST") {
newOptions.body = data;
}
useFetch<T>(url, newOptions as any)
.then((res) => {
resolve(res as AsyncData<T, Error>);
})
.catch((error) => {
reject(error);
});
});
}
get<T = any>(url: string, params?: any, options?: UseFetchOptions<T>) {
return this.request<T>(url, "GET", params, options);
}
post<T = any>(url: string, data?: any, options?: UseFetchOptions<T>) {
return this.request<T>(url, "POST", data, options);
}
}
export default new HYRequest();
全局状态数据共享
使用pinia去实现全局状态管理
具体使用pinia的方式我会在下一个博客中详细说明
yarn add pinia @pinia/nuxt
配置
在nuxt.config.ts中添加配置
modules: ["@pinia/nuxt"]
使用element plus
安装
yarn add element-plus
自动导入
yarn add unplugin-element-plus -D
babel 转义
yarn add unplugin-element-plus -D
nuxt.config.ts配置
// https://nuxt.com/docs/api/configuration/nuxt-config
import ElementPlus from "unplugin-element-plus/vite";
export default defineNuxtConfig({
devtools: { enabled: true },
css: [
"normalize.css",
"@/assets/css/global.scss",
"@/assets/cus-font/iconfont.css",
],
modules: ["@pinia/nuxt"],
// 配置自动导入样式
vite: {
css: {
preprocessorOptions: {
scss: {
additionalData: "@use '@/assets/css/variables.scss' as *;",
},
},
},
plugins: [ElementPlus({})],
},
app: {
// 可以给所有的页面的head添加一下SEO的信息
head: {
title: "",
meta: [
{
name: "description",
content:
"",
},
{
name: "keywords",
content: "",
},
{ name: "viewport", content: "width=device-width, initial-scale=1" },
],
link: [{ rel: "icon", type: "image/x-icon", href: "/favicon.ico" }],
noscript: [{ children: "Javascript is required" }],
},
},
build: {
// 该文件需要进行Babel转义
transpile: ["element-plus/es"],
},
});