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.
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
).
NOTE: Any values specified in children components get automatically merged in
(overriding existing entries (in a "deep merge" fashion)).
<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]
})
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)