Code前端首页关于Code前端联系我们

一、先搞懂浏览器title和Vue Router的关系

terry 13小时前 阅读数 15 #Vue

p>做Vue项目时,很多同学都会碰到「不同页面要显示不同标题」的需求,比如首页显示「首页 - 某某平台」,文章详情页显示「文章标题 - 某某平台」,那用Vue Router怎么实现页面title的灵活设置呢?这篇内容从基础原理到实战技巧,一步步讲透~

浏览器顶部的title,对应HTML里的<title>标签,本质是document.title这个API控制的,但Vue项目是单页应用(SPA),整个应用只有一个HTML文件,路由切换时页面不会刷新,所以默认情况下,所有页面的title都是index.html里写死的内容。

Vue Router的作用是管理路由切换,但它本身不直接处理title,得我们自己结合前端逻辑,在路由变化时主动修改document.title,理解这层关系后,接下来看具体实现方法~

最基础的实现:用全局路由守卫+路由元信息

最常见的固定标题场景(比如首页、关于页),可以用「全局路由守卫 + 路由元信息(meta)」实现。

步骤1:配置路由的meta字段

在路由配置文件(比如router/index.js)里,给每个路由添加meta属性,存对应的title:

const routes = [
  {
    path: '/home',
    component: Home,
    meta: { title: '首页 - 某某平台' } // 这里存页面标题
  },
  {
    path: '/about',
    component: About,
    meta: { title: '关于我们 - 某某平台' }
  }
]

meta是Vue Router提供的「路由元信息」功能,用来存自定义数据,title只是其中一种用法,还能存权限、面包屑等信息。

步骤2:全局路由守卫里改title

router.beforeEach(全局前置守卫),每次路由切换前,从to.meta里拿title,设置给document.title

const router = createRouter({
  history: createWebHistory(),
  routes
})
<p>router.beforeEach((to, from, next) => {
// to是目标路由,from是当前路由,next是放行函数
document.title = to.meta.title || '默认标题' // 没有title就显示默认值
next() // 必须调用next()放行路由
})

这样,每次切换路由时,title会自动变成目标路由meta里的内容,简单又高效~

但如果是(比如文章详情页,title要显示文章标题),这种固定配置就不够用了,得换思路~

场景:数据驱动的title怎么搞?

比如文章详情页,URL是/article/123,title要显示「Vue Router标题设置 - 某某平台」(假设文章标题是接口返回的),这时候title是动态的,得结合「异步请求 + 路由守卫」处理。

方案1:组件内守卫(beforeRouteEnter)

在文章详情组件(Article.vue)里,用beforeRouteEnter守卫(组件进入前触发),请求数据后设置title:

export default {
  name: 'Article',
  beforeRouteEnter(to, from, next) {
    // 注意:这里不能用this,因为组件实例还没创建
    getArticleDetail(to.params.id).then(res => {
      // res.data.title是接口返回的文章标题
      document.title = res.data.title + ' - 某某平台'
      next(vm => {
        // 把数据传给组件实例vm,比如存到data里
        vm.article = res.data
      })
    }).catch(err => {
      console.error('请求失败', err)
      document.title = '文章详情 - 某某平台' // 失败时显示默认
      next()
    })
  }
}

这种方式的好处是数据和title强关联,请求成功后再设置title,避免显示错误内容,但缺点是每个需要动态title的组件都要写一遍逻辑,有点重复。

方案2:路由元信息用函数 + 全局守卫

的逻辑抽到路由配置的meta里,用函数返回动态title,再结合全局守卫统一处理:

步骤1:路由配置meta.title为函数

{
  path: '/article/:id',
  component: Article,
  meta: {  (route) => {
      // route是当前路由对象,能拿到params、query等
      return getArticleTitle(route.params.id) + ' - 某某平台'
    }
  }
}

这里的getArticleTitle是封装的工具函数,内部发请求或者从缓存拿文章标题。

步骤2:全局守卫里判断函数并执行

修改全局守卫的逻辑:

router.beforeEach((to, from, next) => {
  let title = '默认标题'
  // 判断meta.title是函数还是字符串
  if (typeof to.meta.title === 'function') {= to.meta.title(to) // 执行函数,传入route对象
  } else {= to.meta.title || title
  }
  document.title = title
  next()
})

这种方式的好处是集中管理title逻辑,不用每个组件写重复代码,但要注意:如果getArticleTitle是异步请求,函数得返回Promise,全局守卫里要处理异步(比如用async/await),否则title会设置不及时。

举个异步的例子,把getArticleTitle改成返回Promise:

meta: {  async (route) => {
    const res = await getArticleDetail(route.params.id)
    return res.data.title + ' - 某某平台'
  }
}

然后全局守卫用async/await:

router.beforeEach(async (to, from, next) => {
  let title = '默认标题'
  if (typeof to.meta.title === 'function') {= await to.meta.title(to) // 等待异步结果
  } else {= to.meta.title || title
  }
  document.title = title
  next()
})

这样就能确保异步请求完成后,title才会更新~

多语言项目:title跟着语言切换

如果项目做了国际化(比如用vue-i18n),title也需要根据语言切换(比如中文显示「首页」,英文显示「Home」),这时候可以结合vue-i18n的t函数。

步骤1:路由meta存i18n的key

在路由配置里,meta.title存i18n的翻译key:

