runtimeConfig与app.confg

回看app.config.tsnuxt.config.ts,俩者都是向应用程序的其余部分公开变量

runtimeConfig:使用环境变量构建后需要指定的私有或公共令牌。
app.config:在构建时确定的公共令牌、网站配置(如主题变体、标题和任何不敏感的项目配置)。

以下有使用准则,请参考以下标准:

特征比较

特征 runtimeConfig app.config
客户端 水合 捆绑
环境变量 ✅是的
反应性 ✅是的 ✅是的
类型支持 ✅部分 ✅是的
每个请求的配置 ✅不 ✅是的
热模块更换 ✅不 ✅是的
非原始 JS 类型 ✅不 ✅是的

样式

本地样式表

本地样式表存在assets/目录

组件内导入

通过@import导入

1
2
3
4
5
6
7
8
9
10
11
<script>
// Use a static import for server-side compatibility
import '~/assets/css/first.css'

// Caution: Dynamic imports are not server-side compatible
import('~/assets/css/first.css')
</script>

<style>
@import url("~/assets/css/second.css");
</style>

通过npm分发下载

终端下载

1
npm install animate.css

页面、组件、布局引用

1
2
3
4
5
6
7
<script>
import 'animate.css'
</script>

<style>
@import url("animate.css");
</style>

下载的包也可以在Nuxt配置中CSS属性中作为字符串引用

1
2
3
export default defineNuxtConfig({
css: ['animate.css']
})

外部样式表

可以通过Nuxt.config.ts文件的head添加link元素,实例如下:

1
2
3
4
5
6
7
export default defineNuxtConfig({
app: {
head: {
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
}
}})

动态添加样式表

使用useHead可组合项进行动态设置head的值

1
2
3
4
useHead({
link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }]
})

使用Nitro插件修改渲染的头部

这个属于更高级别的控制,可以使用钩子拦截渲染的html,以编程的方式修改头部。
创建插件如下:

1
2
3
4
5
6
export default defineNitroPlugin((nitro) => {
nitro.hooks.hook('render:html', (html) => {
html.head.push('<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css">')
})
})

外部样式表是阻止呈现的资源:必须在浏览器呈现页面之前加载和处理它们。包含不必要的大样式的网页需要更长的时间来呈现。

使用预处理器

  • 首先确保安装sass或者less
  • 知悉编写样式表的目录,通过预处理器的语法将原文件导入需要使用的地方
    1
    2
    3
    <style lang="scss">
    @use "~/assets/scss/main.scss";
    </style>
  • 或者在nuxt.config.ts中配置预处理器
    1
    2
    3
    export default defineNuxtConfig({
    css: ['~/assets/scss/main.scss']
    })

单文件组件动态样式

  • 通过ref或者reactive 变量动态依赖设置
  • 通过computed计算属性动态设置
  • 通过三目运算以数组的形式设置
  • 通过动态字符串

动态v-bind

v-bind函数在样式块中可以引用JavaScript变量和表达式。这种绑定是动态的,当依赖值变化时,样式也会更新。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup lang="ts">
const color = ref("red")
</script>

<template>
<div class="text">hello</div>
</template>

<style>
.text {
color: v-bind(color);
}
</style>

作用域组件

通过在style标签声明scoped属性,声明后的样式将仅应用于当前组件。

css模块

可以使用css模块替换为module属性。使用注入的变量访问。

1
2
3
4
5
6
7
8
9
<template>
<p :class="$style.red">This should be red</p>
</template>

<style module>
.red {
color: red;
}
</style>

预处理器

SFC样式快支持预处理器语法,Vite内置了对 .scss、.sass、.less、.styl 和 .stylus 文件的支持,无需配置。通过下载他们,直接在SFC中使用lang属性提供

1
<style lang="scss||sass||less||stylus||stylus">

路由

Nuxt文件路由为pages/目录下的文件提供路由。
Nuxt核心功能是将页面路由映射到文件。每个Vue文件都位于pages/目录中,文件名将映射到URL。

页面

Nuxt路由基于vue-router,从pages目录,基于文件名。
文件系统通过使用命名约定来创建动态路由和嵌套路由,如:文件目录为
| pages/
—| about.vue
—| index.vue
—| posts.vue
—-| [id].vue
映射出来路由文件为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"routes": [
{
"path": "/about",
"component": "pages/about.vue"
},
{
"path": "/",
"component": "pages/index.vue"
},
{
"path": "/posts/:id",
"component": "pages/posts/[id].vue"
}
]
}

导航

**<NuxtLink>**组件,类似于vue-router中,通过该组件链接之间页面,他呈现的是一个标签,属性设置为页面路由。
<NuxtLink>进入客户端的视口,Nuxt会自动提前加载链接页面组件和页面,从而快速地呈现页面。

