Head 管理

类似于资源注入,Head 管理遵循相同的理念:我们可以在组件的生命周期中,将数据动态地追加到渲染上下文 (render context),然后在模板中的占位符替换为这些数据。

在 2.3.2+ 的版本,你可以通过 this.$ssrContext 来直接访问组件中的服务器端渲染上下文(SSR context)。在旧版本中,你必须通过将其传递给 createApp() 并将其暴露于根实例的 $options 上,才能手动注入服务器端渲染上下文(SSR context) - 然后子组件可以通过 this.$root.$options.ssrContext 来访问它。

我们可以编写一个简单的 mixin 来完成标题管理:

  1. // title-mixin.js
  2. function getTitle (vm) {
  3. // 组件可以提供一个 `title` 选项
  4. // 此选项可以是一个字符串或函数
  5. const { title } = vm.$options
  6. if (title) {
  7. return typeof title === 'function'
  8. ? title.call(vm)
  9. : title
  10. }
  11. }
  12. const serverTitleMixin = {
  13. created () {
  14. const title = getTitle(this)
  15. if (title) {
  16. this.$ssrContext.title = title
  17. }
  18. }
  19. }
  20. const clientTitleMixin = {
  21. mounted () {
  22. const title = getTitle(this)
  23. if (title) {
  24. document.title = title
  25. }
  26. }
  27. }
  28. // 可以通过 `webpack.DefinePlugin` 注入 `VUE_ENV`
  29. export default process.env.VUE_ENV === 'server'
  30. ? serverTitleMixin
  31. : clientTitleMixin

现在,路由组件可以利用以上 mixin,来控制文档标题 (document title):

  1. // Item.vue
  2. export default {
  3. mixins: [titleMixin],
  4. title () {
  5. return this.item.title
  6. },
  7. asyncData ({ store, route }) {
  8. return store.dispatch('fetchItem', route.params.id)
  9. },
  10. computed: {
  11. item () {
  12. return this.$store.state.items[this.$route.params.id]
  13. }
  14. }
  15. }

然后模板中的内容将会传递给 bundle renderer:

  1. <html>
  2. <head>
  3. <title>{{ title }}</title>
  4. </head>
  5. <body>
  6. ...
  7. </body>
  8. </html>

注意:

  • 使用双花括号(double-mustache)进行 HTML 转义插值(HTML-escaped interpolation),以避免 XSS 攻击。

  • 你应该在创建 context 对象时提供一个默认标题,以防在渲染过程中组件没有设置标题。


使用相同的策略,你可以轻松地将此 mixin 扩展为通用的头部管理工具 (generic head management utility)。