Расскажите о динамической модификации заголовка в Vue.

Vue.js

Поскольку упаковка предыдущего проекта Vue всегда была встроена в интеграционную платформу, я не обращал внимания на название проекта. До недавнего времени возникала внезапная потребность нажать кнопку, чтобы открыть новую страницу вне интегрированной платформы, а в это время я узнал, что название моего проекта не меняется уже десять тысяч лет.vue-project. Как и положено, этот вопрос был поставлен тестирующим папой как большой недостаток.

Если я ошибался, я быстро решал эту проблему, но после некоторого периода исследований я обнаружил, что у этой небольшой проблемы есть много разных решений.

Во-первых, нет сомнений в том, что мы должны использоватьdocument.titleМетод изменяет значение title посредством манипуляции с DOM. На данный момент от решения проблемы осталось два шага:

  1. Как сдать титул?
  2. Когда менять заголовок?

ps: Многие люди упоминали, что невозможно пройти в WeChat или некоторых веб-просмотрах IOS (далее WeChat)document.titleМетод модифицирует значение title Решение этой проблемы будет упомянуто в пасхалке в конце статьи.

Переход титула

Далее введите первый ключевой момент: передача титула. В зависимости от того, как передается значение title, есть два варианта:

  1. передача глобальной переменной
  2. маршрутизация

Зачем передавать глобальные переменные? Передача глобальной переменной означает, что все страницы поддерживают одну и ту же глобальную переменную и переключают страницы, чтобы переназначить ее.Наиболее распространенный метод — использование Vuex.Конечно, если вы хотите использоватьthis.$rootдаже безумно хочется использоватьprovide/injectАналогичного эффекта можно добиться.

Способ передачи маршрутизации проще для понимания, то есть значение title передается через параметры перехода маршрутизации. Так как сама бизнес-логика содержит большое количество параметров маршрутизации, для развязки и облегчения последующего обслуживания рекомендуется передавать значение title через мета в конфигурации маршрутизации.

После этого значение title можно получить, обратившись к метаатрибуту текущего объекта маршрута ($route).

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

Через два вышеупомянутых метода значение title может быть передано плавно.

Когда менять заголовок

Теперь, когда значение заголовка передано, давайте поговорим о том, когда пришло время изменить заголовок.

Размышляя над этим вопросом, большинство людей в первую очередь должны подумать об изменении заголовка в хуке жизненного цикла.

крючки жизненного цикла

Как правило, мыmountedЗапрос на инициализацию делается в хуке жизненного цикла, поэтому по инерционному мышлению я модифицировал заголовок в Mounted.

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

В результате эффект не очень хороший, и заголовок вкладки задерживается более чем на 1 секунду, прежде чем будет успешно изменен. По этой задержке видно, что, очевидно, наш код выполняется слишком поздно!

Вспоминая детали кода, связанные с инициализацией в исходном коде Vue, мы можем обнаружить, что мы дажеbeforeCreateНазвание можно изменить в хуке.

Модифицированный код выглядит следующим образом:

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

Можно обнаружить, что эффект модифицированного кода, очевидно, намного лучше, и, хотя ощущение задержки все еще есть, оно уже не очевидно.

охранник маршрута

По сравнению с изменением значения заголовка в хуке жизненного цикла, разве не прекрасно использовать защиту маршрутизации для завершения изменения заголовка при скачке маршрута? В конце концов, переход маршрутизации происходит до выполнения функции жизненного цикла.Использование функции защиты маршрутизации для изменения значения заголовка может значительно сократить задержку модификации заголовка.

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

На данный момент мы в основном полностью выполнили функциональные требования, но все еще есть небольшой недостаток - если значение 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 для управления заголовком

vue-metaКогда плагин установлен, как и Vuex, он вводит глобальное состояние —metaInfo, вы можете определитьmetaInfoАтрибут title в объекте позволяет динамически модифицировать заголовок.

Пасхальное яйцо 2: анализ исходного кода vue-wechat-title

При поиске нужной информации,vue-wechat-titleЧастота этого пакета неожиданно высока.Этот пакет в основном решает проблему, упомянутую выше: он не может пройти через WeChat.document.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)
  }
})()

Поскольку браузер WeChat доступен только вonloadЗаголовок инициализируется значением title в событии, и последующая модификация заголовка не может инициировать изменение заголовка. Поскольку событие onload может изменить заголовок через заголовок, я создаю пустой iframe и запускаю событие onload, чтобы изменить заголовок, а затем удалить его. Это изменяет заголовок на основе заголовка и не оказывает дополнительного влияния на страницу.

Как мы все знаем,vue-wechat-titleпройти черезv-wechat-titleинструкция для запуска динамической модификации заголовка, и каждый раз, когда значение инструкции изменяется, запускается функция обратного вызова в обработчике обновления ——setWechatTitle. Эта функция реализует упомянутую выше обработку совместимости,document.titleИзменения в заголовке.