unhead-vue-skilld

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

unjs/unhead
@unhead/vue

unjs/unhead
@unhead/vue

Full-stack <head> manager built for Vue.
Version: 2.1.10 (Mar 2026) Deps: hookable@^6.0.1, unhead@2.1.10 Tags: next: 3.0.0-beta.9 (Feb 2026), beta: 3.0.0-beta.11 (Mar 2026), latest: 2.1.10 (Mar 2026)
References: Docs — API reference, guides
专为Vue构建的全栈<head>管理器。
版本: 2.1.10(2026年3月) 依赖: hookable@^6.0.1, unhead@2.1.10 标签: next: 3.0.0-beta.9(2026年2月), beta: 3.0.0-beta.11(2026年3月), latest: 2.1.10(2026年3月)
参考资料: 文档 — API参考、指南

API Changes

API变更

This section documents version-specific API changes — prioritize recent major/minor releases.
  • BREAKING:
    createHead()
    and
    createServerHead()
    removed from
    @unhead/vue
    root in v2 — use subpath imports:
    createHead()
    from
    @unhead/vue/client
    (SPA) or
    @unhead/vue/server
    (SSR);
    createServerHead()
    no longer exists source
  • BREAKING: Implicit context removed in v2 —
    setHeadInjectionHandler()
    deleted;
    useHead()
    called after an
    await
    in lifecycle hooks (e.g.
    onMounted
    ) throws because Vue context is lost; wrap async data fetching before calling
    useHead()
    source
  • BREAKING:
    vmid
    and
    hid
    tag properties removed in v2 — use
    key
    for deduplication:
    script: [{ key: 'my-key' }]
    source
  • BREAKING:
    children
    tag property removed in v2 — use
    innerHTML
    instead source
  • BREAKING:
    body: true
    tag property removed in v2 — use
    tagPosition: 'bodyClose'
    instead source
  • BREAKING:
    useScript()
    no longer returns a Promise in v2 —
    .then()
    calls silently fail; use
    .onLoaded(() => ...)
    instead source
  • BREAKING:
    useScript()
    API no longer accessible directly on the instance in v2 — must use
    .proxy
    explicitly:
    script.proxy.myFn()
    not
    script.myFn()
    ; code compiles but calls are lost at runtime source
  • BREAKING:
    stub()
    option and
    script:instance-fn
    hook removed from
    useScript()
    in v2 — replace with custom
    use()
    logic source
  • BREAKING: Promise inputs in
    useHead()
    no longer auto-resolved in v2 — await the promise before passing, or opt in to
    PromisePlugin
    from
    @unhead/vue/plugins
    source
  • BREAKING:
    TemplateParamsPlugin
    and
    AliasSortingPlugin
    no longer built-in in v2 — must opt in:
    createHead({ plugins: [TemplateParamsPlugin, AliasSortingPlugin] })
    imported from
    @unhead/vue/plugins
    source
  • BREAKING: Capo.js tag sorting is now the default in v2 — breaks snapshot tests; opt out with
    createHead({ disableCapoSorting: true })
    source
  • DEPRECATED:
    useServerHead()
    ,
    useServerHeadSafe()
    ,
    useServerSeoMeta()
    — use
    useHead()
    ,
    useHeadSafe()
    ,
    useSeoMeta()
    with
    import.meta.server
    conditionals or
    { mode: 'server' }
    option for tree-shaking
  • NEW:
    <Head>
    ,
    <Title>
    ,
    <Meta>
    ,
    <Link>
    ,
    <Script>
    template components — import from
    @unhead/vue/components
    source
  • NEW:
    DeprecationsPlugin
    from
    @unhead/vue/plugins
    — re-enables removed
    vmid
    ,
    hid
    ,
    children
    ,
    body
    properties for gradual migration to v2 source
