From 0ce515f6b3e9bdc05aea506836fdd6a129ee85ea Mon Sep 17 00:00:00 2001 From: Declan de Wet Date: Mon, 31 Oct 2016 00:48:07 +0200 Subject: [PATCH] more familiar naming convention + begin documentation --- README.md | 231 +++++++++++++++++++++++++++++++++++++++++++++++++++++- index.js | 5 +- 2 files changed, 232 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5b3ac4c..0d0ad3b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,231 @@ # vue-meta -manage page meta info in Vue 2.0 server-rendered apps +Manage page meta info in Vue 2.0 server-rendered components. Supports streaming. No dependencies. + +> **Please note** that this project is still in very early alpha development and is *not* considered to be production ready. + +# Description +`vue-meta` is a [Vue 2.0](https://vuejs.org) plugin that allows you to manage your app's meta information, much like [`react-helmet`](https://github.com/nfl/react-helmet) does for React. However, instead of setting your data as props passed to a proprietary component, you simply export it as part of your component's data using the `metaInfo` property. + +These properties, when set on a deeply nested component, will cleverly overwrite their parent components' `metaInfo`, thereby enabling custom info for each top-level view as well as coupling meta info directly to deeply nested subcomponents for more maintainable code. + +# Install + +```sh +$ yarn add vue-meta +# or $ npm install vue-meta --save +``` + +# Usage + +### Step 1: Preparing the plugin +In order to use this plugin, you first need to pass it to `Vue.use` in a file that runs both on the server and on the client before your root instance is mounted. If you're using [`vue-router`](https://github.com/vuejs/vue-router), then your main `router.js` file is a good place: + +**router.js:** +```js +import Vue from 'vue' +import Router from 'vue-router' +import Meta from 'vue-meta' + +Vue.use(Router) +Vue.use(Meta) + +export default new Router({ + ... +}) +``` + +### Step 2: Exposing `$meta` to `bundleRenderer` + +You'll need to expose the results of the `$meta` method that `vue-meta` adds to the Vue instance to the bundle render context before you can begin injecting your meta information. You'll need to do this in your server entry file: + +**server-entry.js:** +```js +import app from './app' + +const router = app.$router +const store = app.$store +const meta = app.$meta() // here + +export default (context) => { + router.push(context.url) + return Promise.all( + router.getMatchedComponents().map( + (component) => component.preFetch + ? component.preFetch(store) + : component + ) + ) + .then(() => { + context.initialState = store.state + context.meta = meta // and here + return app + }) +} +``` + +### Step 3: Server-side rendering with `inject()` + +All that's left for you to do now before you can begin using `metaInfo` options in your components is to make sure they work on the server by `inject`-ing them. You have two methods at your disposal: + +#### `renderToString()` + +Considerably the easiest method to wrap your head around is if your Vue server markup is rendered out as a string: + +**server.js:** + +```js +... +app.get('*', (request, response) => { + const context = { url: request.url } + + renderer.renderToString(context, (error, html) => { + if (error) { + ... + } else { + const { initialState, meta } = context + const metaInfo = meta.inject() + + response.send(` + + + + ${metaInfo.title.toString()} + + + + ${html} + + + + + `) + } + }) +}) +... +``` + +#### `renderToStream()` + +A little more complex, but well worth it, is to instead stream your response. `vue-meta` supports streaming with no effort (on it's part :stuck_out_tongue_winking_eye:) thanks to Vue's clever `bundleRenderer` context injection: + +**server.js** +```js +app.get('*', (request, response) => { + const context = { url: request.url } + const renderStream = renderer.renderToStream(context) + let firstChunk = true + + response.write('') + + renderStream.on('data', (chunk) => { + if (firstChunk) { + const metaInfo = context.meta.inject() + + if (metaInfo.htmlAttrs) { + response.write(``) + } + + response.write('') + + if (metaInfo.title) { + response.write(metaInfo.title.toString()) + } + + response.write('') + + if (context.initialState) { + response.write( + `` + ) + } + + firstChunk = false + } + response.write(chunk) + }) + + renderStream.on('end', () => { + response.end(` + + + + `) + }) + + renderStream.on('error', (error) => { + response.status(500).end(`
${error.stack}
`) + }) +}) +``` + +### Step 4: Start defining `metaInfo` + +In any of your components, define a `metaInfo` property: + +**App.vue:** +```html + + + +``` + +**Home.vue** +```html + + + +``` + +**About.vue** +```html + + + +``` diff --git a/index.js b/index.js index 131c45f..2dfa3d3 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ // update the meta info & the DOM Vue.mixin({ mounted: function mounted () { - this.$root.$vueMeta().updateMetaInfo() + this.$root.$meta().updateMetaInfo() } }) @@ -33,7 +33,7 @@ * returns a cached manager API for use on the server * @return {Object} - manager (The programmatic API for this module) */ - Vue.prototype.$vueMeta = function $vueMeta () { + Vue.prototype.$meta = function $meta () { _manager.getMetaInfo = _manager.getMetaInfo || Vue.util.bind(getMetaInfo, this) _manager.updateMetaInfo = _manager.updateMetaInfo || updateMetaInfo _manager.inject = _manager.inject || inject @@ -109,7 +109,6 @@ function getMetaInfo () { var info = getMetaInfoDefinition(Vue, this) if (info.titleTemplate) { - info.titleChunk = info.title info.title = info.titleTemplate.replace('%s', info.title) } return info