20分钟带你入门Nuxt3
Nuxt
是一个使用Vue.js
做为视图引擎的Web
框架,可以用它做出SSR
友好的Web
程序,也可以使用其中content
功能来做博客文档(类似于VitePress
等工具)当然也支持纯静态站点部署。
环境配置
开始之前确保 Node
版本 >= 14.16
;
如果使用 VSCode
推荐预先安装相关插件:
- Vue Language Features (Volar) 使
VSCode
支持TypeScript
。 - TypeScript Vue Plugin (Volar)
Vue3
的语言支持拓展。
目前来说是可以工作,但在每个项目里我们都运行了两个语言服务实例:一个来自 Volar
,一个来自 VSCode
的内置服务。在一些项目可能会出现一些问题;
可以通过开启 Volar
的托管模式(Takeover)使用一个 TypeScript
语言服务实例同时为 .vue
和 .ts
文件提供支持;具体配置参考如下:
Nuxt 安装
pnpm dlx nuxi init nuxt-app
pnpm install --shamefully-hoist
pnpm run dev
执行完上述操作后,http://localhost:3000
此页面应能顺利打开。
项目结构
Api
学习开始之前,先看一下项目的结构。Nuxt
项目遵循了一套约定;这些约定与目录又息息相关。
.
├── .nuxt Nuxt 框架帮助生成的文件夹这其实就是当前运行的项目
├── node_modeules
├── .gitignore
├── app.vue 项目的入口文件
├── nuxt.config.ts 项目的配置文件
├── package.json
├── pnpm-lock.yaml
├── README.md
└── tsconfig.json
除了以上的一些目录,Nuxt
还允许你创建或者生成一些其他的目录或者文件,使应用程序的功能更加丰富。
打包产出 .output/
.output/
:执行 pnpm run build
后生成的打包结果。
静态资源 assets/
assets
:用来存放一些静态资源;比如图片、样式等;在 .vue
的模板中你可以这样去引入 assets
下的资源
<img src="~/assets/test.jpg" />
import '~/assets/test.js';
样式的引入需要注意,在普通的 style
标签中可以去引入各种资源的样式包括 scss
less
但如果你使用了 <style lang="scss"></style>
类似的标签则需要去下载预处理器和对应的 loader
<style>
import '~/assets/test.css';
import '~/assets/test.less';
import '~/assets/test.scss';
</style>
scss
的使用:pnpm install sass sass-loader -D
<style lang="scss">
@import url('~/assets/test.scss');
</style>
less
的使用:pnpm install less less-loader -D
<style lang="less">
@import '~/assets/test.less';
</style>
静态资源 public/
public
多用于去存储一些项目的公共文件,比如 favicon.ico
和 robots.txt
等;
但其实也不是不可以使用图片等其他资源,public
下的资源不会经过打包处理,在部署之后全部都在项目的根目录下可以直接访问;例如 http://www.xsq.com/test.jpg
。
如果需要在项目中去使用 public
下的资源则可以以 /
开始去引入内容;例如 <img src="/test.jpg" />
。
注意 如果你真的考虑这样使用,记得写完后重启项目验证是否成功,因为 public
并不在 nuxt
的动态解析范围中,可能会导致项目失去了关于这部分的热更新功能。
组件 components/
此章节开始之前希望你能先将
app.vue
中的示例<NuxtWelcome />
组件删除掉,还原一个干净的项目,方便后续的操作。
nuxt
会自动导入声明在 components
下的所有 .vue
文件当做项目中的组件,但是会有一套它自己的规则。
-
一级目录下的
component
例如:components/TheHeader.vue
,此种情况的使用方式为<TheHeader />
。 -
多级目录下的
components
例如components/base/foo/button.vue
,此种情况的使用方式为<BaseFooButton />
。 -
动态组件(
<componet :is="dynamicComponent" />
)的使用方式大致分成两种:- 配合
resolveComponent
方法。(推荐使用)
<template> <component :is="BaseDynamic" /> <component :is="Dynamic" /> </template> <script setup> // path: components/base/Dynamic.vue const BaseDynamic = resolveComponent('BaseDynamic'); // path: components/Dynamic.vue const Dynamic = resolveComponent('Dynamic'); </script>
- 可以选择在
nuxt.config.ts
中去全局的注册它们,使可以应用到全局的任何地方。
export default defineNuxtConfig { components: { global: true, dirs: ['~/components/global'] } };
使用时则可以这样
<components :is="GlobalDynamic" />
注意 此时
/components/global
下的所有组件都会被全局注册,但需要注意的是global/
下的组件不能和外部存在同名组件。 - 配合
-
动态导入 个人理解与
Vue
中的defineAsyncComponent
类似Vue
异步组件。使用方法如下
<template>
<!-- 对于定义并没有影响,只需要将需要加载的组件前面加上 Lazy 即可实现 -->
<LazyBaseDynamic v-if="isShow" />
<div v-else>loading...</div>
</template>
<script setup lang="ts">
const isShow = ref<boolean>(false);
setTimeout(() => {
isShow.value = true;
}, 2000);
</script>
client/server
组件,目前来看我并没有想到实际的使用场景,就暂时不说了,大伙有兴趣可以去看一下。也希望能得到评论区的最佳实践。ClientOnly
组件
可组合式(hooks) composables/
在 Vue
的开发过程中,对于一些逻辑的复用大多数人都会选择封装 hooks
来解决问题,nuxt
中推荐将 hooks
都存储在 composables/
中进行逻辑复用。
// composables/useCounter.ts
export default function useCounter() {
return 1 + 1;
}
<!-- app.vue -->
<template>
<div>{{ counter }}</div>
</template>
<script setup lang="ts">
const counter = useCounter();
</script>
默认情况下 composables
只会扫描第一层文件并对其加上自动导入的功能。如果想要更深层的引用有如下两种方案:
-
在
index.ts
再加一层默认导出,即可在全局内使用。(推荐)// composables/index.ts export { useFoo } from './nested/useFoo'; export { useBar } from './nested/useBar';
-
在
nuxt.config.ts
配置它可扫描的文件夹深度。export default defineNuxtConfig({ imports: { dirs: ['composables/**'] } });
文档 content/
类似于 Vue
生态下 VitePress
和 VuePress
。但是目前没有提供主题需要自己去实现。
关于 Nuxt3
的 content
部分个人认为很完美,但不是更完美。不像 Nuxt2
提供了一套成熟的主题,Nuxt3
的文档样式都要自己去定制。有兴趣可以去深入的了解下。
Nuxt3 content
文档,Content Nuxt
文档
页面 pages/
(路由)
Nuxt
提供一个基于文件的路由,即会根据 pages/
下的目录结构生成一套程序的导航机制,当然在文件的命名方面也要遵循它的机制。
在开始之前希望先了解两个 Nuxt
的内置组件 <NuxtPage />
和 <NuxtLink />
对标 Vue
中的 <RouterLink />
和 <RouterView />
先将 app.vue
中的内容修改为 <NuxtPage />
作为整个项目的页面的入口;
再创建 pages/index.vue
随意输入内容。index.vue
则是 http://localhost:3000/
中的 /
地址。
此时你还要注意两点内容:
pages/
下的文件默认支持jsx
或者h
渲染函数以及.vue
模板语法的。pages/
下的文件尽可能都保持只有一个根节点,保证页面路由可以正常切换。
此时整个项目的路由就已经全部被 pages/
所接管,就可以愉快的开始学习后面内容了。
静态路由 (入门)
-| pages/
---| index.vue
---| users
---| profile
以上路由路由的访问方式为 http://localhost:3000/users
和 http://localhost:3000/profile
对应的跳转方式为
<!--路由默认的名称为文件名-->
<NuxtLink to="users">UsersPage</NuxtLink>
<NuxtLink :to="{ name: 'users' }">UsersPage</NuxtLink>
动态路由
-| pages/
---| index.vue
---| users-[group].vue
以上路由的访问方式为 http://localhost:3000/users-xxxx
,但是动态路由的跳转还是老老实实的使用路径模式的方案吧。
访问参数的方式分为两种:
- 模板中直接使用
$route
对象,<div>{{ $route.params.group }}</div>
script
中使用useRoute
,const route = useRoute(); console.log(route.params.group);
嵌套路由
-| pages/
---| parent/
------| child.vue
---| parent.vue
以上路由的访问方式为 http://localhost:3000/parent
和 http://localhost:3000/parent/child
务必确认在 parent.vue
中存在 <NuxtPage />
保证子路由可以正常渲染
<!-- parent.vue -->
<template>
<div>
this is parent Page
<NuxtLink :to="{ path: '/parent/child' }">go to child</NuxtLink>
<NuxtPage />
</div>
</template>
<!-- child.vue -->
<template>
<div>this is child page <button @click="$router.back()">go back</button></div>
</template>
如果遇到动态路由导致的路由参数失效无法触发路由刷新的问题,则可以使用给路由加 key
来解决此问题。
- 模板方式
<NuxtPage :page-key="$route.fullPath" />
script
方式则通过definePageMeta
解决。definePageMeta({ key: route => route.fullPath });
404 页面
全局的 404
页面可以声明 page/404.vue
,来作为全局的 404 页面。
通用路由
通用路由的声明方式为 [...slug].vue
任何没有被导航命中的页面都会进入此页面,优先级低于 404.vue
。
[...slug].vue
的规则取决于它声明的位置。假设 pages/[...slug].vue
则是以全局为准。但如果你将它声明在某个页面下,比如 pages/parent/[...slug].vue
则通用页面只在 parent/
下生效。
编程式导航
日常的导航方式你仍可以使用 $router.push()
等方法。但 Nuxt
提供了一个兼容性更好的方法 navigateTo()
API 文档
const goUsers = async () => {
await navigateTo({ path: '/user', query: { msg: 'fromparent/child' } });
};
路由模式
Nuxt
默认的路由模式为 history
可以通过配置修改为 hash
模式,但是此模式不支持 ssr
。
export default defineNuxtConfig({
ssr: false,
router: {
options: {
hashMode: true
}
}
});
页面元数据
可以针对通用配置做出一些针对这个页面的调整。API 文档
<script setup lang="ts">
const route = useRoute();
definePageMeta({
key: route.fullPath
});
</script>
其余属性先混个眼熟,后面用到会再次使用。
key
防止路由的重复渲染keepalive
是否需要缓存这个页面或者定义新的缓存规则layout
需要用哪些布局,或者不适用布局layoutTransition
设置过渡的名称(修改过渡效果)或者不适用过渡pageTransition
设置页面的过渡名称或者不使用页面过渡validate
验证页面,类似于路由导航。这个功能也可以放到中间件中去使用。
布局 layouts/
日常应用程序的开发中,肯定少不了一些通用的布局页面,比如 通用 header
和 footer
中间放主体内容 container
。nuxt
允许你在 layouts/
中去按照特定的规则生成一些布局页面将页面中那些通用的布局放在这里。
注意: nuxt
的布局页面中必须要有一个根元素。
-
默认布局
layouts/default.vue
;default
文件为默认的布局,不需要任何引用声明后即可使用。配合NuxtLayout
来使用布局。<!-- default.vue --> <template> <div> <header>this is default header</header> <slot></slot> <!-- 插槽的使用方式与 vue 一致 --> <slot name="aside"></slot> <footer>this is default footer</footer> </div> </template> <!-- app.vue 任何页面都可使用,这里只是为了示例 --> <NuxtLayout> this is index page <template #aside> this is aside container </template> </NuxtLayout>
-
其他布局
layout/xxx.vue
;非default
命名的文件即为其他布局,在使用层面上面也基本一致,不过需要给NuxtLayout
执行一个name
来表明当前使用的哪个布局;或者也可以使用definePageMeta
声明一个layout
。 注意layout
如果为false
则布局不会生效<NuxtLayout name="custom"> 使用 custom 布局的页面 </NuxtLayout>
definePageMeta({ layout: 'custom' });
-
动态更改布局。通过
setPageLayout
方法来切换布局。setPageLayout('custom');
中间件 middleware/
在日常的使用的过程中称之为 路由拦截 应该更加合适。中间件的声明共有三种模式。
-
在页面中声明中间件
definePageMeta({ middleware: (to, from) => {} });
,此种中间件只会在当前页面导航被触发之前执行。 -
在
middleware/
中创建拦截文件,命名规则为kebab-case
例如some-middleware
。// middleware/auth.ts export default function auth(to, from) { console.log(to, from) }; // pages/user-detail.vue // 此种拦截方式在没有 return 值的情况下,如果在同一页面刷新会触发错误,不晓得是什么原因。 definePageMeta({ middleware: ['auth'] });
-
同样在
middleware/
中创建文件,拦截文件最后加.global
后缀,此拦截器不需要引用即会在全局的页面导航被触发之前执行。global
的拦截优先于以上两种拦截方式。
对比 vue-router
的拦截而言,中间件缺少了 next
第三个参数。需要 next
完成的事情则交给返回值来做。在 nuxt
中还提供了几个导航的钩子。
return abortNavigation()
:停止当前导航;return abortNavigation(error)
:拒绝当前导航并出现错误;return navigateTo()
:导航到指定页面;
```ts
// 伪代码
export default function auth(to, from) {
if (!to.params.id) {
return abortNavigation()
} else {
// get user token by id
if (getAuthToken(to.params.id)) {
return navigateTo('/home')
} else {
return abortNavigation('get user token error')
}
}
}
```
插件 plugins/
可以理解为 nuxt
注册组件库的地方。在这之前可能先需要了解一下配置相关的知识点,毕竟在 nuxt
认为约定大于一切。
plugins/
中文件在Vue
程序加载时一同被加载(可以直接使用无需在进行配置),可以使用文件名.server
.client
让其只在某一个环境下加载。plugins/
只有顶层的文件或者子目录下的index
文件会被自动注册。- 注册插件的方法为
defineNuxtApp
接受的唯一参数是nuxtApp
。(Vue.use
方法类似) - 通过给文件名加序号的方式决定插件注册的顺序。后注册的有能力访问前注册的插件。
plugins/ | - 1.myPlugin.ts | - 2.myOtherPlugin.ts
- 在插件中可以使用
composables/
下的hooks
。但是这个hook
不要涉及到vue
的生命周期否则将无法运行。 - 可以提供一些方法到 nuxt 实例上面方便后续的使用。比如
vue
程序中的$message
组件。import type { NuxtApp } from '#app'; import { useSum } from '~~/composables/useSum'; export default defineNuxtPlugin((nuxtApp: NuxtApp) => { return { provide: { sum: (a: number, b: number) => useSum(a, b) } }; });
如果像上面这样使用可能会失去一些类型提示。对应的你在<template> {{ $sum(1, 3) }} </template> <script setup lang="ts"> const { $sum } = useNuxtApp(); console.log($sum(2, 4)); </script>
index.d.ts
中配置类型声明。declare module '#app' { interface NuxtApp { $sum(a: number, b: number): void; } } declare module '@vue/runtime-core' { interface ComponentCustomProperties { $sum(a: number, b: number): void; } }
vue
插件注册需要依赖nuxtApp.vueApp.use
方法。同样也支持一些指令,组件的注册import type { NuxtApp } from '#app'; export default defineNuxtPlugin((nuxtApp: NuxtApp) => { nuxtApp.vueApp.use(xxx); // 等价于 vue.use nuxtApp.vueApp.directive('focus', {}); // 等价于 vue.directive })
服务端 server/
SSR
并不是一个单纯的前端框架,拥有的一定的后端服务能力,Nuxt
也是支持这一点,允许你在服务端去代理 api
注册路由以及中间件。你甚至可以将后端返回的一些不利于处理的数据,放到 server/
中进行数据的转换最终把完整的数据返回给前端,让前端只需要做纯渲染的逻辑不涉及任何数据处理逻辑。
有兴趣的小伙伴可以去了解一下 server
能力
应用程序配置文件 app.config.ts
此文件用于定义一些通用的可以被公开的配置,比如主题,模式等内容。于此对应的还有一个 runtimeConfig
在 nuxt.config.ts
中进行配置。可以在里面配置一些私密性的信息,比如环境变量还有 key
。
// app.config.ts
export default defineAppConfig({
theme: {
primaryColor: '#ababab'
}
});
// app.vue 访问时同时支持服务端和客户端
const appConfig = useAppConfig();
console.log(appConfig.theme);
// index.d.ts 如果想要完整的类型提示,需要配置额外的类型文件
declare module '@nuxt/schema' {
interface AppConfigInput {
/** Theme configuration */
theme?: {
/** Primary app color */
primaryColor?: string
}
}
}
export {}
项目配置文件 nuxt.config.ts
在前面的介绍中或多或少的也在这个文件中加了一些配置,但这也只是冰山一角,这里也不可能对它进行一个完整的示例或者说讲解。着重看一下 runtimeConfig
运行时配置。后续如果有其他用得到的地方再进行文档查阅。nuxt.config.ts
运行时配置整体分两个部分 public
客户端访问,以及默认的服务端访问。nuxt
默认支持 dotenv
这意味着可以在项目的根目录去根据环境声明一些 env
文件; 了解更多
export default defineNuxtConfig({
runtimeConfig: {
// 只能在服务端访问
apiSecret: process.env.API_SECRET
public: {
// 可以在客户端访问
apiBase: process.env.BASE_URL
}
}
})
访问 runtimeConfig
方式则是通过 useRuntimeConfig()
;
整体的目录规范
.
├── .nuxt Nuxt 框架帮助生成的文件夹这其实就是当前运行的项目
├── .output Nuxt 打包后的文件目录
├── assets 静态文件的存储位置 (参与打包)
├── components 组件目录
├── composables 通过 js 逻辑目录 (hooks)
├── content 静态文档目录
├── layouts 布局
├── middleware 中间件 比如 auth 权限认证
├── node_modeules
├── pages 页面
├── plugins 插件
├── public 项目根静态资源 (不参与打包)
├── server 服务端
├── .gitignore
├── .nuxtignore 构建阶段忽略配置
├── app.config.ts 定义运行时的应用程序配置,比如换肤功能
├── app.vue 项目的入口文件
├── nuxt.config.ts 项目的配置文件
├── package.json
├── pnpm-lock.yaml
├── README.md
└── tsconfig.json
自动导入 AutoImport
在日常使用 nuxt
的开发过程中,大多数情况下是不需要手动导入内容的,nuxt
会帮助我们进行一些自动导入。使其在开发的过程中可以直接使用。
虽然这个功能很好,但并不能解决开发过程中潜在的问题,比如我们需要某些类型的时候,需要手动的导入以获得更好的类型提示。
import type { NuxtApp } from '#app';
此时你可能会疑问 #app
是什么,从哪里来? 可以从 .nuxt/tsconfig.json
中找到答案。包括我们用到的所有方法,都可以在 .nuxt/
下的那些类型声明文件中找到合适的引用。
如果你觉得 nuxt
的自动导入会让你觉得方法难以溯源,你也可以关闭自动导入的功能;
export default defineNuxtConfig({
imports: {
autoImport: false
}
})
常见的一些导入方式有以下几种
import { useAsyncData } from '#app'; // nuxt 内部的一些方法及类型
import { ref, reactive } from '#imports'; // vue 内部的方法及类型
import { AsyncDynamic } from '#components'; // components 目录下的组件
import { useHead } from '#head'; // nuxt 头部的一些内容
// ~ 别名 .nuxt/tsconfig.json 中可以查询所有别名
import { useFoo } from '~/compoables'; // 导入 compoables 组合式函数
异步数据获取
整体来看 nuxt
中获取异步数据的方式有四种方式,可以被分为两组
useFetch
useLazyFetch
const { data: Ref<DataT>, pending: Ref<boolean>, refresh: (force?: boolean) => Promise<void>, error?: any } = useFetch(url: string, options?)
useAsyncData
useLazyAsyncData
const { data: Ref<DataT>,// 返回的数据结果 pending: Ref<boolean>,// 是否在请求状态中 refresh: (force?: boolean) => Promise<void>,// 强制刷新数据 error?: any // 请求失败返回的错误信息 } = useAsyncData( key: string, // 唯一键,确保相同的请求数据的获取和去重 fn: () => Object,// 一个返回数值的异步函数 options?: { lazy: boolean, server: boolean } // options.lazy,是否在加载路由后才请求该异步方法,默认为false // options.server,是否在服务端请求数据,默认为true // options.default,异步请求前设置数据data默认值的工厂函数(对lazy:true选项特别有用) // options.transform,更改fn返回结果的函数 // options.pick,只从数组中指定的key进行缓存 )
需要注意的是这几种获取数据的方式,只能在 setup
或者 生命周期的过程中去使用。
以 聚合数据 免费 api 为例
const url = 'http://apis.juhe.cn/simpleWeather/query?city=上海&key=5051878bb2230acc96a28f53944219a0';
const { data, pending, refresh, error } = await useFetch(url);
const { data, pending, refresh, error } = useLazyFetch(url);
const { data, pending, refresh, error } = useAsyncData('count', () => $fetch(url));
const { data, pending, refresh, error } = useLazyAsyncData('count', () =>$fetch(url));
- 包含
lazy
的数据获取方式不会阻止导航,也需要去额外处理数据为空的情况。 - 数据刷新可以使用返回的
refresh
方法refresh()
,默认的情况下refresh
将取消正在进行的请求,可以通过设置dedupe
来取消这种情况refresh({ dedupe: true })
。 - 如果页面中存在多个请求时进行刷新可以使用
refreshNuxtData
方法,他接收一个key
也就是AsyncData
系列钩子的第一个参数(fetch
的key
是默认生成的),可以不传递即刷新所有的接口请求。 - 与刷新请求对应的还有一个
clearNuxtData
方法同样接收一个key
。此方法会清除所有异步获取到数据包括状态等信息clearNuxtData()
- 多数情况下接口返回的数据不一定都是我们需要的,可以通过
fetch
的第二个参数,或者asyncData
的第三个参数指定一个options
通过pick
只提取我们想要的字段数据。const { data, pending, refresh, error } = await useFetch(url, { pick: ['result', 'code' ] });
可能遇到的问题:
- 在使用
refresh
或者refreshNuxtData
时可能会出现跨域的问题,问题出现的原因是 默认的情况下请求是在服务端发送的,刷新时不是。如果在发送请求时第二个或者第三个参数{ server: false }
同样也会碰到这个问题。
常规认知中可能配置一下 proxy
即可解决此类问题,但是目前来看 nuxt
暂时不支持此种操作。不过社区中提供了一些其他的解决方案 github
搜索引擎优化
常见的 SEO 优化方式主要就是设置 head
中的标签方便爬虫的读取。在 nuxt
中更改这些数据的方式,整体可以分为三种。
-
全局设置在
nuxt.config.ts
中设置,当应用程序的页面中没有设置数据时,以配置文件中的为主export default defineNuxtConfig({ app: { // head 中的标签基本都可以设置 head: { title: 'global title', meta: [ { name: 'description', content: 'global description' } ] } } })
-
useHead
单页面设置,设置了此种方式可以设置当前页面的head
信息useHead({ title: 'user title', meta: [{ name: 'description', content: 'this is ueser description' }] });
-
组件设置,除了
hook
的方式也nuxt
也提供了很多组件可以直接在模板中去编写相关内容。<Title>
、<Base>
、<Script>
、<NoScript>
、<Style>
、<Meta>
、<Link>
、<Body>
<Head> <Title>home title</Title> <Meta name="description" content="this is home desc"></Meta> </Head>
状态管理
在 nuxt
中官方推荐的方式是使用其内部提供的 useState
方法来进行状态的管理。
// composables/states.ts
export const useCounter = () => useState<number>('counter', () => 0);
export const useColor = () => useState<string>('color', () => 'pink');
<!--app.vue-->
<script setup>
const color = useColor() // Same as useState('color')
</script>
<template>
<p>Current color: {{ color }}</p>
</template>
过渡动画
页面过渡动画
在 nuxt.config.ts
中设置动画过渡的类名。
export default defineNuxtConfig({
app: {
// name: 页面转换的类名前缀,默认是 page
// mode: default out-in in-out
pageTransition: { name: 'page', mode: 'out-in' }
}
})
随后在项目的 app.vue
中增加动画 css
,即可实现路由切换的动画效果。
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
如果需要为某一个页面单独的设置过渡动画,也可以使用 definePageMeta
设置这个页面的动画前缀
<template>
this is profile page
<NuxtLink to="/"> go to index </NuxtLink>
</template>
<script setup lang="ts">
definePageMeta({
pageTranstion: {
name: 'profile',
mode: 'out-in'
}
});
</script>
<style lang="scss">
.profile-enter-active,
.profile-leave-active {
transition: all 0.4s;
}
.profile-enter-from,
.profile-leave-to {
transition: translate(50px, 0);
}
</style>
布局过渡动画
布局动画的整体核心和 page
的动画基本一致,默认类名会以 layout
开头。通过可以通过全局配置文件或者 definePageMeta
来进行设置。
类名对应为 layout-enter-active, layout-leave-active, layout-enter-from, layout-leave-to
export default defineNuxtConfig({
app: {
layoutTranstion: {
name: 'layout',
mode: 'out-in'
}
}
})
如果需要为某个单独的 layout
增加动画则可以使用以下语法
definePageMeta({
layout: 'orange',
layoutTransition: {
name: 'side-in'
}
})
layoutTransition
和 pageTransition
同样接受 false
以表示该页面或者全局禁用动画,这取决于你声明的位置。
如果 css
不能满足你的需求,也可以使用它所提供的钩子设置动画。
<script lang="ts" setup>
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => {
console.log('Before enter...')
},
onEnter: (el, done) => {},
onAfterEnter: (el) => {}
}
})
</script>
错误捕获
nuxt
提供三个钩子来捕获 vue
声明周期中的错误。分别为 vueApp.config.errorHanlder
和 onErrorCaptured
以及 nuxtApp.hook('vue:error', cb)
经过测试这只能侦听到生命周期中的错误。并不包含 setup
语法中的任意错误。
// plugins/index.ts
import type { NuxtApp } from '#app';
export default defineNuxtPlugin((nuxtApp: NuxtApp) => {
// err: 错误信息
// context: vue 实例
nuxtApp.vueApp.config.errorHandler = (err, context) => {
console.log(err, context);
}
// err: 错误信息
// instance: vue 实例
// info: 具体报错的钩子名称
nuxtApp.hook('vue:error', (err, instance, info) => {
console.log(err, instance, info);
});
});
<!-- app.vue -->
<script setup lang="ts">
// 该方式与 vue:error 参数一致
onErrorCaptured((err, instance, info) => {
console.log(err, instance, info)
})
</script>
触发的优先级为 onErrorCaptued
-> errorHandler
-> hook(vue:error, cb)
同样还提供了一个捕获 nuxt
错误的钩子 nuxtApp.hook('app:error', cb)
。
import type { NuxtApp } from '#app';
export default defineNuxtPlugin((nuxtApp: NuxtApp) => {
nuxtApp.hook('app:error', err => {
console.log(err);
});
});
错误页面
可以通过声明全局的错误页面来捕获那些程序中的致命错误。 error.vue
应当声明在页面的根目录下,与 app.vue
平级。
在 error
页面中可以使用 clearError
方法重定向到安全的页面。
<template>
<button @click="handleError">Clear errors</button>
</template>
<script setup>
const props = defineProps({
error: Object
})
const handleError = () => clearError({ redirect: '/' })
</script>
与 clearError
相对应还提供了两个抛出错误的方法。createError
和 showError
crateError
创建一个带有元信息的错误对象,在服务端会抛出致命错误,客户端则通过配置fatal
是否为true
来进行决定。if (!data.value) { throw createError({ statusCode: 404, message: '找不到页面', statusMessage: 'Not Fount' }); }
showError
直接抛出一个致命错误。使其进入错误页面if (!data.value) { showError({ statusCode: 404, message: '找不到页面', statusMessage: 'Not Fount' }); }
loading bar 定制
nuxt
提供了一个 <NuxtLoadingIndicator />
组件 可以根据此组件扩展 loading
效果
<script lang="ts" setup>
const show = ref(true)
</script>
<template>
<NuxtLayout>
<NuxtLoadingIndicator
:height="0"
>
<van-overlay :show="show" @click="show = false">
<div class="wrapper" @click.stop>
<van-loading type="spinner" color="#1989fa" />
</div>
</van-overlay>
</NuxtLoadingIndicator>
<NuxtPage />
</NuxtLayout>
</template>
<style lang="scss">
.wrapper {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
</style>
结语
Nuxt3
基于 Vue
而衍生出来的 SSR 框架,相信熟悉 Vue3
的小伙伴都不会很难。
更多的社区插件集成方案参考:nuxt.com/modules
移动端 nuxt
项目最佳实践参考:
转载自:https://juejin.cn/post/7186596767591301177