refactor(nuxt)!: move head option support into defineNuxtComponent (#8901)

This commit is contained in:
Daniel Roe 2022-11-15 14:47:17 +00:00 committed by GitHub
parent dfce660875
commit 2c2fbdffc9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 51 additions and 64 deletions

View File

@ -5,10 +5,10 @@ description: defineNuxtComponent() is a helper function for defining type safe c
# `defineNuxtComponent` # `defineNuxtComponent`
`defineNuxtComponent()` is a helper function for defining type safe Vue components using options API similar to [defineComponent()](https://vuejs.org/api/general.html#definecomponent). `defineNuxtComponent()` wrapper also adds support for `asyncData` component option. `defineNuxtComponent()` is a helper function for defining type safe Vue components using options API similar to [defineComponent()](https://vuejs.org/api/general.html#definecomponent). `defineNuxtComponent()` wrapper also adds support for `asyncData` and `head` component options.
::alert{type=warning} ::alert{type=warning}
Options API support for `asyncData` may well change before the stable release of Nuxt 3. Options API support for `asyncData` and `head` may well change before the stable release of Nuxt 3.
:: ::
::Alert ::Alert
@ -34,3 +34,19 @@ export default defineNuxtComponent({
}) })
</script> </script>
``` ```
## `head()`
If you choose not to use `setup()` in your app, you can use the `head()` method within your component definition:
```vue [pages/index.vue]
<script lang="ts">
export default defineNuxtComponent({
head(nuxtApp) {
return {
title: 'My site'
}
},
})
</script>
```

View File

@ -4,6 +4,9 @@ import { NuxtApp, useNuxtApp } from '../nuxt'
import { useAsyncData } from './asyncData' import { useAsyncData } from './asyncData'
import { useRoute } from './router' import { useRoute } from './router'
// eslint-disable-next-line import/no-restricted-paths
import { useHead } from '#head'
export const NuxtComponentIndicator = '__nuxt_component' export const NuxtComponentIndicator = '__nuxt_component'
async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) { async function runLegacyAsyncData (res: Record<string, any> | Promise<Record<string, any>>, fn: (nuxtApp: NuxtApp) => Promise<Record<string, any>>) {
@ -25,7 +28,7 @@ export const defineNuxtComponent: typeof defineComponent =
const { setup } = options const { setup } = options
// Avoid wrapping if no options api is used // Avoid wrapping if no options api is used
if (!setup && !options.asyncData) { if (!setup && !options.asyncData && !options.head) {
return { return {
[NuxtComponentIndicator]: true, [NuxtComponentIndicator]: true,
...options ...options
@ -43,6 +46,11 @@ export const defineNuxtComponent: typeof defineComponent =
promises.push(runLegacyAsyncData(res, options.asyncData)) promises.push(runLegacyAsyncData(res, options.asyncData))
} }
if (options.head) {
const nuxtApp = useNuxtApp()
useHead(typeof options.head === 'function' ? () => options.head(nuxtApp) : options.head)
}
return Promise.resolve(res) return Promise.resolve(res)
.then(() => Promise.all(promises)) .then(() => Promise.all(promises))
.then(() => res) .then(() => res)

View File

@ -10,7 +10,7 @@ export type { PageMeta } from '../pages/runtime'
// eslint-disable-next-line import/no-restricted-paths // eslint-disable-next-line import/no-restricted-paths
export type { MetaObject } from '../head/runtime' export type { MetaObject } from '../head/runtime'
// eslint-disable-next-line import/no-restricted-paths // eslint-disable-next-line import/no-restricted-paths
export { useHead, useMeta } from '#head' export { useHead } from '#head'
export const isVue2 = false export const isVue2 = false
export const isVue3 = true export const isVue3 = true

View File

@ -29,9 +29,6 @@ export default defineNuxtModule({
}) })
} }
// Add mixin plugin
addPlugin({ src: resolve(runtimeDir, 'mixin-plugin') })
// Add library specific plugin // Add library specific plugin
addPlugin({ src: resolve(runtimeDir, 'lib/vueuse-head.plugin') }) addPlugin({ src: resolve(runtimeDir, 'lib/vueuse-head.plugin') })
} }

View File

