聊聊Vue 中 title 的动态修改问题


Posted in Javascript onJune 11, 2019

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

犯了错的我赶紧解决这个问题,但是经过一段时间的摸索,我却发现,这一个小小的问题,却有着很多不同的解法。

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

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

ps:很多人提到过在微信或者一部分 IOS webview (下文一律以微信指代)中无法通过 document.title 方法修改 title 的值,这个问题的解决方案在文末的彩蛋中会提及。

title 的传递

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

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

何为全局变量传递?全局变量传递指的是所有页面维护同一个全局变量,切换页面对其重新赋值,最常见的方法是使用 Vuex,当然,如果你要使用 this.$root 甚至丧心病狂地想要使用 provide/inject 一样可以达到类似的效果。

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

之后,通过访问当前路由对象($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。

想到这个问题,大多数人第一个想到的应该就是在生命周期钩子中修改 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 对标题的修改。

总结

以上所述是小编给大家介绍的Vue 中 title 的动态修改问题,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
Prototype使用指南之array.js
Jan 10 Javascript
js 函数调用模式小结
Dec 26 Javascript
用JQUERY增删元素的代码
Feb 14 Javascript
jquery ajax例子返回值详解
Sep 11 Javascript
javascript 实现map集合
Apr 03 Javascript
JavaScript实现信用卡校验方法
Apr 07 Javascript
将JavaScript的jQuery库中表单转化为JSON对象的方法
Nov 17 Javascript
javascript实现无缝上下滚动特效
Dec 16 Javascript
jquery实现焦点轮播效果
Feb 23 Javascript
JavaScript数据结构之数组的表示方法示例
Apr 12 Javascript
angularJs自定义过滤器实现手机号信息隐藏的方法
Oct 08 Javascript
Layer UI表格列日期格式化及取消自动填充日期的实现方法
May 10 Javascript
vue+element模态框中新增模态框和删除功能
Jun 11 #Javascript
vue.js中导出Excel表格的案例分析
Jun 11 #Javascript
ES6 Proxy实现Vue的变化检测问题
Jun 11 #Javascript
webpack实践之DLLPlugin 和 DLLReferencePlugin的使用教程
Jun 10 #Javascript
vue2 中二级路由高亮问题及配置方法
Jun 10 #Javascript
JS中实现一个下载进度条及播放进度条的代码
Jun 10 #Javascript
vuex 中插件的编写案例解析
Jun 10 #Javascript
You might like
php中将图片gif,jpg或mysql longblob或blob字段值转换成16进制字符串
2011/08/23 PHP
PHP性能优化大全(php.ini)
2016/05/20 PHP
php+ajax登录跳转登录实现思路
2016/07/31 PHP
LNMP部署laravel以及xhprof安装使用教程
2017/09/14 PHP
[原创]用javascript实现检测指定目录是否存在的方法
2008/01/12 Javascript
jQuery powerFloat万能浮动层下拉层插件使用介绍
2010/12/27 Javascript
Javascript面向对象编程(三) 非构造函数的继承
2011/08/28 Javascript
javascript 二进制运算技巧解析
2012/11/27 Javascript
jQuery之排序组件的深入解析
2013/06/19 Javascript
JS取得绝对路径的实现代码
2015/01/16 Javascript
jQuery创建DOM元素实例解析
2015/01/19 Javascript
JS控制表单提交的方法
2015/07/09 Javascript
javascript实现确定和取消提示框效果
2015/07/10 Javascript
jQuery+HTML5加入购物车代码分享
2020/10/29 Javascript
js实现上一页下一页的效果【附代码】
2016/03/10 Javascript
JavaScript字符串对象
2017/01/14 Javascript
通过命令行创建vue项目的方法
2017/07/20 Javascript
ES6中的迭代器、Generator函数及Generator函数的异步操作方法
2019/05/12 Javascript
手把手教你 CKEDITOR 4 扩展插件制作
2019/06/18 Javascript
微信小程序实现倒计时功能
2020/11/19 Javascript
[01:20]DOTA2更新全新英雄 天涯墨客现已加入游戏
2018/08/25 DOTA
Python实现的手机号归属地相关信息查询功能示例
2017/06/08 Python
python3模块smtplib实现发送邮件功能
2018/05/22 Python
Python可迭代对象操作示例
2019/05/07 Python
python pandas 时间日期的处理实现
2019/07/30 Python
Jupyter Notebook的连接密码 token查询方式
2020/04/21 Python
selenium判断元素是否存在的两种方法小结
2020/12/07 Python
open_basedir restriction in effect. 原因与解决方法
2021/03/14 PHP
英国医生在线预约:Top Doctors
2019/10/30 全球购物
Linux的主要特性
2014/10/06 面试题
大二学生学习个人自我评价
2014/01/19 职场文书
廉洁自律承诺书范文
2015/04/28 职场文书
2016年十一促销广告语
2016/01/28 职场文书
Django项目如何获得SSL证书与配置HTTPS
2021/04/30 Python
CentOS 7安装mysql5.7使用XtraBackUp备份工具命令详解
2022/04/12 MySQL
Android Gradle 插件自定义Plugin实现注意事项
2022/06/16 Java/Android