1
2
3
4
5
6
7
8
9
10
11
<template>
<header>
<nav>
<ul>
<li><NuxtLink to="/about">About</NuxtLink></li>
<li><NuxtLink to="/posts/1">Post 1</NuxtLink></li>
<li><NuxtLink to="/posts/2">Post 2</NuxtLink></li>
</ul>
</nav>
</header>
</template>

路由参数

通过useRoute()api在Vue组件的块或者方法中使用,以便于访问当前路由的详细信息。

1
2
3
4
5
6
7
<script setup lang="ts">
const route = useRoute()

// When accessing /posts/1, route.params.id will be 1
console.log(route.params.id)
</script>

路由中间件

Nuxt提供了可以自定义的路由中间件,用于导航到特定路由之前提取要运行的代码

可以使用middleware属性在nuxt.config.ts中配置,或者在pages/目录下创建中间件文件,文件名必须为middleware.js或者middleware.ts,文件中必须导出一个函数,函数参数为context,返回一个Promise,当函数执行完成时,返回的Promise会作为路由执行结果。

路由中间件有三种:

  • 匿名(或内联)路由中间件,直接在使用它们的页面中定义。
  • 命名路由中间件,放置在middleware/目录,当在页面上使用时,将通过异步导入自动加载。(注意:路由中间件名称被规范化为 kebab-case,因此变为 。someMiddlewaresome-middleware
  • 全局路由中间件,放置在middleware/目录(带后缀),并将在每次路线更改时自动运行。.global

路由验证

Nuxt通过validate属性在路由中配置,**通过**definePageMeta**()**。
当路由匹配时,验证函数将被调用,如果返回false,路由将不会被激活,路由将被重定向到其他页面。

1
2
3
4
5
6
7
8
<script setup lang="ts">
definePageMeta({
validate: async (route) => {
// Check if the id is made up of digits
return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id)
}
})
</script>

SEO和Meta

默认设置

使用强大的头部配置、可组合项和组件可以改善Nuxt应用的SEO。
Nuxt提供了一些默认的SEO配置,可以在nuxt.config.ts中配置。

1
2
3
4
5
6
7
8
export default defineNuxtConfig({
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1',
}
}
})

app.head可以通过nuxt.config.ts配置文件中配置,允许我们为整个应用程序自定义头部。

useHead

useHead()可组合功能可以在组件中使用,我们可以方便的通过编程和响应式方法管理应用头部标签。跟其他可组合项一样,只能与组件和生命周期钩子一起使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup lang="ts">
useHead({
title: 'My App',
meta: [
{ name: 'description', content: 'My amazing site.' }
],
bodyAttrs: {
class: 'test'
},
script: [ { innerHTML: 'console.log(\'Hello world\')' } ]
})
</script>

useSeoMeta

useSeoMeta()useHead()的别名,它允许我们使用与useHead()相同的配置,但是它将自动处理titlemeta属性,这使得配置SEO更简单。
它的主要作用是可以将站点的SEO元标记标为具备ts支持的平面对象,便于更好的配置。

1
2
3
4
5
6
7
8
9
10
11
<script setup lang="ts">
useSeoMeta({
title: 'My Amazing Site',
ogTitle: 'My Amazing Site',
description: 'This is my amazing site, let me tell you all about it.',
ogDescription: 'This is my amazing site, let me tell you all about it.',
ogImage: 'https://example.com/image.png',
twitterCard: 'summary_large_image',
})
</script>

组件中

Nuxt提供 <Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html> 和<Head>,便于直接于组件间模板元数据交互。

注意,这些组件名称在模板中必须将他们大写。

并且可以接受嵌套的元标记,但这不会影响哪里嵌套的元标记在最终的 HTML 中呈现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<script setup lang="ts">
const title = ref('Hello World')
</script>

<template>
<div>
<Head>
<Title>{{ title }}</Title>
<Meta name="description" :content="title" />
<Style type="text/css" children="body { background-color: green; }" ></Style>
</Head>

<h1>{{ title }}</h1>
</div>
</template>```

## 用于`useHead()`,`app.head`和组件传值类型
```ts
interface MetaObject {
title?: string
titleTemplate?: string | ((title?: string) => string)
templateParams?: Record<string, string | Record<string, string>>
base?: Base
link?: Link[]
meta?: Meta[]
style?: Style[]
script?: Script[]
noscript?: Noscript[];
htmlAttrs?: HtmlAttributes;
bodyAttrs?: BodyAttributes;
}

特征

反应

