Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(head): drop @vueuse/head dependency #19519

Merged
merged 13 commits into from
Mar 8, 2023
12 changes: 7 additions & 5 deletions docs/1.getting-started/5.seo-meta.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ export default defineNuxtConfig({

## Composable: `useHead`

The `useHead` composable function allows you to manage your head tags in a programmatic and reactive way, powered by [@vueuse/head](https://github.com/vueuse/head).
The `useHead` composable function allows you to manage your head tags in a programmatic and reactive way,
powered by [Unhead](https://unhead.harlanzw.com/).

As with all composables, it can only be used with a components `setup` and lifecycle hooks.

Expand All @@ -62,7 +63,7 @@ useHead({
bodyAttrs: {
class: 'test'
},
script: [ { children: 'console.log(\'Hello world\')' } ]
script: [ { innerHTML: 'console.log(\'Hello world\')' } ]
})
</script>
```
Expand Down Expand Up @@ -153,6 +154,7 @@ The below is the non-reactive types used for `useHead`, `app.head` and component
interface MetaObject {
title?: string
titleTemplate?: string | ((title?: string) => string)
templateParams?: Record<string, string | Record<string, string>>
base?: Base
link?: Link[]
meta?: Meta[]
Expand All @@ -164,7 +166,7 @@ interface MetaObject {
}
```

See [zhead](https://github.com/harlan-zw/zhead/tree/main/packages/schema/src) for more detailed types.
See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts) for more detailed types.

## Features

Expand Down Expand Up @@ -228,7 +230,7 @@ Now, if you set the title to `My Page` with `useHead` on another page of your si

### Body Tags

You can use the `body: true` option on the `link` and `script` meta tags to append them to the end of the `<body>` tag.
You can use the `tagPosition: 'bodyClose''` option on applicable tags to append them to the end of the `<body>` tag.
danielroe marked this conversation as resolved.
Show resolved Hide resolved

For example:

Expand All @@ -238,7 +240,7 @@ useHead({
script: [
{
src: 'https://third-party-script.com',
body: true
tagPosition: 'bodyClose' // valid options are: 'head' | 'bodyClose' | 'bodyOpen'
}
]
})
Expand Down
8 changes: 4 additions & 4 deletions docs/3.api/1.composables/use-head.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ description: useHead customizes the head properties of individual pages of your

# `useHead`

Nuxt provides the `useHead` composable to add and customize the head properties of individual pages of your Nuxt app.

`useHead` is powered by [@vueuse/head](https://github.com/vueuse/head), you can find more in-depth documentation [here](https://unhead.harlanzw.com/)
The `useHead` composable function allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.harlanzw.com/).

::ReadMore{link="/docs/getting-started/seo-meta"}
::
Expand All @@ -17,7 +15,7 @@ Nuxt provides the `useHead` composable to add and customize the head properties
useHead(meta: MaybeComputedRef<MetaObject>): void
```

Below are the non-reactive types for `useHead`. See [zhead](https://github.com/harlan-zw/zhead/tree/main/packages/schema/src) for more detailed types.
Below are the non-reactive types for `useHead`.

```ts
interface MetaObject {
Expand All @@ -34,6 +32,8 @@ interface MetaObject {
}
```

See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/schema/src/schema.ts) for more detailed types.

::alert{type=info}
The properties of `useHead` can be dynamic, accepting `ref`, `computed` and `reactive` properties. `meta` parameter can also accept a function returning an object to make the entire object reactive.
::
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ export default defineBuildConfig({
'nuxt/schema',
'@vue/reactivity',
'@vue/shared',
'@vueuse/head'
'@unhead/vue'
]
})
5 changes: 2 additions & 3 deletions packages/nuxt/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@
"@nuxt/telemetry": "^2.1.10",
"@nuxt/ui-templates": "^1.1.1",
"@nuxt/vite-builder": "3.2.3",
"@unhead/ssr": "^1.1.16",
"@unhead/ssr": "^1.1.20",
"@unhead/vue": "^1.1.20",
"@vue/reactivity": "^3.2.47",
"@vue/shared": "^3.2.47",
"@vueuse/head": "^1.1.15",
"chokidar": "^3.5.3",
"cookie-es": "^0.5.0",
"defu": "^6.1.2",
Expand All @@ -82,7 +82,6 @@
"ufo": "^1.1.1",
"unctx": "^2.1.2",
"unenv": "^1.2.1",
"unhead": "^1.1.16",
"unimport": "^3.0.2",
"unplugin": "^1.1.0",
"untyped": "^1.2.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/core/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import escapeRE from 'escape-string-regexp'
import { defu } from 'defu'
import fsExtra from 'fs-extra'
import { dynamicEventHandler } from 'h3'
import { createHeadCore } from 'unhead'
import { createHeadCore } from '@unhead/vue'
import { renderSSRHead } from '@unhead/ssr'
import { distDir } from '../dirs'
import { ImportProtectionPlugin } from './plugins/import-protection'
Expand Down
22 changes: 16 additions & 6 deletions packages/nuxt/src/head/module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { resolve } from 'pathe'
import { addComponent, addPlugin, defineNuxtModule } from '@nuxt/kit'
import { addComponent, addPlugin, defineNuxtModule, tryResolveModule } from '@nuxt/kit'
import { distDir } from '../dirs'

const components = ['NoScript', 'Link', 'Base', 'Title', 'Meta', 'Style', 'Head', 'Html', 'Body']

export default defineNuxtModule({
meta: {
name: 'meta'
name: 'meta',
configKey: 'head'
},
defaults: {
polyfillVueUseHead: true
danielroe marked this conversation as resolved.
Show resolved Hide resolved
},
setup (options, nuxt) {
const runtimeDir = nuxt.options.alias['#head'] || resolve(distDir, 'head/runtime')

// Transpile @nuxt/meta and @vueuse/head
nuxt.options.build.transpile.push('@vueuse/head')
// Transpile @unhead/vue & unhead, this results in a smaller bundle
nuxt.options.build.transpile.push('@unhead/vue')

// Add #head alias
nuxt.options.alias['#head'] = runtimeDir
Expand All @@ -30,8 +34,14 @@ export default defineNuxtModule({
kebabName: componentName
})
}
// Allow dependencies using @vueuse/head to work
if (options.polyfillVueUseHead) {
// backwards compatibility
nuxt.options.alias['@vueuse/head'] = tryResolveModule('@unhead/vue') || '@unhead/vue'
addPlugin({ src: resolve(runtimeDir, 'lib/vueuse-head-polyfill.plugin') })
}

// Add library specific plugin
addPlugin({ src: resolve(runtimeDir, 'lib/vueuse-head.plugin') })
// Add library-specific plugin
addPlugin({ src: resolve(runtimeDir, 'lib/unhead.plugin') })
}
})
4 changes: 2 additions & 2 deletions packages/nuxt/src/head/runtime/composables.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { HeadEntryOptions, UseHeadInput, ActiveHeadEntry } from '@vueuse/head'
import { useSeoMeta as _useSeoMeta } from '@vueuse/head'
import type { HeadEntryOptions, UseHeadInput, ActiveHeadEntry } from '@unhead/vue'
import { useSeoMeta as _useSeoMeta } from '@unhead/vue'
import type { HeadAugmentations } from 'nuxt/schema'
import { useNuxtApp } from '#app/nuxt'

Expand Down
2 changes: 1 addition & 1 deletion packages/nuxt/src/head/runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { UseHeadInput } from '@vueuse/head'
import type { UseHeadInput } from '@unhead/vue'
import type { HeadAugmentations } from 'nuxt/schema'

export * from './composables'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { createHead, useHead } from '@vueuse/head'
import { createHead, useHead } from '@unhead/vue'
import { renderSSRHead } from '@unhead/ssr'
import { defineNuxtPlugin } from '#app/nuxt'
// @ts-expect-error untyped
Expand All @@ -16,25 +16,25 @@ export default defineNuxtPlugin((nuxtApp) => {
const unpauseDom = () => {
pauseDOMUpdates = false
// triggers dom update
head.internalHooks.callHook('entries:updated', head.unhead)
head.hooks.callHook('entries:updated', head)
}
head.internalHooks.hook('dom:beforeRender', (context) => { context.shouldRender = !pauseDOMUpdates })
head.hooks.hook('dom:beforeRender', (context) => { context.shouldRender = !pauseDOMUpdates })
nuxtApp.hooks.hook('page:start', () => { pauseDOMUpdates = true })
// wait for new page before unpausing dom updates (triggered after suspense resolved)
nuxtApp.hooks.hook('page:finish', unpauseDom)
nuxtApp.hooks.hook('app:mounted', unpauseDom)
}

// useHead does not depend on a vue component context, we keep it on the nuxtApp for backwards compatibility
// support backwards compatibility, remove at some point
nuxtApp._useHead = useHead
danielroe marked this conversation as resolved.
Show resolved Hide resolved

if (process.server) {
nuxtApp.ssrContext!.renderMeta = async () => {
const meta = await renderSSRHead(head.unhead)
const meta = await renderSSRHead(head)
return {
...meta,
bodyScriptsPrepend: meta.bodyTagsOpen,
// resolves naming difference with NuxtMeta and @vueuse/head
// resolves naming difference with NuxtMeta and Unhead
bodyScripts: meta.bodyTags
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @ts-expect-error ts failing with type
import { polyfillAsVueUseHead } from '@unhead/vue/polyfill'
import { defineNuxtPlugin } from '#app/nuxt'

export default defineNuxtPlugin((nuxtApp) => {
// avoid breaking ecosystem dependencies using low-level @vueuse/head APIs
polyfillAsVueUseHead(nuxtApp.vueApp._context.provides.usehead)
})
72 changes: 33 additions & 39 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 14 additions & 1 deletion test/basic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ describe('nuxt links', () => {
})

describe('head tags', () => {
it('should render tags', async () => {
it('SSR should render tags', async () => {
const headHtml = await $fetch('/head')

expect(headHtml).toContain('<title>Using a dynamic component - Title Template Fn Change</title>')
Expand All @@ -377,6 +377,19 @@ describe('head tags', () => {
expect(indexHtml).toContain('<title>Basic fixture</title>')
})

it('SPA should render appHead tags', async () => {
const headHtml = await $fetch('/head', { headers: { 'x-nuxt-no-ssr': '1' } })

expect(headHtml).toContain('<meta name="description" content="Nuxt Fixture">')
expect(headHtml).toContain('<meta charset="utf-8">')
expect(headHtml).toContain('<meta name="viewport" content="width=1024, initial-scale=1">')
})

it('legacy vueuse/head works', async () => {
const headHtml = await $fetch('/vueuse-head')
expect(headHtml).toContain('<title>using provides usehead and updateDOM - VueUse head polyfill test</title>')
})

it('should render http-equiv correctly', async () => {
const html = await $fetch('/head')
// http-equiv should be rendered kebab case
Expand Down
Loading