前几天客户要我去做一个功能,大意是单页应用里每切换一个页面都要变换标题,并且标题是根据内容而定的[1]。
静态的标题挺好搞的。一个典型的实现是在路由配置表上的 meta 对象中写入标题,然后在导航守卫里获取它并更改 document.title
12345678910111213141516171819202122232425262728293031323334import VueRouter from 'vue-router'// ...const router = new VueRouter({ routes: [{ path: '/', name: 'HomePage', component: HomePage, }, { path: '/article/:article_id', name: 'Article', meta: { title: '文章页' }, component: ArticlePage, }]})// 默认标题const default_title = '次世代博客网站'router.beforeHooks.push((to, from, next) => { const nearestMatched = to.matched.slice().reverse().find(r => r.meta && ((r.meta !== undefined) && r.meta)) if (nearestMatched) { const {title} = nearestMatched.meta if (typeof(title) === 'string') { // 有设置 title 的话就设置 document.title = title } else { // 没有设置 title 的话就设置默认的标题 document.title = default_title } }})
但麻烦的就在于,标题的设置需要动态性。设定一两个页面的动态标题还挺容易的,但要做通用的就麻烦点了。我想到的是用插件的方式:
123456789101112131415161718192021222324252627// async-titleconst setTitle = function (title) { document.title = title}export default { 'install' (Vue) { Vue.mixin({ mounted () { const { $AsyncTitle } = this.$options if ($AsyncTitle) { let set = setTitle $AsyncTitle.call(this, data, title => { set(title) set = () => { console.error('$AsyncTitle set handle 不能重复调用, 你设置的值是', title) } }) } }, }) },}
在 vue 文件中这样调用:
123456789101112131415161718192021222324252627282930<template> <article class="article-page"> <h1 class="title">{{ title }}</h1> <div class="content" v-html="content"></div> </article></template><script> import api from '@/api' export default { data: () => ({ title: '', content: '', }), $AsyncTitle (set) { const { article_id } = this.$route.params api.getArticle(article_id).then(article => { Object.assign(this, { title: article.title, content: article.content, }) set(article.title) // 设定标题 }).catch(err => { console.error(err) set(`文章加载失败`) alert(`错误:${err.message}`) }) }, }</script>
动静并存
利用这个插件,也可以做成静态的标题,只要$AsyncTitle
内同步执行 set
函数就可以了。或者麻烦点,改造下导航守卫:
1234567891011121314151617// 默认标题const default_title = ' 次世代博客网站 'router.beforeHooks.push((to, from, next) => { const nearestMatched = to.matched.slice().reverse().find(r => r.meta && ((r.meta !== undefined) && r.meta)) if (nearestMatched) { const {title} = nearestMatched.meta if (typeof(title) === 'string') { // 有设置 title 的话就设置 document.title = title } else if (title !== null) { // meta 里的 title 若设置为 null 则不更改标题 // 没有设置 title 的话就设置默认的标题 document.title = default_title } }})
然后在需要动态控制的路由配置表上写下:
1234567{ path: '/xxx', meta: { title: null }, component: TheComponent,}
好像也没什么了不起的。。。
也就是动态的,比如打开一篇博客文章,标题改为文章的标题 ↩︎