可以通过计算值或响应式变量或对象来动态更新元标记,所有的属性都支持反应式。
建议使用getter()而不是computed(),因为computed()将创建一个新对象,这将导致不必要的重新渲染。
以下是三种更改元标记的三种写法。

  • 使用useHead()
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    <script setup lang="ts">
    const description = ref('My amazing site.')
    useHead({meta: [{ name: 'description', content: description }], })
    </script> ```
    + 使用SeoMeta
    ```vue
    <script setup lang="ts">
    const description = ref('My amazing site.')
    useSeoMeta({
    description
    })
    </script> ```
    + 组件
    ```vue
    <script setup lang="ts">
    const description = ref('My amazing site.')
    </script>

    <template>
    <div>
    <Meta name="description" :content="description" />
    </div>
    </template>```

    ### 标题模板
    可以通过`titleTemplate`~属性自定义动态标题模板,该属性可以是字符串或函数。
    这样可以将网站的名称添加到每个页面的标题中。

    如果需要使用某个函数完全控制标题,应该在`nuxt.config.ts`中进行设置。
    ```vue
    <script setup lang="ts">
    useHead({
    title:'sdf'
    titleTemplate: (titleChunk) => {
    return titleChunk ? `${titleChunk} - Site Title` : 'Site Title';
    }
    })
    </script>

Body标签

可以使用标签的选项将他们附加到标签的末尾

1
2
3
4
5
6
7
8
9
10
11
12
<script setup lang="ts">
useHead({
script: [
{
src: 'https://third-party-script.com',
// valid options are: 'head' | 'bodyClose' | 'bodyOpen'
tagPosition: 'bodyClose' // 标签闭合的末尾
}
]
})
</script>

动态标题

以下示例,设置带有占位符的字符串,这样就可以灵活的为Nuxt应用程序设置每个路由动态设置页面标题:titleTemplate %s || function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 <!-- 字符串匹配 -->
<script setup lang="ts">
useHead({
// as a string,
// where `%s` is replaced with the title
titleTemplate: '%s - Site Title',
})
</script>

<!-- 函数匹配 -->
<script setup lang="ts">
useHead({
// or as a function
titleTemplate: (productCategory) => {
return productCategory
? `${productCategory} - Site Title`
: 'Site Title'
}
})
</script>

nuxt.config也用于设置页面标题的替代方法,但是不允许页面标题是是动态的。因此,一般建议在文件中使用添加一个动态标题,然后将其应用月Nuxt应用程序的所有路由。

外部CSS

以下是可以使用useHeadlink可组合或使用组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!-- 使用头 -->
<script setup lang="ts">
useHead({
link: [
{
rel: 'preconnect',
href: 'https://fonts.googleapis.com'
},
{
rel: 'stylesheet',
href: 'https://fonts.googleapis.com/css2?family=Roboto&display=swap',
crossorigin: ''
}
]
})
</script>

<!-- 在组件中 -->
<template>
<div>
<Link rel="preconnect" href="https://fonts.googleapis.com" />
<Link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap" crossorigin="" />
</div>
</template>

过渡

页面过渡

  • 全部生效
    可以在Nuxt.config.ts中配置页面过渡,这个设置会将过渡应用于所有页面。如下所示:
    1
    2
    3
    4
    5
    export default defineNuxtConfig({
    app: {
    pageTransition: { name: 'page', mode: 'out-in' }
    },
    })
  • 页面之间
    只需要在页面之间添加过渡,则在app.vue设置过渡效果
  • 页面设置不同的
    将需要设置的页面通过definePageMeta键入pageTransition属性,例如:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    <script setup lang="ts">
    definePageMeta({
    pageTransition: {
    name: 'rotate'
    }
    })
    </script>```
    ## 布局过渡
    通过`defingNuxtConfig`配置启用`layout`布局过渡
    ```js
    export default defineNuxtConfig({
    app: {
    layoutTransition: { name: 'layout', mode: 'out-in' }
    },
    })

    也可以通过definePageMeta属性在页面中设置layoutTransition布局过渡
    1
    2
    3
    4
    5
    6
    7
    8
    9
    <script setup lang="ts">
    definePageMeta({
    layout: 'orange',
    layoutTransition: {
    name: 'slide-in'
    }
    })
    </script>

全局设置

可以使用nuxt.confg全局自定义默认这些过渡名称
密钥都接受pageTransitionlayoutTransitiontransitionProps可作为序列化值,其中都可以传递自定义CSS转换的、和其他有效的过渡道具。

1
2
3
4
5
6
7
8
9
10
11
12
13
export default defineNuxtConfig({
app: {
pageTransition: {
name: 'fade',
mode: 'out-in' // default
},
layoutTransition: {
name: 'slide',
mode: 'out-in' // default
}
}
})