@ -12,9 +12,3 @@ import { useNuxtApp } from '#app'
export function useHead (meta: MaybeComputedRef<MetaObject>) { export function useHead (meta: MaybeComputedRef<MetaObject>) {
useNuxtApp()._useHead(meta) useNuxtApp()._useHead(meta)
} }
// TODO: remove useMeta support when Nuxt 3 is stable
/** @deprecated Please use new `useHead` composable instead */
export function useMeta (meta: MaybeComputedRef<MetaObject>) {
return useHead(meta)
}

View File

@ -1,24 +0,0 @@
import { getCurrentInstance } from 'vue'
import { useHead } from './composables'
import { defineNuxtPlugin, useNuxtApp } from '#app'
const metaMixin = {
created () {
const instance = getCurrentInstance()
if (!instance) { return }
const options = instance.type
if (!options || !('head' in options)) { return }
const nuxtApp = useNuxtApp()
const source = typeof options.head === 'function'
? () => options.head(nuxtApp)
: options.head
useHead(source)
}
}
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.mixin(metaMixin)
})

View File

@ -5,8 +5,7 @@ const commonPresets: InlinePreset[] = [
defineUnimportPreset({ defineUnimportPreset({
from: '#head', from: '#head',
imports: [ imports: [
'useHead', 'useHead'
'useMeta'
] ]
}), }),
// vue-demi (mocked) // vue-demi (mocked)

View File

@ -253,7 +253,6 @@ describe('head tags', () => {
expect(headHtml).toContain('<meta name="description" content="overriding with an inline useHead call">') expect(headHtml).toContain('<meta name="description" content="overriding with an inline useHead call">')
expect(headHtml).toMatch(/<html[^>]*class="html-attrs-test"/) expect(headHtml).toMatch(/<html[^>]*class="html-attrs-test"/)
expect(headHtml).toMatch(/<body[^>]*class="body-attrs-test"/) expect(headHtml).toMatch(/<body[^>]*class="body-attrs-test"/)
expect(headHtml).toContain('script>console.log("works with useMeta too")</script>')
expect(headHtml).toContain('<script src="https://a-body-appended-script.com" data-meta-body></script></body>') expect(headHtml).toContain('<script src="https://a-body-appended-script.com" data-meta-body></script></body>')
const indexHtml = await $fetch('/') const indexHtml = await $fetch('/')

View File

@ -29,6 +29,7 @@ describe.skipIf(isWindows)('minimal nuxt application', () => {
expect(stats.client.totalBytes).toBeLessThan(110000) expect(stats.client.totalBytes).toBeLessThan(110000)
expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(` expect(stats.client.files.map(f => f.replace(/\..*\.js/, '.js'))).toMatchInlineSnapshot(`
[ [
"_nuxt/composables.js",
"_nuxt/entry.js", "_nuxt/entry.js",
"_nuxt/error-404.js", "_nuxt/error-404.js",
"_nuxt/error-500.js", "_nuxt/error-500.js",

View File

@ -1,7 +1,15 @@
<script setup> <script>
const a = ref('') export default defineNuxtComponent({
head () {
useHead({ return {
htmlAttrs: {
class: 'html-attrs-test'
}
}
},
setup () {
const a = ref('')
useHead({
// title template function example // title template function example
titleTemplate: title => `${title} - Title Template Fn Change`, titleTemplate: title => `${title} - Title Template Fn Change`,
bodyAttrs: { bodyAttrs: {
@ -14,22 +22,11 @@ useHead({
} }
], ],
meta: [{ name: 'description', content: 'first' }] meta: [{ name: 'description', content: 'first' }]
})
useHead({ meta: [{ charset: 'utf-16' }, { name: 'description', content: computed(() => `${a.value} with an inline useHead call`) }] })
a.value = 'overriding'
}
}) })
useHead({ meta: [{ charset: 'utf-16' }, { name: 'description', content: computed(() => `${a.value} with an inline useHead call`) }] })
useMeta({ script: [{ children: 'console.log("works with useMeta too")' }] })
a.value = 'overriding'
</script>
<script>
export default {
head () {
return {
htmlAttrs: {
class: 'html-attrs-test'
}
}
}
}
</script> </script>
<template> <template>