Also changed:
@unhead/schema
deprecated — use
@unhead/vue/types
instead ·
createHeadCore
deprecated — use
createUnhead
· Default SSR tags auto-inserted in v2 (
charset
,
viewport
,
html lang="en"
); disable with
createHead({ disableDefaults: true })
· CJS exports removed, ESM only · Vue 2 support removed ·
useHead()
context lost after
async
in Vue lifecycle hooks — fetch data first, then call
useHead()
本部分记录对应版本的API变动,优先参考最新的主版本/次版本更新。
  • 破坏性变更:v2版本中
    @unhead/vue
    根导出移除了
    createHead()
    createServerHead()
    ,请使用子路径导入:从
    @unhead/vue/client
    (SPA场景)或
    @unhead/vue/server
    (SSR场景)导入
    createHead()
    createServerHead()
    已被移除 来源
  • 破坏性变更:v2版本移除了隐式上下文,
    setHeadInjectionHandler()
    已删除;如果在生命周期钩子(如
    onMounted
    )的
    await
    之后调用
    useHead()
    会抛出错误,因为Vue上下文已丢失;请在调用
    useHead()
    之前完成异步数据获取 来源
  • 破坏性变更:v2版本移除了
    vmid
    hid
    标签属性,请使用
    key
    实现去重:
    script: [{ key: 'my-key' }]
    来源
  • 破坏性变更:v2版本移除了
    children
    标签属性,请改用
    innerHTML
    来源
  • 破坏性变更:v2版本移除了
    body: true
    标签属性,请改用
    tagPosition: 'bodyClose'
    来源
  • 破坏性变更:v2版本中
    useScript()
    不再返回Promise,
    .then()
    调用会静默失败,请改用
    .onLoaded(() => ...)
    来源
  • 破坏性变更:v2版本中
    useScript()
    API不再可以直接在实例上访问,必须显式使用
    .proxy
    :调用方式为
    script.proxy.myFn()
    而非
    script.myFn()
    ;代码可正常编译但运行时调用会失效 来源
  • 破坏性变更:v2版本中
    useScript()
    移除了
    stub()
    选项和
    script:instance-fn
    钩子,请替换为自定义
    use()
    逻辑 来源
  • 破坏性变更:v2版本中
    useHead()
    不再自动解析Promise类型的输入,请在传入前await该Promise,或者从
    @unhead/vue/plugins
    引入
    PromisePlugin
    主动开启该能力 来源
  • 破坏性变更:v2版本不再内置
    TemplateParamsPlugin
    AliasSortingPlugin
    ,需要主动引入使用:从
    @unhead/vue/plugins
    导入后配置
    createHead({ plugins: [TemplateParamsPlugin, AliasSortingPlugin] })
    来源
  • 破坏性变更:v2版本默认开启Capo.js标签排序,会导致快照测试失败;可通过
    createHead({ disableCapoSorting: true })
    关闭该能力 来源
  • 已废弃:
    useServerHead()
    useServerHeadSafe()
    useServerSeoMeta()
    ,请改用
    useHead()
    useHeadSafe()
    useSeoMeta()
    配合
    import.meta.server
    条件判断,或者传入
    { mode: 'server' }
    选项实现tree-shaking
  • 新增:
    <Head>
    <Title>
    <Meta>
    <Link>
    <Script>
    模板组件,可从
    @unhead/vue/components
    导入 来源
  • 新增:
    @unhead/vue/plugins
    导出的
    DeprecationsPlugin
    ,可重新支持已移除的
    vmid
    hid
    children
    body
    属性,用于渐进式迁移到v2版本 来源
其他变更:
@unhead/schema
已废弃,请改用
@unhead/vue/types
·
createHeadCore
已废弃,请改用
createUnhead
· v2版本会自动插入默认SSR标签(
charset
viewport
html lang="en"
);可通过
createHead({ disableDefaults: true })
关闭 · 移除了CJS导出,仅支持ESM · 移除了Vue 2支持 · Vue生命周期钩子中执行
async
操作后
useHead()
会丢失上下文,请先获取数据再调用
useHead()

Best Practices

