1. SEO and Meta
使用强大的head配置、可组合组件和组件来改善nuxt应用的SEO。
- nuxt开箱即用,提供了相同的默认值,如果需要,你可以覆盖这些默认值。
- charset: utf-8
- viewport: width=device-width, initial-scale=1
可以在nuxt.config.ts中进行使用:
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1'
}
}
})
在nuxt.config.js中提供app.head属性可以让你自定义整个应用的头部,这个方法不允许提供响应数据,建议在app.vue中使用useHead()方法。
1.1 useHead()
useHead可组合功能允许以编程式、响应式的方式管理head标记,由Unhead提供支持。
与所有组合式一样,他只能用于script setup中使用像生命周期hooks用法那样
index.vue
<script setup lang="ts">
useHead({
title: '首页-感受nuxt魅力',
meta: [
{
name: 'description',
content: 'My nuxt meta use useHead'
}
],
bodyAttrs: {
class: 'usehead_style'
},
script: [{
innerHTML: 'console.log(\'Hello useHead nuxt page\')'
}]
})
</script>
<style>
.usehead_style {
background: #0189ff;
}
</style>
- 我们之前在开发PC/M站时,在不同的页面总要展示不同的title等meta信息,此功能刚好适用。
1.2 useHeadSafe()
useHeadSafe事故useHead外部再进行一次包装,将输入限制为只允许安全的值输入。
useHeadSafe({
script: [
{ id: 'xss-script', innerHTML: 'alert("xss")' }
],
meta: [
{ 'http-equiv': 'refresh', content: '0;javascript:alert(1)' }
]
})
// Will safely generate
// <script id="xss-script"></script>
// <meta content="0;javascript:alert(1)">
但同样的代码放在useHead中,就可以完整的被执行,会有风险。
1.3 useSeoMeta 和 useServerSeoMeta
useSeoMeta和useServerSeoMeta组合式函数可以让你将网站的SEO 元标签定义成一个平面对象,并具有完整的typescript支持。这样可以避免拼写错误和常见错误,例如使用name 而不是 property。
- 官方示例
<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>
- 业务场景示例:若你有一个商品详情页面,不同的商品meta title是商品的名称,那么可以如下操作:
<script setup>
const route = useRoute();
const id = route?.params?.id || 0
const name = route?.query?.name || ''
const title = ref('')
onMounted(() => {
title.value = name
})
useSeoMeta({
title,
description: () => `description: ${title.value}`
})
</script>
这样我们可以看到的效果是:当我们跳转至对应商品页面时,就会展示对应的title和description。
- 像useSeoMeta一样,useServerSeoMeta让网站的SEO标签定义为具有完整typescript支持的平面对象。在大多数情况下,meta不需要响应,因为机器人只会扫描初始负载。因为,建议使用useServerSeoMeta作为一个以性能为重点的应用程序,它不会在客户机上做任何事情(或返回一个head对象),参数与useSeoMeta完全相同。
1.4 Components
1.4.1 介绍
- nuxt提供了
<Title>, <Base>, <NoScript>, <Style>, <Meta>, <Link>, <Body>, <Html>,<Head>
组件,这样就可以直接与组件模板中的元数据进行交互。 - 因为这些组件名与原生HTML元素相匹配,所以在模板中非常重要的第一点就是要记住大写。
<Head>和<Body>
可以接受嵌套的元标签(出于美观的原因),但这对嵌套元标签在最终HTML中的呈现位置没有影响。
1.4.2 代码示例
<template>
<div>
<Head>
<Title>{{ title }}</Title>
<Meta name="description" :content="content"/>
<Style type="text/css" children="body { background-color: green; }" ></Style>
</Head>
this is about pages
</div>
</template>
<script setup lang="ts">
const title = ref('About 自定义title')
const content = ref('作者: Ably')
</script>
此时在about页面上,我们可以看到对应标签生效后的全部内容。例如:网页title变为了我们设置的’About 自定义title’,页面的整体背景颜色变为了绿色。
1.4.3 使用场景
在业务场景中的应用,我们可以全局设置整个应用的meta数据,形成一个整体的风格,但是在一些活动页面/介绍页面,可以有所不同,此时就可以在活动/介绍组件中,通过上述方式,对meta元数据进行自定义设置。
1.5 Types
下面是用于useHead、app.head和组件的非响应式类型。
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;
}
1.6 特性(Features)
1.6.1 响应式(Reactivity)
- 所有属性都支持响应式,例如computed、getter和reactive方式。
- 建议使用getter (() => value)而不是computed(computed(() => value))。
<script setup lang="ts">
const description = ref('My amazing site.')
useHead({
meta: [
{ name: 'description', content: description }
],
})
</script>
1.6.2 Title Template
- 可以使用titleTemplate选项提供一个动态模板,用于自定义网站的标题。例如,将网站名称添加到每个页面标题中。
- titleTemplate可以是字符串(其中%s)会被标题替换,也可以是函数
- 如果你想使用一个函数(用于完全控制), 就不能再nuxt.config中设置,需要在app.vue中进行设置,这样会应用到网站上的所有页面。
app.vue中对于整个项目title的设置
<script setup lang="ts">
useHead({
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} - Ably App` : 'Ably App'
}
})
</script>
如果在页面中设置过title,那么会在这个后面拼接上 Ably App, 否则直接展示 Ably App
1.6.3 Body Tags
可以在标签上使用tagPosition: 'bodyClose'
选项,将他们附加到<body>
标签的末尾
<script setup lang="ts">
useHead({
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} - Ably App` : 'Ably App'
},
script: [
{
src: 'https://third-party-script.com',
// valid options are: 'head' | 'bodyClose' | 'bodyOpen'
tagPosition: 'bodyClose'
}
]
})
</script>
例如,我们可以通过这种方式引入第三方库,或者插件。
1.7 Examples 官方示例
1.7.1 With definePageMeta
- 在您的pages/目录中,您可以使用definePageMeta和useHead来基于当前路由设置元数据。
- 例如,你可以首先设置当前页面标题(这是在构建时通过宏提取的,所以它不能动态设置):
<script setup lang="ts">
definePageMeta({
title: 'Some Page'
})
</script>
- 之后在你的布局文件中,你可能会使用之前设置的路由元数据
<script setup lang="ts">
const route = useRoute()
useHead({
meta: [{ property: 'og:title', content: `App Name - ${route.meta.title}` }]
})
</script>
1.7.2 动态标题(Dynamic Title)
在下面的例子中,titleTemplate要么被设置为带有%s占位符的字符串,要么被设置为一个函数,这样可以更灵活地为nuxt应用的每个路由动态设置页面标题
<script setup lang="ts">
useHead({
// as a string,
// where `%s` is replaced with the title
titleTemplate: '%s - Site Title',
// ... or as a function
titleTemplate: (productCategory) => {
return productCategory
? `${productCategory} - Site Title`
: 'Site Title'
}
})
</script>
1.7.3 内部CSS(External CSS)
下面的例子展示了如何使用useHead的link属性或使用< link >组件来启用Google Fonts:
- 方式一:Components方式
<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>
- 方式二:useHead 方式
<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>
2. Transitions
nuxt利用Vue的transition组件在页面和布局之间应用过渡动画
2.1 Page Transitions
可以启用页面转换对所有页面应用自动进行过渡
2.1.1 全局动画
- 在nuxt.config.ts中配置过渡动画选项
export default defineNuxtConfig({
app: {
pageTransition: { name: 'page', mode: 'out-in' }
},
})
- 在app.vue中配置选项执行的动画效果
<style>
.page-enter-active,
.page-leave-active {
transition: all 0.4s;
}
.page-enter-from,
.page-leave-to {
opacity: 0;
filter: blur(1rem);
}
</style>
- 这样再次切换页面时,我们就可以看到生动的动画效果啦
效果视频审核中
2.1.2 局部动画
- 要为页面设置不同的过渡,可以在页面的definePageMeta中设置pageTransition 键
- 例如在About.vue中设置单独的页面效果
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'rotate'
}
})
</script>
此时在app.vue中定义rotate动画效果
.rotate-enter-active,
.rotate-leave-active {
transition: all 0.4s;
}
.rotate-enter-from,
.rotate-leave-to {
opacity: 0;
transform: rotate3d(1, 1, 1, 15deg);
}
这样去about页面,我们就可以看到最新的效果啦
2.1.3 动画设置
我们在定义动画时可以看到,无论是在nuxt.config.ts中定义pageTransition,还是在definePageMeta中定义pageTransition,都有一个name,是这个动画的名称。在定义动画时,与vue一样
- [name]-enter-active、[name]-leave-active 设置动画属性及持续时间
- [name]-enter-from、[name]-leave-to 设置动画变化效果
2.2 Layout transitions
2.2.1 全局layout动画
- 参考官方示例,讲解layout布局使用,以及切换布局时的动画效果
- layouts/default.vue 默认布局效果,与pages同级
<template>
<div>
<pre>default layout</pre>
<slot />
</div>
</template>
<style scoped>
div {
background-color: lightgreen;
}
</style>
- layouts/orange.vue 橙色布局效果
<template>
<div>
<pre>orange layout</pre>
<slot />
</div>
</template>
<style scoped>
div {
background-color: #eebb90;
padding: 20px;
height: 100vh;
}
</style>
- app.vue中,定义layout动画效果,以及在nuxtpage标签外包一层nuxtlayout
<template>
<div>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</div>
</template>
<style>
.layout-enter-active,
.layout-leave-active {
transition: all 0.4s;
}
.layout-enter-from,
.layout-leave-to {
filter: grayscale(1);
}
</style>
- nuxt.config.ts中配置 layout动画
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
devtools: { enabled: true },
app: {
head: {
title: '默认标题',
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1'
},
pageTransition: { // page动画效果
name: 'page',
mode: 'out-in'
},
layoutTransition: { // layout动画效果
name: 'layout',
mode: 'out-in'
}
}
})
2.2.2 局部layout动画
- 当我们需要某个页面,例如about.vue页面使用orange布局时,可以在about.vue页面中设置
definePageMeta({
layout: 'orange'
})
这样,在切换页面时,使用不同布局的页面,就会有不同的动画效果,可以通过这种方式设置切换整体app风格。
2.2.3 动画设置
- 全局动画效果配置,可以在nuxt.config.ts中app下面进行配置pageTransition、layoutTransition,全部生效
- 当有自定义动画时,可以在页面中通过definePageMeta下面进行配置pageTransition、layoutTransition,局部页面生效
- pageTransition、layoutTransition 参数
- mode 动画进出方式: out-in
- name 动画名称,用于在css中定义动画
- 动画可以关闭,若配置,那么pageTransition、layoutTransition是上述定义的对象,若关闭,这两个值为false即可禁用
2.3 JavaScript Hooks
对于高级用例,可以使用JavaScript hooks钩子为nuxt页面创建高度动态和自定义的过渡动画。这种方式为JavaScript动画库(GSAP或Tween.js)提供了完美的用例。
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'custom-flip',
mode: 'out-in',
onBeforeEnter: (el) => {
console.log('Before enter...')
},
onEnter: (el, done) => {},
onAfterEnter: (el) => {}
}
})
</script>
2.4 动态过渡动画
- 定义layouts/default.vue页面及内容:
<script setup lang="ts">
const route = useRoute()
const id = computed(() => Number(route.params.id || 1))
const prev = computed(() => '/' + (id.value - 1))
const next = computed(() => '/' + (id.value + 1))
</script>
<template>
<div>
<slot />
<div v-if="$route.params.id">
<NuxtLink :to="prev">⬅️</NuxtLink> |
<NuxtLink :to="next">➡️</NuxtLink>
</div>
</div>
</template>
点击按钮,切换到前一个page 或 后一个page
- pages/[id].vue 动态公共页面及相关页面内容
<script setup lang="ts">
definePageMeta({
pageTransition: {
name: 'slide-right',
mode: 'out-in'
},
middleware (to, from) {
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>
加上动画效果后,与轮播组件一样流畅完整。
2.5 在<NuxtPage />
中增加transition动画
当<NuxtPage />
在app.vue中使用时,transition-props可以直接作为组件props传递来激活全局转换。
<template>
<div>
<NuxtLayout>
<NuxtPage :transition="{
name: 'bounce',
mode: 'out-in'
}" />
</NuxtLayout>
</div>
</template>
如果有用,点个赞呗~
总结用法,希望可以帮助到你,
我是Ably,你无须超越谁,只要超越昨天的自己就好~
官方文档