可以设置 meta title, description, keywords, author. 与组件同生命周期, 组件卸载时还原. 可以在 composition api 的 setup 里使用, 也可以再 Vue 传统组件写法里的 created, mounted, methods 里使用.

安装

创建HTMLHead.tsx

import {
  reactive,
  onBeforeUnmount,
  Teleport,
  getCurrentInstance,
  ComponentInternalInstance,
} from 'vue'

const store = reactive({
  title: '',
  description: '',
  keywords: '',
  author: '',
})

// try to remove existing elements
try {
  for (const sel of [
    'title',
    'meta[name="description"]',
    'meta[name="keywords"]',
    'meta[name="author"]',
  ]) {
    const el = document.head.querySelector(sel)
    if (el) {
      el.remove()
    }
  }
} catch (error) {}

export function HTMLHead() {
  return (
    <Teleport to="head">
      {store.title && <title>{store.title}</title>}
      {store.description && (
        <meta name="description" content={store.description} />
      )}
      {store.keywords && <meta name="keywords" content={store.keywords} />}
      {store.author && <meta name="author" content={store.author} />}
    </Teleport>
  )
}

export const useTitle = generateUseFunction('title')
export const useDescription = generateUseFunction('description')
export const useKeywords = generateUseFunction('keywords')
export const useAuthor = generateUseFunction('author')

function generateUseFunction(name: string) {
  return (value: string, vm?: ComponentInternalInstance) => {
    // @ts-ignore
    store[name] = value
    const thevm = vm || getCurrentInstance()
    if (!thevm) {
      throw new Error(
        `HTMLHead use ${name}: the second argument is required when called outside of setup`
      )
    }
    onBeforeUnmount(() => {
      // @ts-ignore
      if (store[name] === value) {
        // @ts-ignore
        store[name] = ''
      }
    }, thevm)
  }
}

引入 HTMLHead 组件并附加到根组件中, 如App.vue

<template>
  <div id="app">
    <HTMLHead />
  </div>
</template>

<script>
  import { HTMLHead } from './HTMLHead.tsx'
  export default {
    components: {},
  }
</script>

使用

import {
  useTitle,
  useDescription,
  useKeywords,
  useAuthor,
} from './HTMLHead.tsx'

在 setup 中使用

setup() {
  useTitle('title text')
}

:warning: 在异步 setup 及 setup 以外使用需要第二个参数.

created, mounted, methods 中使用

created() {
  useTitle('title text', this)
}

在 异步 setup 的 await 之后使用

async setup() {
  const vm = getCurrentInstance()
  await something
  useTitle('title text', vm)
}

如果你有更好的写法, 请告诉我.