const routes = [
  {
    path: '/home',
    component: Home,
    meta: { title: 'route.home.title' } // 对应i18n里的key
  }
]

步骤2:全局守卫里用t函数翻译

在全局守卫中,引入vue-i18n的t函数,把meta.title对应的key翻译成当前语言:

import { useI18n } from 'vue-i18n'
<p>router.beforeEach((to, from, next) => {
const { t } = useI18n()
const titleKey = to.meta.title
document.title = t(titleKey) || 'Default Title' // 没有翻译则显示默认
next()
})

这样,当切换语言(比如从中文切到英文)时,只要路由切换或者页面刷新,title就会自动变成对应语言的内容,如果想更实时(语言切换时不刷新页面也更新title),可以在语言切换的逻辑里,主动触发路由守卫或者直接修改document.title~

实战踩坑:这些细节要注意

前面讲了各种场景的实现,但实际开发中容易碰到一些「玄学问题」,这里总结几个常见坑和解决方案~

坑1:首屏title不显示或显示默认值

原因:Vue应用初始化时,index.html里的title是静态的,而路由守卫是在路由切换时才触发,所以首屏(打开项目时的第一个页面)的title可能还是index.html里的内容

解决方法:在入口文件(main.js)里,先设置一次默认title,或者在App.vue的onMounted里设置:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
<p>const app = createApp(App)
app.use(router)
app.mount('#app')
处理:获取初始路由的meta.title
const initialRoute = router.currentRoute.value
document.title = initialRoute.meta.title || '默认标题'

坑2:动态title更新延迟(比如文章详情页title还是旧的)

原因:异步请求还没返回,title已经被设置了,导致显示旧数据或默认值。

解决方法:等数据请求成功后再放行路由,或者在请求成功后再设置title,比如用beforeRouteEnter时,把next放在then里:

beforeRouteEnter(to, from, next) {
  getArticleDetail(to.params.id).then(res => {
    document.title = res.data.title + ' - 某某平台'
    next(vm => {
      vm.article = res.data
    })
  }).catch(() => {
    document.title = '文章详情 - 某某平台'
    next()
  })
}

这样确保title是最新的~

坑3:微信/部分手机浏览器title不更新

原因:某些移动端浏览器(比如微信内置浏览器)对document.title的修改有延迟,因为它们的WebView内核做了缓存优化。

解决方法:强制触发页面重绘,比如动态创建一个空的iframe,加载后再移除:

function setDocumentTitle(title) {
  document.title = title
  // 判断是否是微信环境(可以用UA判断)
  if (navigator.userAgent.includes('MicroMessenger')) {
    const iframe = document.createElement('iframe')
    iframe.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
    iframe.style.display = 'none'
    iframe.onload = () => {
      setTimeout(() => {
        iframe.remove()
      }, 0)
    }
    document.body.appendChild(iframe)
  }
}

然后在所有设置title的地方,用这个函数代替document.title = xxx

router.beforeEach((to, from, next) => {
  const title = to.meta.title || '默认标题'
  setDocumentTitle(title)
  next()
})

这样就能解决微信里title不更新的问题~

进阶:让页面组件标题和浏览器title同步

很多项目里,页面顶部的标题(比如Element Plus的PageHeader)也需要和浏览器title保持一致,这时候可以用「provide/inject + 计算属性」或者「props传递」来实现。

方案:App.vue里统一管理title,传给子组件

在App.vue中,用useRoute获取当前路由,计算出title,然后通过v-slot传给路由组件:

<template>
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component 
        :is="Component" 
        :page-title="currentTitle" 
      />
    </keep-alive>
  </router-view>
</template>
<p><script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'</p>
<p>const route = useRoute()
// 复用之前的title获取逻辑(支持函数、i18n等)
const currentTitle = computed(() => {
let title = '默认标题'
if (typeof route.meta.title === 'function') {= route.meta.title(route)
} else {= route.meta.title || title
}
// 如果有多语言,这里还要结合t函数,= t(title)
return title
})
</script>

然后在子组件(比如Article.vue)里,接收page-title并显示:

<template>
  <el-page-header :content="pageTitle" />
  <!-- 其他内容 -->
</template>
<p><script setup>
const props = defineProps(['pageTitle'])
</script>

这样,浏览器title和页面组件内的标题就完全同步了,用户体验更统一~

不同场景的title方案对照(表格+

把不同场景的实现方式整理成表格,方便快速参考:

场景 实现方式 核心逻辑
(如首页) 路由meta + 全局守卫 路由配置meta.title,全局守卫统一设置document.title
(如详情页) 组件内守卫/路由元信息函数 + 异步请求 数据请求成功后设置title,或用async/await处理异步
多语言项目 vue-i18n t函数 + 全局守卫 路由meta存i18n key,全局守卫用t函数翻译
特殊浏览器兼容 封装title函数 + iframe重绘 处理微信等浏览器title更新延迟问题
同步 provide/inject或props + 计算属性 让页面组件标题和浏览器title保持一致

Vue Router设置title的核心是「在路由变化时主动修改document.title」,结合路由元信息、全局守卫、异步请求等手段,覆盖固定、动态、多语言等场景,实际开发中,还要注意首屏、异步、浏览器兼容这些细节,才能让title稳定又灵活~

如果对你有帮助,记得收藏备用呀~碰到具体问题,微信里title还是不更新”“动态title闪烁”这些,评论区留言,咱们一起解决~

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门