覆盖全局过渡属性,为单个nuxt页面设置不同的过渡属性,可以使用definePageMeta

1
2
3
4
5
6
7
8
9
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'bounce',
mode: 'out-in' // default
}
})
</script>

禁用过渡

pageTransitionlayoutTransition属性可以设置为false,以禁用页面和布局过渡。
单个页面中

1
2
3
4
5
6
<script setup lang="ts">
definePageMeta({
pageTransition: false,
layoutTransition: false
})
</script>

全局设置

1
2
3
4
5
6
7
export default defineNuxtConfig({
app: {
pageTransition: false,
layoutTransition: false
}
})

通过JavaScript钩子精准控制过渡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => {
console.log('Before enter...')
},
onEnter: (el, done) => {},
onAfterEnter: (el) => {}
}
})
</script>

动态过渡

如果需要通过条件控制过渡,可以使用内联中间件分配不同的过渡名。to.meta.pageTransition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'slide-right',
mode: 'out-in'
},
// 中间件
middleware (to, from) {
if (to.meta.pageTransition && typeof to.meta.pageTransition !== 'boolean')
to.meta.pageTransition.name = +to.params.id > +from.params.id ? 'slide-left' : 'slide-right'
}
})
</script>

<template>
<h1>#{{ $route.params.id }}</h1>
</template>

<style>
.slide-left-enter-active,
.slide-left-leave-active,
.slide-right-enter-active,
.slide-right-leave-active {
transition: all 0.2s;
}
.slide-left-enter-from {
opacity: 0;
transform: translate(50px, 0);
}
.slide-left-leave-to {
opacity: 0;
transform: translate(-50px, 0);
}
.slide-right-enter-from {
opacity: 0;
transform: translate(-50px, 0);
}
.slide-right-leave-to {
opacity: 0;
transform: translate(50px, 0);
}
</style>

使用NuxtPage过渡

app.vue中使用时,transitionProps可以作为组件props传递以激活全局转换

1
2
3
4
5
6
7
8
9
10
11
<template>
<div>
<NuxtLayout>
<NuxtPage :transition="{
name: 'bounce',
mode: 'out-in'
}" />
</NuxtLayout>
</div>
</template>

数据获取

Nuxt提供了可组合项来处理应用程序中的数据获取。
Nuxt提供俩个组合项:

  • useFetch:组件设置函数中处理数据获取的最直接方法。
  • useAsyncData:结合使用,提供了更细颗粒度的控制
    Nuxt提供内置库:
  • $fetch:非常适合根据用户交互发出网络请求。

useFetch

useFetch该组合项是执行数据提取的最直接方法。

1
2
3
4
5
6
7
8
<script setup lang="ts">
const { data: count } = await useFetch('/api/count')
</script>

<template>
<p>Page visits: {{ count }}</p>
</template>

$fetch

Nuxt内置了$fetch包含该库,用于页面数据异步加载时候获取数据。

1
2
3
4
5
6
7
8
9
10
11
<script setup lang="ts">
async function addTodo() {
const todo = await $fetch('/api/todos', {
method: 'POST',
body: {
// My todo data
}
})
}
</script>

useAsyncData

useAsyncDatauseFetch的更细颗粒度的控制。负责包装异步逻辑,并解析后返回结果。

当CMS或第三方提供自己的API时,可以使用useAsyncData。例如:

1
2
3
4
5
6
7
<script setup lang="ts">
const { data, error } = await useAsyncData('users', () => myGetFunction('users'))

// This is also possible:
const { data, error } = await useAsyncData(() => myGetFunction('users'))
</script>

第一个参数useAsyncDatakey,用于缓存数据。这个key可以通过直接传递查询函数来忽略,key会自动生成。因为自动生成键只考虑调用的文件和行,为了避免产生不必要的行为因此,可以传递一个自定义的key。

1
2
3
4
5
6
7
8
<script setup lang="ts">
const { id } = useRoute().params

const { data, error } = await useAsyncData(`user:${id}`, () => {
return myGetFunction('users', { id })
})
</script>

可组合项是个可以等待多个完成,然后检索每个结果的好方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
<script setup lang="ts">
const { data: discounts, pending } = await useAsyncData('cart-discount', async () => {
const [coupons, offers] = await Promise.all([
$fetch('/cart/coupons'),
$fetch('/cart/offers')
])

return { coupons, offers }
})
// discounts.value.coupons
// discounts.value.offers
</script>

返回值

useFetch具有下面列出相同的返回值。

  • data:传入的异步函数结果
  • peding:布尔值,指示这个异步函数是否执行完毕
  • refresh/execute:可用于刷新函数返回的数据的函数
  • error:数据获取失败返回的错误信息
  • status:返回数据请求状态的字符串