mirror of
https://github.com/nuxt/nuxt.git
synced 2024-11-28 16:42:04 +00:00
feat(head): useSeoMeta
composable (#18441)
This commit is contained in:
parent
de4086f6ed
commit
1406d21ed2
@ -70,6 +70,63 @@ useHead({
|
|||||||
::ReadMore{link="/docs/api/composables/use-head"}
|
::ReadMore{link="/docs/api/composables/use-head"}
|
||||||
::
|
::
|
||||||
|
|
||||||
|
## Composable: `useSeoMeta`
|
||||||
|
|
||||||
|
The `useSeoMeta` composable lets you define your site's SEO meta tags as a flat object with full TypeScript support.
|
||||||
|
|
||||||
|
This helps you avoid typos and common mistakes, such as using `name` instead of `property`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
#### Simple
|
||||||
|
|
||||||
|
```vue{}[app.vue]
|
||||||
|
<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>
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reactive
|
||||||
|
|
||||||
|
When inserting tags that are reactive, for example, from an API request, you should
|
||||||
|
use the computed getter syntax, the same as `useHead`.
|
||||||
|
|
||||||
|
```vue{}[app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
const data = useFetch(() => $fetch('/api/example'))
|
||||||
|
useSeoMeta({
|
||||||
|
ogTitle: () => `${data.value?.title} - My Site`,
|
||||||
|
description: () => data.value?.description,
|
||||||
|
ogDescription: () => data.value?.description,
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client-side Optimization
|
||||||
|
|
||||||
|
In most instances, the meta does not need to be reactive as robots will only scan the initial load.
|
||||||
|
|
||||||
|
The composable itself is ~2kB, so you may consider only using it on the server and having it be tree-shaken from the client bundle.
|
||||||
|
|
||||||
|
```vue{}[app.vue]
|
||||||
|
<script setup lang="ts">
|
||||||
|
// only run on the server or in development mode
|
||||||
|
if (process.dev || process.server) {
|
||||||
|
useSeoMeta({ description: () => myDescription.value })
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
::ReadMore{link="https://unhead.harlanzw.com/guide/guides/useseometa"}
|
||||||
|
::
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
Nuxt provides `<Title>`, `<Base>`, `<NoScript>`, `<Style>`, `<Meta>`, `<Link>`, `<Body>`, `<Html>` and `<Head>` components so that you can interact directly with your metadata within your component's template.
|
Nuxt provides `<Title>`, `<Base>`, `<NoScript>`, `<Style>`, `<Meta>`, `<Link>`, `<Body>`, `<Html>` and `<Head>` components so that you can interact directly with your metadata within your component's template.
|
||||||
|
@ -44,9 +44,9 @@
|
|||||||
"@nuxt/vite-builder": "3.0.0",
|
"@nuxt/vite-builder": "3.0.0",
|
||||||
"@vue/reactivity": "^3.2.45",
|
"@vue/reactivity": "^3.2.45",
|
||||||
"@vue/shared": "^3.2.45",
|
"@vue/shared": "^3.2.45",
|
||||||
"@vueuse/head": "^1.0.22",
|
"@vueuse/head": "^1.0.23",
|
||||||
"unhead": "^1.0.17",
|
"unhead": "^1.0.18",
|
||||||
"@unhead/ssr": "^1.0.17",
|
"@unhead/ssr": "^1.0.18",
|
||||||
"chokidar": "^3.5.3",
|
"chokidar": "^3.5.3",
|
||||||
"cookie-es": "^0.5.0",
|
"cookie-es": "^0.5.0",
|
||||||
"defu": "^6.1.1",
|
"defu": "^6.1.1",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { resolve } from 'pathe'
|
import { resolve } from 'pathe'
|
||||||
import { addComponent, addPlugin, defineNuxtModule } from '@nuxt/kit'
|
import { addComponent, addImportsSources, addPlugin, defineNuxtModule } from '@nuxt/kit'
|
||||||
import { distDir } from '../dirs'
|
import { distDir } from '../dirs'
|
||||||
|
|
||||||
const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body']
|
const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body']
|
||||||
@ -17,6 +17,13 @@ export default defineNuxtModule({
|
|||||||
// Add #head alias
|
// Add #head alias
|
||||||
nuxt.options.alias['#head'] = runtimeDir
|
nuxt.options.alias['#head'] = runtimeDir
|
||||||
|
|
||||||
|
addImportsSources({
|
||||||
|
from: '@vueuse/head',
|
||||||
|
imports: [
|
||||||
|
'useSeoMeta'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
// Register components
|
// Register components
|
||||||
const componentsPath = resolve(runtimeDir, 'components')
|
const componentsPath = resolve(runtimeDir, 'components')
|
||||||
for (const componentName of components) {
|
for (const componentName of components) {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/lodash.template": "^4",
|
"@types/lodash.template": "^4",
|
||||||
"@types/semver": "^7.3.13",
|
"@types/semver": "^7.3.13",
|
||||||
"@unhead/schema": "^1.0.17",
|
"@unhead/schema": "^1.0.18",
|
||||||
"@vitejs/plugin-vue": "^4.0.0",
|
"@vitejs/plugin-vue": "^4.0.0",
|
||||||
"nitropack": "^2.0.0-rc.0",
|
"nitropack": "^2.0.0-rc.0",
|
||||||
"unbuild": "latest",
|
"unbuild": "latest",
|
||||||
|
133
pnpm-lock.yaml
133
pnpm-lock.yaml
@ -121,7 +121,7 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@nuxt/test-utils': link:../../../packages/test-utils
|
'@nuxt/test-utils': link:../../../packages/test-utils
|
||||||
nuxt: link:../../../packages/nuxt
|
nuxt: link:../../../packages/nuxt
|
||||||
vitest: 0.27.2
|
vitest: 0.27.3
|
||||||
|
|
||||||
examples/app-config:
|
examples/app-config:
|
||||||
specifiers:
|
specifiers:
|
||||||
@ -417,10 +417,10 @@ importers:
|
|||||||
'@nuxt/vite-builder': workspace:*
|
'@nuxt/vite-builder': workspace:*
|
||||||
'@types/fs-extra': ^11.0.1
|
'@types/fs-extra': ^11.0.1
|
||||||
'@types/hash-sum': ^1.0.0
|
'@types/hash-sum': ^1.0.0
|
||||||
'@unhead/ssr': ^1.0.17
|
'@unhead/ssr': ^1.0.18
|
||||||
'@vue/reactivity': ^3.2.45
|
'@vue/reactivity': ^3.2.45
|
||||||
'@vue/shared': ^3.2.45
|
'@vue/shared': ^3.2.45
|
||||||
'@vueuse/head': ^1.0.22
|
'@vueuse/head': ^1.0.23
|
||||||
chokidar: ^3.5.3
|
chokidar: ^3.5.3
|
||||||
cookie-es: ^0.5.0
|
cookie-es: ^0.5.0
|
||||||
defu: ^6.1.1
|
defu: ^6.1.1
|
||||||
@ -448,7 +448,7 @@ importers:
|
|||||||
unbuild: ^1.1.1
|
unbuild: ^1.1.1
|
||||||
unctx: ^2.1.1
|
unctx: ^2.1.1
|
||||||
unenv: ^1.0.1
|
unenv: ^1.0.1
|
||||||
unhead: ^1.0.17
|
unhead: ^1.0.18
|
||||||
unimport: ^1.3.0
|
unimport: ^1.3.0
|
||||||
unplugin: ^1.0.1
|
unplugin: ^1.0.1
|
||||||
untyped: ^1.2.2
|
untyped: ^1.2.2
|
||||||
@ -463,10 +463,10 @@ importers:
|
|||||||
'@nuxt/telemetry': 2.1.9
|
'@nuxt/telemetry': 2.1.9
|
||||||
'@nuxt/ui-templates': 1.1.0
|
'@nuxt/ui-templates': 1.1.0
|
||||||
'@nuxt/vite-builder': link:../vite
|
'@nuxt/vite-builder': link:../vite
|
||||||
'@unhead/ssr': 1.0.17
|
'@unhead/ssr': 1.0.18
|
||||||
'@vue/reactivity': 3.2.45
|
'@vue/reactivity': 3.2.45
|
||||||
'@vue/shared': 3.2.45
|
'@vue/shared': 3.2.45
|
||||||
'@vueuse/head': 1.0.22_vue@3.2.45
|
'@vueuse/head': 1.0.23_vue@3.2.45
|
||||||
chokidar: 3.5.3
|
chokidar: 3.5.3
|
||||||
cookie-es: 0.5.0
|
cookie-es: 0.5.0
|
||||||
defu: 6.1.1
|
defu: 6.1.1
|
||||||
@ -493,7 +493,7 @@ importers:
|
|||||||
ultrahtml: 1.2.0
|
ultrahtml: 1.2.0
|
||||||
unctx: 2.1.1
|
unctx: 2.1.1
|
||||||
unenv: 1.0.1
|
unenv: 1.0.1
|
||||||
unhead: 1.0.17
|
unhead: 1.0.18
|
||||||
unimport: 1.3.0
|
unimport: 1.3.0
|
||||||
unplugin: 1.0.1
|
unplugin: 1.0.1
|
||||||
untyped: 1.2.2
|
untyped: 1.2.2
|
||||||
@ -510,7 +510,7 @@ importers:
|
|||||||
specifiers:
|
specifiers:
|
||||||
'@types/lodash.template': ^4
|
'@types/lodash.template': ^4
|
||||||
'@types/semver': ^7.3.13
|
'@types/semver': ^7.3.13
|
||||||
'@unhead/schema': ^1.0.17
|
'@unhead/schema': ^1.0.18
|
||||||
'@vitejs/plugin-vue': ^4.0.0
|
'@vitejs/plugin-vue': ^4.0.0
|
||||||
c12: ^1.1.0
|
c12: ^1.1.0
|
||||||
create-require: ^1.1.1
|
create-require: ^1.1.1
|
||||||
@ -545,7 +545,7 @@ importers:
|
|||||||
devDependencies:
|
devDependencies:
|
||||||
'@types/lodash.template': 4.5.1
|
'@types/lodash.template': 4.5.1
|
||||||
'@types/semver': 7.3.13
|
'@types/semver': 7.3.13
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
'@vitejs/plugin-vue': 4.0.0_vite@4.0.4
|
'@vitejs/plugin-vue': 4.0.0_vite@4.0.4
|
||||||
nitropack: 2.0.0-rc.0
|
nitropack: 2.0.0-rc.0
|
||||||
unbuild: 1.1.1
|
unbuild: 1.1.1
|
||||||
@ -2287,30 +2287,30 @@ packages:
|
|||||||
eslint-visitor-keys: 3.3.0
|
eslint-visitor-keys: 3.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@unhead/dom/1.0.17:
|
/@unhead/dom/1.0.18:
|
||||||
resolution: {integrity: sha512-iLVBQ1ck8r8U+n1Rdk93ry6NP0jgRXuJwrujXlYUFkrvbkXl08rTfyyXHT+AkA1aZgFLS9KtYtbEhwzNeWfZtg==}
|
resolution: {integrity: sha512-zX7w/Z3a1/spyQ3SuxB/0s1Tjx8zu5RzYBBXTtYvGutF8g/ScXreC0c5Vm5F3x4HOPdWG+71Qr/M+k6AxPLHDA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@unhead/schema/1.0.17:
|
/@unhead/schema/1.0.18:
|
||||||
resolution: {integrity: sha512-Vfc6HWcZAzibzlzBMNhVTOC7AYqvq3QMIY6VF7myowl4xbSiPLOmp63ZWAXKavYjHDchAJXvL/PF+6sDaIeCLQ==}
|
resolution: {integrity: sha512-LjNxwwQMZTD0b3LlB4/mmCZpO6HP7ZjK5sKuMpy7/+2O9HJO6TefxsDVrJVAitdUfm5Jej9cNEjnL2gJkc2uWg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@zhead/schema': 1.0.9
|
'@zhead/schema': 1.0.9
|
||||||
hookable: 5.4.2
|
hookable: 5.4.2
|
||||||
|
|
||||||
/@unhead/ssr/1.0.17:
|
/@unhead/ssr/1.0.18:
|
||||||
resolution: {integrity: sha512-+Ghf7RO7GDdS1AR38FFpmn0ZmhlgYxmAD6xg79Zgen6blmlfYVsWTL78rjnkkBc6EB9qvnGVAcqYtjO+KwjljA==}
|
resolution: {integrity: sha512-In0bJSLAyN8DdCuNJaoOIrjsK40g904ELR/0Eue9VzyO0fe147dPGfYlwwUrZOqj0JzGtndiQCF/D6bjn76ovw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@unhead/vue/1.0.17_vue@3.2.45:
|
/@unhead/vue/1.0.18_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-yTz8yKMWpseWXSL449vIm0lAIxZAYSLiWRBhUG5vcAPo6L39fyKsBXiUOHt+ScYLAQaoxT2gEEIcsbHMxv5pmQ==}
|
resolution: {integrity: sha512-VZ61a2pRtGXI9sj1aba5Qmm35veVvRDIE0Xsog3I0TfwavlwklZcg9bF2eT+GcDnsq1NxNO7uDyrb/+xNAzSxA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: '>=2.7 || >=3'
|
vue: '>=2.7 || >=3'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
hookable: 5.4.2
|
hookable: 5.4.2
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
dev: false
|
dev: false
|
||||||
@ -2722,15 +2722,15 @@ packages:
|
|||||||
- vue
|
- vue
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@vueuse/head/1.0.22_vue@3.2.45:
|
/@vueuse/head/1.0.23_vue@3.2.45:
|
||||||
resolution: {integrity: sha512-YmUdbzNdCnhmrAFxGnJS+Rixj+swE+TQC9OEaYDHIro6gE7W11jugcdwVP00HrA4WRQhg+TOQ4YcY2oL/PP1hw==}
|
resolution: {integrity: sha512-CiC9VWYbvwAqjWDBJH4WfQfBk7NWMZpvmpvIUYsm3X+aa8QHMiDGzR+RFKZSUtykiCGnSZk97yIvo5eJBmSh8A==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
vue: '>=2.7 || >=3'
|
vue: '>=2.7 || >=3'
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unhead/dom': 1.0.17
|
'@unhead/dom': 1.0.18
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
'@unhead/ssr': 1.0.17
|
'@unhead/ssr': 1.0.18
|
||||||
'@unhead/vue': 1.0.17_vue@3.2.45
|
'@unhead/vue': 1.0.18_vue@3.2.45
|
||||||
vue: 3.2.45
|
vue: 3.2.45
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -8082,11 +8082,11 @@ packages:
|
|||||||
node-fetch-native: 1.0.1
|
node-fetch-native: 1.0.1
|
||||||
pathe: 1.0.0
|
pathe: 1.0.0
|
||||||
|
|
||||||
/unhead/1.0.17:
|
/unhead/1.0.18:
|
||||||
resolution: {integrity: sha512-JjKjFxKwsmsAl/nLu3uTqd7bSTGOu4bhDj2KSYGBBfTVBmzJVK8lC/Aj0q/1au5g4NH4la+ulbmo3oWU4BqICw==}
|
resolution: {integrity: sha512-lHuOvFcj7ijFM6ceRuPq1+0sOAap8fueJxf+SkuWtfm68oxuLP8ct3C3oRyMT/hyWjzfWgoaECmjmw5x2cHnpg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@unhead/dom': 1.0.17
|
'@unhead/dom': 1.0.18
|
||||||
'@unhead/schema': 1.0.17
|
'@unhead/schema': 1.0.18
|
||||||
hookable: 5.4.2
|
hookable: 5.4.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
@ -8348,6 +8348,29 @@ packages:
|
|||||||
- terser
|
- terser
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vite-node/0.27.3_@types+node@18.11.18:
|
||||||
|
resolution: {integrity: sha512-eyJYOO64o5HIp8poc4bJX+ZNBwMZeI3f6/JdiUmJgW02Mt7LnoCtDMRVmLaY9S05SIsjGe339ZK4uo2wQ+bF9g==}
|
||||||
|
engines: {node: '>=v14.16.0'}
|
||||||
|
hasBin: true
|
||||||
|
dependencies:
|
||||||
|
cac: 6.7.14
|
||||||
|
debug: 4.3.4
|
||||||
|
mlly: 1.1.0
|
||||||
|
pathe: 0.2.0
|
||||||
|
picocolors: 1.0.0
|
||||||
|
source-map: 0.6.1
|
||||||
|
source-map-support: 0.5.21
|
||||||
|
vite: 4.0.4_@types+node@18.11.18
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@types/node'
|
||||||
|
- less
|
||||||
|
- sass
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vite-plugin-checker/0.5.4_vite@4.0.4:
|
/vite-plugin-checker/0.5.4_vite@4.0.4:
|
||||||
resolution: {integrity: sha512-T6y+OHXqwOjGrCErbhzg5x79NQZV46cgLwYTxuMQnDzAfA6skh2i8PIHcKks8ZlxopzbkvMb5vwc2DpNXiHJdg==}
|
resolution: {integrity: sha512-T6y+OHXqwOjGrCErbhzg5x79NQZV46cgLwYTxuMQnDzAfA6skh2i8PIHcKks8ZlxopzbkvMb5vwc2DpNXiHJdg==}
|
||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
@ -8512,6 +8535,56 @@ packages:
|
|||||||
- terser
|
- terser
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/vitest/0.27.3:
|
||||||
|
resolution: {integrity: sha512-Ld3UVgRVhJUtqvQ3dW89GxiApFAgBsWJZBCWzK+gA3w2yG68csXlGZZ4WDJURf+8ecNfgrScga6xY+8YSOpiMg==}
|
||||||
|
engines: {node: '>=v14.16.0'}
|
||||||
|
hasBin: true
|
||||||
|
peerDependencies:
|
||||||
|
'@edge-runtime/vm': '*'
|
||||||
|
'@vitest/browser': '*'
|
||||||
|
'@vitest/ui': '*'
|
||||||
|
happy-dom: '*'
|
||||||
|
jsdom: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@edge-runtime/vm':
|
||||||
|
optional: true
|
||||||
|
'@vitest/browser':
|
||||||
|
optional: true
|
||||||
|
'@vitest/ui':
|
||||||
|
optional: true
|
||||||
|
happy-dom:
|
||||||
|
optional: true
|
||||||
|
jsdom:
|
||||||
|
optional: true
|
||||||
|
dependencies:
|
||||||
|
'@types/chai': 4.3.4
|
||||||
|
'@types/chai-subset': 1.3.3
|
||||||
|
'@types/node': 18.11.18
|
||||||
|
acorn: 8.8.1
|
||||||
|
acorn-walk: 8.2.0
|
||||||
|
cac: 6.7.14
|
||||||
|
chai: 4.3.7
|
||||||
|
debug: 4.3.4
|
||||||
|
local-pkg: 0.4.3
|
||||||
|
picocolors: 1.0.0
|
||||||
|
source-map: 0.6.1
|
||||||
|
std-env: 3.3.1
|
||||||
|
strip-literal: 1.0.0
|
||||||
|
tinybench: 2.3.1
|
||||||
|
tinypool: 0.3.0
|
||||||
|
tinyspy: 1.0.2
|
||||||
|
vite: 4.0.4_@types+node@18.11.18
|
||||||
|
vite-node: 0.27.3_@types+node@18.11.18
|
||||||
|
why-is-node-running: 2.2.2
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- less
|
||||||
|
- sass
|
||||||
|
- stylus
|
||||||
|
- sugarss
|
||||||
|
- supports-color
|
||||||
|
- terser
|
||||||
|
dev: true
|
||||||
|
|
||||||
/vscode-jsonrpc/6.0.0:
|
/vscode-jsonrpc/6.0.0:
|
||||||
resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==}
|
resolution: {integrity: sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==}
|
||||||
engines: {node: '>=8.0.0 || >=10.0.0'}
|
engines: {node: '>=8.0.0 || >=10.0.0'}
|
||||||
|
Loading…
Reference in New Issue
Block a user