mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-22 05:35:13 +00:00
feat(nuxt, bridge): support titleTemplate
, viewport
and charset
for useHead
(#4221)
This commit is contained in:
parent
041e8694d1
commit
1091d456a8
@ -1,26 +1,29 @@
|
||||
# Head Management
|
||||
|
||||
You can customize the meta tags for your site through several different ways:
|
||||
Out-of-the-box, Nuxt provides good default values for `charset` and `viewport` meta tags, but you can override these if you need to, as well as customizing other meta tags for your site in several different ways.
|
||||
|
||||
:ReadMore{link="/api/configuration/nuxt.config#head"}
|
||||
|
||||
## `useHead` Composable
|
||||
|
||||
Within your `setup` function, you can call `useHead` with an object of meta properties with keys corresponding to meta tags: `title`, `base`, `script`, `style`, `meta` and `link`, as well as `htmlAttrs` and `bodyAttrs`. Alternatively, you can pass a function returning the object for reactive metadata.
|
||||
Within your `setup` function, you can call `useHead` with an object of meta properties with keys corresponding to meta tags: `title`, `titleTemplate`, `base`, `script`, `style`, `meta` and `link`, as well as `htmlAttrs` and `bodyAttrs`. There are also two shorthand properties, `charset` and `viewport`, which set the corresponding meta tags. Alternatively, you can pass a function returning the object for reactive metadata.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
export default {
|
||||
setup () {
|
||||
useHead({
|
||||
meta: [
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1, maximum-scale=1' }
|
||||
],
|
||||
bodyAttrs: {
|
||||
class: 'test'
|
||||
}
|
||||
})
|
||||
```vue
|
||||
<script setup>
|
||||
useHead({
|
||||
titleTemplate: 'My App - %s', // or, title => `My App - ${title}`
|
||||
viewport: 'width=device-width, initial-scale=1, maximum-scale=1',
|
||||
charset: 'utf-8',
|
||||
meta: [
|
||||
{ name: 'description', content: 'My amazing site.' }
|
||||
],
|
||||
bodyAttrs: {
|
||||
class: 'test'
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
||||
::ReadMore{link="/api/composables/use-head"}
|
||||
@ -68,7 +71,7 @@ export default {
|
||||
|
||||
You can use `definePageMeta` along with `useHead` to set metadata based on the current route.
|
||||
|
||||
For example, to include the page title alongside your app name, first define your page title:
|
||||
For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically):
|
||||
|
||||
```vue{}[pages/some-page.vue]
|
||||
<script setup>
|
||||
@ -78,14 +81,14 @@ definePageMeta({
|
||||
</script>
|
||||
```
|
||||
|
||||
And then in your layout file:
|
||||
And then in your layout file you might use the route metadata you've previously set:
|
||||
|
||||
```vue{}[layouts/default.vue]
|
||||
<script setup>
|
||||
const route = useRoute()
|
||||
|
||||
useHead({
|
||||
title: computed(() => `App Name - ${route.meta.title}`)
|
||||
meta: [{ name: 'og:title', content: `App Name - ${route.meta.title}` }]
|
||||
})
|
||||
</script>
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ Nuxt 3 provides several different ways to manage your meta tags.
|
||||
2. Through the `useHead` [composable](/guide/features/head-management)
|
||||
3. Through [global meta components](/guide/features/head-management)
|
||||
|
||||
You can customize `title`, `base`, `script`, `style`, `meta`, `link`, `htmlAttrs` and `bodyAttrs`.
|
||||
You can customize `title`, `titleTemplate`, `base`, `script`, `style`, `meta`, `link`, `htmlAttrs` and `bodyAttrs`.
|
||||
|
||||
::alert{icon=📦}
|
||||
Nuxt currently uses [`vueuse/head`](https://github.com/vueuse/head) to manage your meta tags, but implementation details may change.
|
||||
|
@ -27,6 +27,7 @@
|
||||
export default {
|
||||
setup () {
|
||||
useHead({
|
||||
titleTemplate: '%s - useHead example',
|
||||
bodyAttrs: {
|
||||
class: 'test'
|
||||
}
|
||||
|
@ -21,12 +21,11 @@ export default defineNuxtModule({
|
||||
// Add #head alias
|
||||
nuxt.options.alias['#head'] = runtimeDir
|
||||
|
||||
// Global meta
|
||||
// Global meta -for Bridge, this is necessary to repeat here
|
||||
// and in packages/schema/src/config/_app.ts
|
||||
const globalMeta: MetaObject = defu(nuxt.options.app.head, {
|
||||
meta: [
|
||||
{ charset: options.charset },
|
||||
{ name: 'viewport', content: options.viewport }
|
||||
]
|
||||
charset: options.charset,
|
||||
viewport: options.viewport
|
||||
})
|
||||
|
||||
// Add global meta configuration
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { createHead, renderHeadToString } from '@vueuse/head'
|
||||
import { ref, watchEffect, onBeforeUnmount, getCurrentInstance } from 'vue'
|
||||
import { computed, ref, unref, watchEffect, onBeforeUnmount, getCurrentInstance, ComputedGetter } from 'vue'
|
||||
import defu from 'defu'
|
||||
import type { MetaObject } from '..'
|
||||
import { defineNuxtPlugin } from '#app'
|
||||
|
||||
@ -14,9 +15,26 @@ export default defineNuxtPlugin((nuxtApp) => {
|
||||
headReady = true
|
||||
})
|
||||
|
||||
nuxtApp._useHead = (meta: MetaObject) => {
|
||||
const headObj = ref(meta as any)
|
||||
head.addHeadObjs(headObj)
|
||||
const titleTemplate = ref<MetaObject['titleTemplate']>()
|
||||
|
||||
nuxtApp._useHead = (meta: MetaObject | ComputedGetter<MetaObject>) => {
|
||||
titleTemplate.value = (unref(meta) as MetaObject).titleTemplate || titleTemplate.value
|
||||
|
||||
const headObj = computed(() => {
|
||||
const overrides: MetaObject = { meta: [] }
|
||||
const val = unref(meta) as MetaObject
|
||||
if (titleTemplate.value && 'title' in val) {
|
||||
overrides.title = typeof titleTemplate.value === 'function' ? titleTemplate.value(val.title) : titleTemplate.value.replace(/%s/g, val.title)
|
||||
}
|
||||
if (val.charset) {
|
||||
overrides.meta!.push({ key: 'charset', charset: val.charset })
|
||||
}
|
||||
if (val.viewport) {
|
||||
overrides.meta!.push({ name: 'viewport', content: val.viewport })
|
||||
}
|
||||
return defu(overrides, val)
|
||||
})
|
||||
head.addHeadObjs(headObj as any)
|
||||
|
||||
if (process.server) { return }
|
||||
|
||||
|
@ -102,6 +102,8 @@ export default {
|
||||
head: {
|
||||
$resolve: (val, get) => {
|
||||
return defu(val, get('meta'), {
|
||||
charset: 'utf-8',
|
||||
viewport: 'width=device-width, initial-scale=1',
|
||||
meta: [],
|
||||
link: [],
|
||||
style: [],
|
||||
|
@ -21,4 +21,6 @@ export interface MetaObject extends Record<string, any> {
|
||||
style?: Array<Record<string, any>>
|
||||
/** Each item in the array maps to a newly-created `<script>` element, where object properties map to attributes. */
|
||||
script?: Array<Record<string, any>>
|
||||
|
||||
titleTemplate?: string | ((title: string) => string)
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ describe('pages', () => {
|
||||
|
||||
// should render text
|
||||
expect(html).toContain('Hello Nuxt 3!')
|
||||
// should render <Head> components
|
||||
expect(html).toContain('<title>Basic fixture</title>')
|
||||
// should inject runtime config
|
||||
expect(html).toContain('RuntimeConfig | testConfig: 123')
|
||||
// composables auto import
|
||||
@ -119,12 +117,20 @@ describe('pages', () => {
|
||||
describe('head tags', () => {
|
||||
it('should render tags', async () => {
|
||||
const html = await $fetch('/head')
|
||||
expect(html).toContain('<title>Using a dynamic component</title>')
|
||||
expect(html).toContain('<title>Using a dynamic component - Fixture</title>')
|
||||
expect(html).not.toContain('<meta name="description" content="first">')
|
||||
expect(html).toContain('<meta charset="utf-16">')
|
||||
expect(html).not.toContain('<meta charset="utf-8">')
|
||||
expect(html).toContain('<meta name="description" content="overriding with an inline useHead call">')
|
||||
expect(html).toMatch(/<html[^>]*class="html-attrs-test"/)
|
||||
expect(html).toMatch(/<body[^>]*class="body-attrs-test"/)
|
||||
expect(html).toContain('script>console.log("works with useMeta too")</script>')
|
||||
|
||||
const index = await $fetch('/')
|
||||
// should render charset by default
|
||||
expect(index).toContain('<meta charset="utf-8">')
|
||||
// should render <Head> components
|
||||
expect(index).toContain('<title>Basic fixture - Fixture</title>')
|
||||
})
|
||||
})
|
||||
|
||||
|
2
test/fixtures/basic/pages/head.vue
vendored
2
test/fixtures/basic/pages/head.vue
vendored
@ -5,7 +5,7 @@ useHead({
|
||||
},
|
||||
meta: [{ name: 'description', content: 'first' }]
|
||||
})
|
||||
useHead({ meta: [{ name: 'description', content: 'overriding with an inline useHead call' }] })
|
||||
useHead({ charset: 'utf-16', meta: [{ name: 'description', content: 'overriding with an inline useHead call' }] })
|
||||
useMeta({ script: [{ children: 'console.log("works with useMeta too")' }] })
|
||||
</script>
|
||||
|
||||
|
3
test/fixtures/basic/plugins/my-plugin.ts
vendored
3
test/fixtures/basic/plugins/my-plugin.ts
vendored
@ -1,4 +1,7 @@
|
||||
export default defineNuxtPlugin(() => {
|
||||
useHead({
|
||||
titleTemplate: '%s - Fixture'
|
||||
})
|
||||
return {
|
||||
provide: {
|
||||
myPlugin: () => 'Injected by my-plugin'
|
||||
|
Loading…
Reference in New Issue
Block a user