最佳实践

  • Always use
    injectHead()
    from
    @unhead/vue
    instead of
    getActiveHead()
    from
    unhead
    in Vue components —
    injectHead()
    binds to the Vue component context (visible in
    onServerPrefetch
    ), while
    getActiveHead()
    returns a shared cross-request instance that breaks in SSR. The maintainer confirmed this is the correct approach for Vue. source
  • Avoid calling
    useHead()
    inside watchers — each call creates a new entry rather than updating the existing one, leading to duplicate entries. Instead, pass reactive refs or computed getters directly to a single
    useHead()
    call at setup time so updates flow automatically. source
  • When
    useHead()
    must be called after async operations (e.g. inside
    onMounted
    ), capture the head instance at setup time with
    injectHead()
    and pass it as
    { head }
    in the second argument — Vue's inject context is lost after
    await
    . For most cases, prefer the reactive state pattern: define
    useHead()
    once at setup with computed getters, and update a
    ref
    asynchronously. source
  • Use
    useHeadSafe()
    instead of
    useHead()
    whenever head input comes from user-provided or third-party sources — it enforces an attribute whitelist and strips script tags and event handlers, preventing XSS without requiring manual sanitization. source
  • Add the
    UnheadVite()
    plugin from
    @unhead/addons/vite
    to your Vite config for Vue apps — it tree-shakes server-only composables from the client build and transforms
    useSeoMeta()
    calls into raw
    useHead()
    calls, saving ~3kb. Nuxt configures this automatically; standalone Vue apps must opt in. source
  • Pass
    { mode: 'server' }
    to
    useHead()
    for static SEO metadata (Open Graph images, robots, schema.org) that doesn't need client-side reactivity — this strips the tags from the client bundle entirely. Similarly use
    { mode: 'client' }
    for analytics scripts to keep them out of SSR output. Caveat:
    titleTemplate
    must be included in both environments to avoid title flashing. source
  • Use
    tagPosition: 'bodyClose'
    for non-critical scripts (analytics, chat widgets) instead of
    head
    — this prevents render-blocking and improves page load performance. Use
    tagPriority: 'critical' | 'high' | 'low'
    aliases rather than raw numbers to preserve Capo.js-derived ordering weights that Unhead applies automatically. source
  • Use
    textContent
    instead of
    innerHTML
    for inline scripts and styles —
    textContent
    escapes HTML characters, preventing injection. Only use
    innerHTML
    when HTML entities are required, and sanitize the content yourself (e.g. with DOMPurify). For user-generated inline content, prefer
    useHeadSafe()
    which restricts scripts to
    type="application/json"
    only. source
  • Register
    TemplateParamsPlugin
    and define global
    templateParams
    (e.g.
    siteName
    ,
    separator
    ) once in your head instance setup rather than repeating them per page. These params work across all head tags — including
    og:title
    and
    meta
    descriptions — not just
    titleTemplate
    . Set
    %separator
    to a smart separator like
    ·
    or
    ; it auto-removes when adjacent to empty content. source
  • Use
    InferSeoMetaPlugin
    to automatically derive
    og:title
    and
    og:description
    from existing
    title
    and
    description
    tags, eliminating manual duplication. Configure
    ogTitle
    with a transform function to strip the site name suffix from Open Graph titles (e.g. removing
    "| My Site"
    that
    titleTemplate
    appends). source
  • 在Vue组件中始终使用
    @unhead/vue
    导出的
    injectHead()
    ,而非
    unhead
    导出的
    getActiveHead()
    injectHead()
    会绑定到Vue组件上下文(在
    onServerPrefetch
    中可见),而
    getActiveHead()
    返回的是跨请求共享的实例,会在SSR场景下出错。维护者已确认这是Vue场景下的正确用法。来源
  • 避免在watcher中调用
    useHead()
    :每次调用都会创建新条目而非更新现有条目,会导致条目重复。正确做法是在setup阶段单次调用
    useHead()
    ,直接传入响应式ref或者计算属性,这样更新会自动生效。来源
  • 如果必须在异步操作后调用
    useHead()
    (例如在
    onMounted
    内部),请在setup阶段通过
    injectHead()
    提前获取head实例,然后在第二个参数中传入
    { head }
    :Vue的inject上下文会在
    await
    后丢失。大多数场景下更推荐使用响应式状态模式:在setup阶段定义
    useHead()
    并传入计算属性,异步更新对应的ref即可。来源
  • 当head的输入内容来自用户提供或第三方来源时,请使用
    useHeadSafe()
    而非
    useHead()
    :它会强制执行属性白名单,剥离script标签和事件处理器,无需手动清理即可防范XSS。来源
  • Vue应用请在Vite配置中添加
    @unhead/addons/vite
    导出的
    UnheadVite()
    插件:它会从客户端构建产物中tree-shake掉仅服务端使用的组合式API,并且将
    useSeoMeta()
    调用转换为原生的
    useHead()
    调用,可减少约3kb体积。Nuxt会自动配置该插件,独立Vue应用需要主动引入。来源
  • 对于不需要客户端响应性的静态SEO元数据(Open Graph图片、robots、schema.org),请给
    useHead()
    传入
    { mode: 'server' }
    :这会将对应标签从客户端包中完全剔除。同理,对于分析脚本可以传入
    { mode: 'client' }
    ,避免它们出现在SSR输出中。注意:
    titleTemplate
    需要在两个环境中都配置,避免标题闪烁。来源
  • 非关键脚本(分析、聊天组件)请使用
    tagPosition: 'bodyClose'
    而非放在head中:这可以避免阻塞渲染,提升页面加载性能。请使用
    tagPriority: 'critical' | 'high' | 'low'
    别名而非原始数字优先级,以保留Unhead自动应用的Capo.js排序权重。来源
  • 内联脚本和样式请使用
    textContent
    而非
    innerHTML
    textContent
    会转义HTML字符,防范注入攻击。仅当需要HTML实体时才使用
    innerHTML
    ,并且请自行清理内容(例如使用DOMPurify)。对于用户生成的内联内容,更推荐使用
    useHeadSafe()
    ,它仅允许
    type="application/json"
    类型的脚本。来源
  • 请在head实例初始化阶段注册
    TemplateParamsPlugin
    并一次性定义全局
    templateParams
    (例如
    siteName
    separator
    ),不要在每个页面重复定义。这些参数对所有head标签都生效——包括
    og:title
    和meta描述,不只是
    titleTemplate
    。可以将
    %separator
    设置为
    ·
    这类智能分隔符,相邻内容为空时会自动移除。来源
  • 使用
    InferSeoMetaPlugin
    可以自动从已有的
    title
    description
    标签生成
    og:title
    og:description
    ,无需手动重复配置。可以给
    ogTitle
    配置转换函数,去除Open Graph标题中的站点名称后缀(例如移除
    titleTemplate
    追加的
    "| My Site"
    )。来源