聊聊 Vue 中 title 的动态修改

因为以前的 Vue 项目打包成果物一直是嵌入集成平台中,因此一直没有关注过项目的 title。直到最近,忽然有个需求,要求点击按钮在集成平台外新开一个页面,此时我才发现,原来个人项目的 title 一直是万年不变的 vue-project。理所应当的,这个问题被测试爸爸提了一个大大的缺陷。vue

犯了错的我赶忙解决这个问题,可是通过一段时间的摸索,我却发现,这一个小小的问题,却有着不少不一样的解法。web

首先,毫无疑问的是,咱们应该使用 document.title 方法经过 DOM 操做来修改 title 的值。此时,距离解决问题还差两步:浏览器

  1. 如何传递 title?
  2. 什么时候修改 title?

ps:不少人提到过在微信或者一部分 IOS webview (下文一概以微信指代)中没法经过 document.title 方法修改 title 的值,这个问题的解决方案在文末的彩蛋中会说起。微信

title 的传递

接下来进入第一个重点:title 的传递。根据传递 title 值的方式,分为两种方案:app

  1. 全局变量传递
  2. 路由传递

何为全局变量传递?全局变量传递指的是全部页面维护同一个全局变量,切换页面对其从新赋值,最多见的方法是使用 Vuex,固然,若是你要使用 this.$root 甚至丧心病狂地想要使用 provide/inject 同样能够达到相似的效果。iphone

路由传递的方法就比较容易理解了,即经过路由跳转传参传递 title 的值。因为业务逻辑中自己就包含大量的路由传参,为了解耦方便后续维护,推荐将 title 的值经过路由配置中的 meta 进行传递。ide

以后,经过访问当前路由对象($route)的 meta 属性便可获取到 title 值。函数

// router.js
const routes = [
  {
    path: '/',
    ...
    meta: {
      title: '首页'
    }
  }, {
    path: '/A',
    meta: {
      title: 'A模块'
    }
  }
]
复制代码
// 业务模块,获取 title
...
beforeCreate () {
  console.log(this.$route.meta)
}
...
复制代码

经过上面的两种方法,能够顺利传递 title 的值。测试

title 的修改时机

完成了 title 值的传递,接下来咱们谈谈什么时候该修改 title。ui

想到这个问题,大多数人第一个想到的应该就是在生命周期钩子中修改 title。

生命周期钩子

通常状况下,咱们在 mounted 生命周期钩子中进行初始化请求,因此惯性思惟之下,我在 mounted 中进行了 title 的修改。

// 业务代码
mounted () {
  document.title = this.$route.meta.title
}
复制代码

结果,效果不佳,标签页的 title 延迟 1 秒以上才成功修改。经过这个延迟能够发现,显然,咱们的代码执行地太晚了!

回忆了一下 Vue 源码中初始化相关的代码细节,咱们能够发现,咱们甚至在 beforeCreate 钩子中就能够进行 title 的修改。

改动后的代码以下:

// 业务代码
beforeCreate () {
  document.title = this.$route.meta.title
}
复制代码

能够发现,修改后的代码效果明显好了许多,延迟感虽然还有,可是已经不太明显。

路由守卫

比起在生命周期钩子中修改 title 值,在路由跳转时利用路由守卫完成 title 的修改,岂不美哉?毕竟路由跳转发生在生命周期函数执行以前,使用路由守卫修改 title 值能够明显下降 title 修改的延时。

// router.js
router.beforeEach((to, from, next) => {
  document.title = to.meta.title
  next()
})
复制代码

此时,咱们基本完美完成了功能需求,可是,仍是有一点小瑕疵——若是 meta 中没有定义 title 值,此时 title 值就变成了 undefined,扑街~

因此,咱们须要设置默认的 title 值(通常能够是该项目的名称),做为 title 值不存在时的备胎。修改后的代码以下:

// router.js
const defaultTitle = '默认 title'
router.beforeEach((to, from, next) => {
  document.title = to.meta.title ? to.meta.title : defaultTitle
  next()
})
复制代码

到这里为止,咱们完美实现了需求,而且实现了该功能与业务代码的解耦。

彩蛋1:使用 vue-meta 管理 title

vue-meta 插件在安装时,像 Vuex 相似,注入了全局状态——metaInfo,你能够经过定义 metaInfo 对象中的 title 属性,实现 title 的动态修改。

彩蛋2:vue-wechat-title 源码解析

在搜索相关资料的时候,vue-wechat-title 这个包的出现频率出乎意料的高,这个包主要解决了前面提到的那个问题:在微信中没法经过 document.title 方法修改 title 的值。固然,这个兼容性问题都能解决了,正常状况下的 title 修改固然不在话下。

咱们先来看看 vue-wechat-title 的源码:

// vue-wechat-title 源码
(function () {
  // 插件安装钩子
  function install (Vue) {
    var setWechatTitle = function (title, img) {
      if (title === undefined || window.document.title === title) {
        return
      }
      // 修改 title
      document.title = title
      var mobile = navigator.userAgent.toLowerCase()
      // 兼容性判断
      if (/iphone|ipad|ipod/.test(mobile)) {
        // 建立空的 iframe,触发 onload 事件
        var iframe = document.createElement('iframe')
        iframe.style.display = 'none'
        // 替换成站标favicon路径或者任意存在的较小的图片便可
        iframe.setAttribute('src', img || 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7')
        // onload 回调函数
        var iframeCallback = function () {
          setTimeout(function () {
            // 卸磨杀驴
            iframe.removeEventListener('load', iframeCallback)
            document.body.removeChild(iframe)
          }, 0)
        }
        // 定义事件
        iframe.addEventListener('load', iframeCallback)
        document.body.appendChild(iframe)
      }
    }
    // 定义全局指令,
    Vue.directive('wechat-title', function (el, binding) {
      // update 钩子,调用 title 修改函数
      setWechatTitle(binding.value, el.getAttribute('img-set') || null)
    })
  }

  if (typeof exports === 'object') {
    module.exports = install
  } else if (typeof define === 'function' && define.amd) {
    define([], function () {
      return install
    })
  } else if (window.Vue) {
    Vue.use(install)
  }
})()
复制代码

因为微信浏览器只在onload 事件中经过 title 的值初始化标题,然后续的 title 修改,没法触发标题的修改。既然 onload 事件可以经过 title 修改标题,那么我建立一个空的 iframe 触发 onload 事件修改了标题后就移除它。这种方式根据 title 修改了标题,而且没有对页面形成额外的影响。

众所周知,vue-wechat-title 经过 v-wechat-title 指令来触发 title 的动态修改,每当指令的值修改后,触发 update 钩子中的回调函数——setWechatTitle。该函数经过前面提到的兼容性处理,实现了document.title 对标题的修改。

相关文章
相关标签/搜索