Skip to content

shyiko/vue-document

Repository files navigation

vue-document npm

A document manager for Vue.js. Think react-helmet but for Vue.js.

Features:

  • lightweight (1kb before gzipping);
  • user-defined injection logic;
  • server-side rendering support;
  • reactive.

Installation

Get it directly from NPM:

npm i --save-dev vue-document

or through unpkg:

<script src="https://unpkg.com/vue-document></script>
(in this case vue-document will be available as window.VueDocument).

Usage

NOTE: Any values specified in children components get automatically merged in
(overriding existing entries (in a "deep merge" fashion)).

Client-Side

<template>
  <div id="app">{{ message }}</div>
</template>

<script>
  import Vue from 'vue'
  import VueDocument from 'vue-document'
  
  Vue.use(VueDocument, {
    injector (document) {
      // "this" is referencing Vue component which caused "document" metadata to change
      const metadata = this.$root.$document
      document.title = metadata.head.title
      document.querySelector('meta[name="description"]').content = 
        metadata.meta.find((meta) => meta.name === 'description').content
    }
  })
  
  export default {
    document: {
      head: {
        title: 'Custom Title',
        meta: [
          {name: 'description', content: 'Custom Description'}
        ]
      }
    },
    el: '#app',
    data: {
      message: 'Hello Vue!'
    }
  }
</script>

In the example above, instead of writing injector ourselves we could use vue-document-injector-title.js and vue-document-injector-meta.js (later is used for custom <meta/> injection, not just description). injector handler could then be simplified to

  import VueDocument, {titleInjector, metaInjector} from 'vue-document'
  
  Vue.use(VueDocument, {
    injector: [titleInjector, metaInjector]
  })

Live Demo @ JS Bin

Server-Side

The example below is not using bundleRenderer just to keep things simple.
There is nothing stopping you from doing that.

const Vue = require('vue')
const VueDocument = require('vue-document')

Vue.use(VueDocument)

const vm = new Vue({
  document: {
    head: {
      title: 'Custom Title',
      meta: [
        {name: 'description', content: 'Custom Description'}
      ]
    }
  },
  render (h) {
    return h('div', {attrs: {id: 'app'}}, this.message)
  }, 
  el: '#app',
  data: {
    message: 'Hello Vue!'
  }
})

const renderer = require('vue-server-renderer').createRenderer()
renderer.renderToString(vm, (err, html) => {
  if (err) {
    throw err
  }
  const metadata = vm.$document
  console.log(
    `
      <!doctype html>
      <title>${metadata.head.title}</title>
      <meta name="description" content="${
        metadata.meta.find((meta) => meta.name === 'description').content
      }">
      ${html}
    `
  )   
})

Another option is to inject vm.$document into the existing html (which can be helpful if page is generated by html-webpack-plugin or something similar):

const Vue = require('vue')
const VueDocument = require('vue-document')
const {titleInjector, metaInjector} = VueDocument

Vue.use(VueDocument, {
  injector: [titleInjector, metaInjector]
})

const window = require('domino')
  .createWindow('<!doctype html><title>*</title>')
const document = window.document

const vm = ...
vm.$documentForceUpdate(document)
 
console.log(document.documentElement.outerHTML)

License

MIT

About

A 1kb document manager for Vue.js

Resources

License

Stars

Watchers

Forks

Packages

No packages published