mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-03 02:14:04 +03:00
chore: add docs site
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
module.exports = {
|
||||
locales: {
|
||||
'/': {
|
||||
lang: 'en-US',
|
||||
title: 'Vue Meta',
|
||||
description: 'Metadata manager for Vue.js'
|
||||
},
|
||||
},
|
||||
serviceWorker: true,
|
||||
theme: 'vue',
|
||||
themeConfig: {
|
||||
repo: 'nuxt/vue-meta',
|
||||
docsDir: 'docs',
|
||||
locales: {
|
||||
'/': {
|
||||
label: 'English',
|
||||
selectText: 'Languages',
|
||||
editLinkText: 'Edit this page on GitHub',
|
||||
nav: [{
|
||||
text: 'Guide',
|
||||
link: '/guide/'
|
||||
}, {
|
||||
text: 'API',
|
||||
link: '/api/'
|
||||
}, {
|
||||
text: 'Release Notes',
|
||||
link: 'https://github.com/nuxt/vue-meta/releases'
|
||||
}],
|
||||
sidebar: [
|
||||
'/installation.md',
|
||||
'/',
|
||||
{
|
||||
title: 'Usage',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/guide/',
|
||||
'/guide/ssr',
|
||||
'/guide/metainfo',
|
||||
'/guide/special',
|
||||
'/guide/caveats',
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'FAQ',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/faq/',
|
||||
'/faq/performance.md',
|
||||
'/faq/prevent-initial.md',
|
||||
'/faq/component-props.md',
|
||||
'/faq/async-action.md',
|
||||
]
|
||||
},
|
||||
/*{
|
||||
title: 'Advanced',
|
||||
collapsable: false,
|
||||
children: [
|
||||
'/guide/advanced/navigation-guards.md',
|
||||
'/guide/advanced/meta.md',
|
||||
'/guide/advanced/transitions.md',
|
||||
'/guide/advanced/data-fetching.md',
|
||||
'/guide/advanced/scroll-behavior.md',
|
||||
'/guide/advanced/lazy-loading.md'
|
||||
]
|
||||
}*/
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,14 @@
|
||||
<img src="logo.png" alt="vue-meta"/>
|
||||
|
||||
::: tip We need your help
|
||||
We are working on defining the RFC for Vue Meta v3.0. It will be a ground-breaking release built from the ground up.
|
||||
|
||||
We would like your help with this! Please visit the [Vue Meta v3.0 rfc](https://github.com/nuxt/rfcs/issues/19) and let us know your thoughts.
|
||||
:::
|
||||
|
||||
# Introduction
|
||||
Vue Meta is a [Vue.js](https://vuejs.org) plugin that allows you to manage your app's metadata, 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.
|
||||
|
||||
[Get started](/guide) or play with the [examples](https://github.com/nuxt/vue-meta/tree/master/examples)
|
||||
@@ -0,0 +1,470 @@
|
||||
---
|
||||
sidebar: auto
|
||||
---
|
||||
|
||||
# API Reference
|
||||
|
||||
## Plugin options
|
||||
|
||||
### keyName
|
||||
- type `string`
|
||||
- default `metaInfo`
|
||||
|
||||
The name of the component option that contains all the information that gets converted to the various meta tags & attributes for the page
|
||||
|
||||
### attribute
|
||||
- type `string`
|
||||
- default `data-vue-meta`
|
||||
|
||||
The name of the attribute vue-meta arguments on elements to know which it should manage and which it should ignore.
|
||||
|
||||
### ssrAttribute
|
||||
- type `string`
|
||||
- default `data-vue-meta-server-rendered`
|
||||
|
||||
The name of the attribute that is aded to the `html` tag to inform `vue-meta` that the server has already generated the meta tags for the initial render
|
||||
|
||||
See [How to prevent update on page load](/faq/prevent-initial)
|
||||
|
||||
### tagIDKeyName
|
||||
- type `string`
|
||||
- default `vmid`
|
||||
|
||||
The property that tells vue-meta to overwrite (instead of append) an item in a tag list.
|
||||
For example, if you have two `meta` tag list items that both have `vmid` of 'description',
|
||||
then vue-meta will overwrite the shallowest one with the deepest one.
|
||||
|
||||
### contentKeyName
|
||||
- type `string`
|
||||
- default `content`
|
||||
|
||||
The key name for the content-holding property
|
||||
|
||||
### metaTemplateKeyName
|
||||
- type `string`
|
||||
- default `template`
|
||||
|
||||
The key name for possible meta templates
|
||||
|
||||
### refreshOnceOnNavigation
|
||||
- type `boolean`
|
||||
- default `false`
|
||||
|
||||
When `true` then vue-meta will pause updates once page navigation starts and resumes updates when navigation finishes (resuming also triggers an update).
|
||||
This could both be a performance improvement as a possible fix for 'flickering' when you are e.g. replacing stylesheets
|
||||
|
||||
## Plugin methods
|
||||
|
||||
The `vue-meta` plugin injects a `$meta()` function in the Vue prototype which provides the following methods
|
||||
|
||||
:::tip Note
|
||||
`$meta()` is a function so we only need to insert it once in the `Vue.prototype`, but still use `this` to reference the component it was called from
|
||||
:::
|
||||
|
||||
### $meta().getOptions
|
||||
- returns [`pluginOptions`](/api/#plugin-options)
|
||||
|
||||
Could be used by third-party libraries who wish to interact with `vue-meta`
|
||||
|
||||
### $meta().refresh
|
||||
- returns [`metaInfo`](/api/#metaInfo-properties)
|
||||
|
||||
Updates the current meta info with new meta info.
|
||||
Useful when updating meta info as the result of an asynchronous action that resolves after the initial render takes place.
|
||||
|
||||
### $meta().inject
|
||||
- returns [`metaInfo`](/api/#metaInfo-properties)
|
||||
|
||||
:::tip SSR only
|
||||
`inject` is available in the server plugin only and is not available on the client
|
||||
:::
|
||||
|
||||
It returns a special `metaInfo` object where all keys have an object as value which contains a `text()` method for returning html code
|
||||
|
||||
See [Rendering with renderToString](/guide/ssr.html#simple-rendering-with-rendertostring) for an example
|
||||
|
||||
### $meta().pause
|
||||
- arguments:
|
||||
- refresh (type `boolean`, default `false`)
|
||||
- returns `resume()`
|
||||
|
||||
Pauses global metadata updates until either the returned resume method is called or [resume](/api/#meta-resume)
|
||||
|
||||
### $meta().resume
|
||||
- arguments:
|
||||
- refresh (type `boolean`, default `false`)
|
||||
- returns [`metaInfo`](/api/#metaInfo-properties) (optional)
|
||||
|
||||
Resumes metadata updates after they have been paused. If `refresh` is `true` it immediately initiates a metadata update by calling [refresh](/api/#meta-refresh)
|
||||
|
||||
## metaInfo properties
|
||||
|
||||
::: tip Note
|
||||
The documentation below uses `metaInfo` in the examples, please note that this keyName is [configurable](/api/#keyname) and could be different in your case
|
||||
:::
|
||||
|
||||
### title
|
||||
- type `string`
|
||||
|
||||
Maps to the inner-text value of the `<title>` element.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
title: 'Foo Bar'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<title>Foo Bar</title>
|
||||
```
|
||||
|
||||
### titleTemplate
|
||||
- type `string | Function`
|
||||
|
||||
The value of `title` will be injected into the `%s` placeholder in `titleTemplate` before being rendered. The original title will be available on `metaInfo.titleChunk`.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
title: 'Foo Bar',
|
||||
titleTemplate: '%s - Baz'
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<title>Foo Bar - Baz</title>
|
||||
```
|
||||
|
||||
The property can also be a function:
|
||||
|
||||
```js
|
||||
titleTemplate: (titleChunk) => {
|
||||
// If undefined or blank then we don't need the hyphen
|
||||
return titleChunk ? `${titleChunk} - Site Title` : 'Site Title';
|
||||
}
|
||||
```
|
||||
|
||||
### htmlAttrs
|
||||
### headAttrs
|
||||
### bodyAttrs
|
||||
- type `object`
|
||||
|
||||
Each **key:value** maps to the equivalent **attribute:value** of the `<body>` element.
|
||||
|
||||
Since `v2.0` value can also be an `Array<string>`
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
amp: true
|
||||
},
|
||||
bodyAttrs: {
|
||||
class: ['dark-mode', 'mobile']
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<html lang="en" amp>
|
||||
<body class="dark-mode mobile">Foo Bar</body>
|
||||
```
|
||||
|
||||
### base
|
||||
- type `object`
|
||||
|
||||
Maps to a newly-created `<base>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
base: { target: '_blank', href: '/' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<base target="_blank" href="/">
|
||||
```
|
||||
|
||||
### meta
|
||||
- type `collection`
|
||||
|
||||
Each item in the array maps to a newly-created `<meta>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
```
|
||||
|
||||
#### Content templates
|
||||
|
||||
Since `v1.5.0`, you can now set up meta templates that work similar to the titleTemplate:
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{
|
||||
'property': 'og:title',
|
||||
'content': 'Test title',
|
||||
'template': chunk => `${chunk} - My page`, //or as string template: '%s - My page',
|
||||
'vmid': 'og:title'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<meta charset="utf-8">
|
||||
<meta name="og:title" property="og:title" content="Test title - My page">
|
||||
```
|
||||
|
||||
### link
|
||||
- type `collection`
|
||||
|
||||
|
||||
Each item in the array maps to a newly-created `<link>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
link: [
|
||||
{ rel: 'stylesheet', href: '/css/index.css' },
|
||||
{ rel: 'favicon', href: 'favicon.ico' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<link rel="stylesheet" href="/css/index.css">
|
||||
<link rel="favicon" href="favicon.ico">
|
||||
```
|
||||
|
||||
### style
|
||||
- type `object`
|
||||
|
||||
Each item in the array maps to a newly-created `<style>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
style: [
|
||||
{ cssText: '.foo { color: red }', type: 'text/css' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<style type="text/css">.foo { color: red }</style>
|
||||
```
|
||||
|
||||
### script
|
||||
- type `collection`
|
||||
|
||||
Each item in the array maps to a newly-created `<script>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
script: [
|
||||
{ src: 'https://cdn.jsdelivr.net/npm/vue/dist/vue.js', async: true, defer: true }
|
||||
],
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js" async defer></script>
|
||||
```
|
||||
|
||||
#### Add json or other raw data
|
||||
::: warning
|
||||
You have to disable sanitizers so the content of `innerHTML` won't be escaped. Please see [__dangerouslyDisableSanitizersByTagID](/api/#dangerouslydisablesanitizersbytagid) for more info on related risks
|
||||
:::
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
script: [{
|
||||
vmid: 'ldjson-schema',
|
||||
innerHTML: '{ "@context": "http://schema.org" }',
|
||||
type: 'application/ld+json'
|
||||
}],
|
||||
__dangerouslyDisableSanitizersByTagID: {
|
||||
'ldjson-schema': ['innerHTML']
|
||||
},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<script type="application/ld+json">{ "@context": "http://schema.org" }</script>
|
||||
```
|
||||
#### Special attribute: `body: true`
|
||||
|
||||
If you e.g. wish to force delayed execution of a script or just want the script to be included in the `<body>` of the page, add `body: true`.
|
||||
Script tags with `body: true` are rendered just before `</body>`
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
script: [{
|
||||
innerHTML: 'console.log("I am in body");',
|
||||
type: 'text/javascript',
|
||||
body: true
|
||||
}]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### noscript
|
||||
- type `collection`
|
||||
|
||||
Each item in the array maps to a newly-created `<noscript>` element, where object properties map to attributes.
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
noscript: [
|
||||
{ innerHTML: 'This website requires JavaScript.' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<noscript>This website requires JavaScript.</noscript>
|
||||
```
|
||||
|
||||
### __dangerouslyDisableSanitizers
|
||||
- type `Array<string>`
|
||||
|
||||
::: danger
|
||||
If you need to disable sanitation, please always use [__dangerouslyDisableSanitizersByTagID](/api/#dangerouslydisablesanitizers) when possible
|
||||
|
||||
By disabling sanitization, you are opening potential vectors for attacks such as SQL injection & Cross-Site Scripting (XSS). Be very careful to not compromise your application.
|
||||
:::
|
||||
|
||||
By default, `vue-meta` sanitizes HTML entities in _every_ property. You can disable this behaviour on a per-property basis using `__dangerouslyDisableSantizers`. Just pass it a list of properties you want sanitization to be disabled on:
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
title: '<I will be sanitized>',
|
||||
meta: [{
|
||||
vmid: 'description',
|
||||
name: 'description',
|
||||
content: '& I will not be <sanitized>'
|
||||
}],
|
||||
__dangerouslyDisableSanitizers: ['meta']
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<title><I will be sanitized></title>
|
||||
<meta vmid="description" name="description" content="& I will not be <sanitized>">
|
||||
```
|
||||
|
||||
### __dangerouslyDisableSanitizersByTagID
|
||||
- type `object`
|
||||
|
||||
::: warning
|
||||
By disabling sanitization, you are opening potential vectors for attacks such as SQL injection & Cross-Site Scripting (XSS). Be very careful to not compromise your application.
|
||||
:::
|
||||
|
||||
Provides same functionality as `__dangerouslyDisableSanitizers` but you can specify which property for which `tagIDKeyName` sanitation should be disabled. It expects an object with the vmid's as key and an array with property keys as value:
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
title: '<I will be sanitized>',
|
||||
meta: [{
|
||||
vmid: 'description',
|
||||
name: 'still-&-sanitized',
|
||||
content: '& I will not be <sanitized>'
|
||||
}],
|
||||
__dangerouslyDisableSanitizersByTagID: {
|
||||
description: ['content']
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```html
|
||||
<title><I will be sanitized></title>
|
||||
<meta vmid="description" name="still-&-sanitized" content="& I will not be <sanitized>">
|
||||
```
|
||||
|
||||
### changed
|
||||
- type `Function`
|
||||
|
||||
A callback function which is called whenever the `metaInfo` updates / changes.
|
||||
|
||||
The callback receives the following arguments:
|
||||
- **newInfo**
|
||||
- type `object`<br/>
|
||||
The updated [`metaInfo`](/api/#metaInfo-properties) object
|
||||
- **addedTags**
|
||||
- type `Array<HTMLElement>`<br/>
|
||||
List of elements that were added
|
||||
- **removedTags**
|
||||
- type `Array<HTMLElement>`<br/>
|
||||
List of elements that were removed
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
changed (newInfo, addedTags, removedTags) {
|
||||
console.log('Meta info was updated!')
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### afterNavigation
|
||||
- type `Function`
|
||||
|
||||
A callback function which is called when `vue-meta` has updated the metadata after navigation occured.
|
||||
This can be used to track page views with the updated document title etc.
|
||||
|
||||
Adding a `afterNavigation` callback behaves the same as when [refreshOnceOnNavigation](/api/#refreshonceonnavigation) is `true`
|
||||
|
||||
The callback receives the following arguments:
|
||||
- **newInfo**
|
||||
- type `object`<br/>
|
||||
The updated [`metaInfo`](/api/#metaInfo-properties) object
|
||||
|
||||
```js
|
||||
{
|
||||
metaInfo: {
|
||||
afterNavigation(metaInfo) {
|
||||
trackPageView(document.title)
|
||||
// is the same as
|
||||
trackPageView(metaInfo.title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,76 @@
|
||||
# How is `metaInfo` resolved?
|
||||
|
||||
You can define a `metaInfo` property on any component in the tree. Child components that have `metaInfo` will recursively merge their `metaInfo` into the parent context, overwriting any duplicate properties. To better illustrate, consider this component heirarchy:
|
||||
|
||||
```html
|
||||
<parent>
|
||||
<child></child>
|
||||
</parent>
|
||||
```
|
||||
|
||||
If both `<parent>` _and_ `<child>` define a `title` property inside `metaInfo`, then the `title` that gets rendered will resolve to the `title` defined inside `<child>`.
|
||||
|
||||
## Concatenate metadata
|
||||
|
||||
When specifying an array in `metaInfo`, like in the below examples, the default behaviour is to simply concatenate the lists.
|
||||
|
||||
**Input:**
|
||||
```js
|
||||
// parent component
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'description', content: 'foo' }
|
||||
]
|
||||
}
|
||||
}
|
||||
// child component
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ name: 'description', content: 'bar' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```html
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="foo">
|
||||
<meta name="description" content="bar">
|
||||
```
|
||||
|
||||
## Unique metadata
|
||||
|
||||
This is not what we want, since the meta `description` needs to be unique for every page. If you want to change this behaviour such that `description` is instead replaced, then give it a `vmid`:
|
||||
|
||||
**Input:**
|
||||
```js
|
||||
// parent component
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ vmid: 'description', name: 'description', content: 'foo' }
|
||||
]
|
||||
}
|
||||
}
|
||||
// child component
|
||||
{
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: 'bar' }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Output:**
|
||||
```html
|
||||
<meta charset="utf-8">
|
||||
<meta vmid="description" name="description" content="bar">
|
||||
```
|
||||
|
||||
While solutions like `react-helmet` manage the occurrence order and merge behaviour for you automatically, it involves a lot more code and is therefore prone to failure in some edge-cases, whereas this method is _almost_ bulletproof because of its versatility; _at the expense of one tradeoff:_ these `vmid` properties will be rendered out in the final markup (`vue-meta` uses these client-side to prevent duplicating or overriding markup). If you are serving your content GZIP'ped, then the slight increase in HTTP payload size is negligible.
|
||||
@@ -0,0 +1,22 @@
|
||||
# How to use async data in metaInfo?
|
||||
|
||||
`vue-meta` will do this for you automatically when your component state changes.
|
||||
|
||||
Just make sure that you're using the function form of `metaInfo`:
|
||||
|
||||
```js
|
||||
{
|
||||
data () {
|
||||
return {
|
||||
title: 'Foo Bar Baz'
|
||||
}
|
||||
},
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.title
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Check out the [vuex-async](https://github.com/nuxt/vue-meta/tree/master/examples/vuex-async) example for a more detailed demonstration
|
||||
@@ -0,0 +1,55 @@
|
||||
# How to use component props or data
|
||||
|
||||
Easy. Instead of defining `metaInfo` as an object, define it as a function and access `this` as usual:
|
||||
|
||||
**Post.vue:**
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<h1>{{{ title }}}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'post',
|
||||
props: ['title'],
|
||||
data () {
|
||||
return {
|
||||
description: 'A blog post about some stuff'
|
||||
}
|
||||
},
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.title,
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: this.description }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**PostContainer.vue:**
|
||||
```html
|
||||
<template>
|
||||
<div>
|
||||
<post :title="title"></post>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Post from './Post.vue'
|
||||
|
||||
export default {
|
||||
name: 'post-container',
|
||||
components: { Post },
|
||||
data () {
|
||||
return {
|
||||
title: 'Example blog post'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
@@ -0,0 +1,16 @@
|
||||
# Any performance considarations?
|
||||
|
||||
Short answer, no
|
||||
|
||||
On the client, `vue-meta` batches DOM updates using [`requestAnimationFrame`](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). It needs to do this because it registers a Vue mixin that subscribes to the [`beforeMount`](https://vuejs.org/api/#beforeMount) lifecycle hook on all components in order to be notified that renders have occurred and data is ready. If `vue-meta` did not batch updates, the DOM meta info would be re-calculated and re-updated for every component on the page in quick-succession.
|
||||
|
||||
Thanks to batch updating, the update will only occurr once - even if the correct meta info has already been compiled by the server. If you don't want this behaviour, see below.
|
||||
|
||||
:::tip Improvements since v2.0
|
||||
Previous versions of vue-meta injected lifecycle hooks from the global mixin on all components on the page. Also when refreshing metadata it checked all components on the page
|
||||
|
||||
Since v2.0 runtime performance should be improved due to:
|
||||
- the global mixin injects just a `beforeCreate` lifecycle hook, other hooks are only added for components which define `metaInfo`
|
||||
- we track component branches with `vue-meta` components which means that when refreshing metadata we can skip branches without metaInfo
|
||||
|
||||
:::
|
||||
@@ -0,0 +1,12 @@
|
||||
# How to prevent update on page load
|
||||
|
||||
Add the `data-vue-meta-server-rendered` attribute to the `<html>` tag on the server-side:
|
||||
|
||||
```html
|
||||
<html data-vue-meta-server-rendered>
|
||||
...
|
||||
```
|
||||
|
||||
`vue-meta` will check for this attribute whenever it attempts to update the DOM - if it exists, `vue-meta` will just remove it and perform no updates. If it does not exist, `vue-meta` will perform updates as usual.
|
||||
|
||||
While this may seem verbose, it _is_ intentional. Having `vue-meta` handle this for you automatically would limit interoperability with other server-side programming languages. If you use PHP to power your server, for example, you might also have meta info handled on the server already and want to prevent this extraneous update.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Preparing the plugin
|
||||
:::tip Note
|
||||
This step is optional if you don't need SSR and `Vue` is available as a global variable. `vue-meta` will install itself in this case.
|
||||
:::
|
||||
|
||||
In order to use this plugin, you first need to pass it to `Vue.use` - if you're not rendering on the server-side, your JS entry file will suffice. If you are rendering on the server, then place it 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({
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
`vue-meta` allows a few custom options:
|
||||
|
||||
```js
|
||||
Vue.use(Meta, {
|
||||
keyName: 'metaInfo',
|
||||
attribute: 'data-vue-meta',
|
||||
ssrAttribute: 'data-vue-meta-server-rendered',
|
||||
tagIDKeyName: 'vmid',
|
||||
refreshOnceOnNavigation: true
|
||||
})
|
||||
```
|
||||
|
||||
See the [API](/api/#plugin-options) for a description of the available plugin options
|
||||
@@ -0,0 +1,39 @@
|
||||
# Caveats
|
||||
|
||||
## Reactive variables in template functions
|
||||
|
||||
Both [title](/api/#titletemplate) as [meta](/api/#content-templates) support using template function.
|
||||
Due to how Vue.js determines reactivity it is not possible to use reactive variables directly in template function
|
||||
|
||||
```js
|
||||
{
|
||||
// this wont work
|
||||
metaInfo() {
|
||||
return {
|
||||
titleTemplate: chunk => (
|
||||
this.locale === 'nl-NL'
|
||||
? `${chunk} - Welkom`
|
||||
: `${chunk} - Welcome`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You need to assign the reactive variable to a local variable for this to work:
|
||||
|
||||
```js
|
||||
{
|
||||
// this will work
|
||||
metaInfo() {
|
||||
const locale = this.locale
|
||||
return {
|
||||
titleTemplate: chunk => (
|
||||
locale === 'nl-NL'
|
||||
? `${chunk} - Welkom`
|
||||
: `${chunk} - Welcome`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
# Defining `metaInfo`
|
||||
|
||||
You can define a `[keyName]` property in any of your components, by default this is `metaInfo`.
|
||||
|
||||
See the [API](/api) for a list of recognised `metaInfo` properties
|
||||
|
||||
::: tip Note
|
||||
Altough we talk about the `metaInfo` variable on this page, please note that the keyName is [configurable](/api/#keyname) and could be different in your case. E.g. [Nuxt.js](https://nuxtjs.org/api/pages-head#the-head-method) uses `head` as keyName
|
||||
:::
|
||||
|
||||
**App.vue:**
|
||||
```html
|
||||
<template>
|
||||
<div id="app">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
metaInfo: {
|
||||
// if no subcomponents specify a metaInfo.title, this title will be used
|
||||
title: 'Default Title',
|
||||
// all titles will be injected into this template
|
||||
titleTemplate: '%s | My Awesome Webapp'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**Home.vue**
|
||||
```html
|
||||
<template>
|
||||
<div id="page">
|
||||
<h1>Home Page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Home',
|
||||
metaInfo: {
|
||||
title: 'My Awesome Webapp',
|
||||
// override the parent template and just use the above title only
|
||||
titleTemplate: null
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**About.vue**
|
||||
```html
|
||||
<template>
|
||||
<div id="page">
|
||||
<h1>About Page</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'About',
|
||||
metaInfo: {
|
||||
// title will be injected into parent titleTemplate
|
||||
title: 'About Us'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
@@ -0,0 +1,108 @@
|
||||
# Special cases
|
||||
|
||||
::: tip Read first
|
||||
Understanding [How is metaInfo resolved?](/faq/#concatenate-metadata) is probably a prerequisite for these cases
|
||||
:::
|
||||
|
||||
## Remove parent property by child
|
||||
|
||||
If a child returns `null` as content value then the parent metaInfo property with the same `vmid` will be ignored
|
||||
|
||||
:::tip Content value
|
||||
With content value we mean the following value of a `metaInfo` property:
|
||||
- the value of a key for `object` types as [`htmlAttrs`](/api/#htmlattrs)
|
||||
- the value of `[contentKeyName]` or `innerHTML` keys for `collection` types as [`meta`](/api/#meta)
|
||||
:::
|
||||
|
||||
The following might be a bit-farfetched, but its just an example
|
||||
```js
|
||||
// parent
|
||||
metaInfo: {
|
||||
style: [{
|
||||
vmid: 'page-load-overlay',
|
||||
innerHTML: `
|
||||
body div.loading {
|
||||
z-index: 999;
|
||||
background-color: #0f0f0f;
|
||||
opacity: 0.9;
|
||||
}
|
||||
`,
|
||||
}]
|
||||
}
|
||||
|
||||
// dynamically loaded child
|
||||
metaInfo() {
|
||||
const style = this.cssTexts
|
||||
return { style }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
this.cssTexts: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.cssTexts.push({
|
||||
vmid: 'page-load-overlay',
|
||||
innerHTML: null
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## Use child property conditionally
|
||||
|
||||
If you wish to use a child property conditionally and use the parents' property as default value, make sure the child returns `undefined` as content value
|
||||
|
||||
:::tip Content value
|
||||
With content value we mean the following value of a `metaInfo` property:
|
||||
- the value of a key for `object` types as [`htmlAttrs`](/api/#htmlattrs)
|
||||
- the value of `[contentKeyName]` or `innerHTML` keys for `collection` types as [`meta`](/api/#meta)
|
||||
:::
|
||||
|
||||
The below example will still show a description when the GET in the child fails
|
||||
```js
|
||||
// parent
|
||||
metaInfo: {
|
||||
meta: [{
|
||||
vmid: 'description',
|
||||
name: 'description',
|
||||
content: 'my standard description',
|
||||
}]
|
||||
}
|
||||
|
||||
// child
|
||||
metaInfo() {
|
||||
return {
|
||||
meta: [{
|
||||
vmid: 'description',
|
||||
name: 'description',
|
||||
content: this.description,
|
||||
}]
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
description: undefined
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getDescription() {
|
||||
this.description = this.$axios.get()
|
||||
if (!this.description) {
|
||||
// if GET request failed or returned empty,
|
||||
// explicitly set to undefined so the parents'
|
||||
// default description is used
|
||||
this.description = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Boolean attributes
|
||||
|
||||
`vue-meta` maintains a [list](https://github.com/nuxt/vue-meta/blob/master/src/shared/constants.js) of attributes which are boolean attributes according to the HTML specs (and some extra). Whatever value you will pass to these attributes, they will be rendered as a boolean attribute.<sup>*</sup>
|
||||
|
||||
<sup>*</sup><small>Except for the special values `undefined` and `null`, see above</small>
|
||||
|
||||
:::tip Note
|
||||
Prior to `v2.0` any attribute key with `undefined` as value was rendered as boolean attribute. This has been removed as packagers often remove object properties with an `undefined` value as given `a = {}` then `a.a === undefined`
|
||||
:::
|
||||
@@ -0,0 +1,117 @@
|
||||
# Server Side Rendering
|
||||
|
||||
If you have an isomorphic/universal webapp, you'll likely want to render your metadata on the server side as well. Here's how.
|
||||
|
||||
## Add `vue-meta` to the context
|
||||
|
||||
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 meta = app.$meta() // here
|
||||
|
||||
export default (context) => {
|
||||
router.push(context.url)
|
||||
context.meta = meta // and here
|
||||
return app
|
||||
}
|
||||
```
|
||||
|
||||
## Inject metadata into page string
|
||||
|
||||
Probably the easiest method to wrap your head around is if your Vue server markup is rendered out as a string using `renderToString`:
|
||||
|
||||
**server.js:**
|
||||
|
||||
```js
|
||||
app.get('*', (req, res) => {
|
||||
const context = { url: req.url }
|
||||
renderer.renderToString(context, (error, html) => {
|
||||
if (error) return res.send(error.stack)
|
||||
const {
|
||||
title, htmlAttrs, headAttrs, bodyAttrs, link,
|
||||
style, script, noscript, meta
|
||||
} = context.meta.inject()
|
||||
return res.send(`
|
||||
<!doctype html>
|
||||
<html data-vue-meta-server-rendered ${htmlAttrs.text()}>
|
||||
<head ${headAttrs.text()}>
|
||||
${meta.text()}
|
||||
${title.text()}
|
||||
${link.text()}
|
||||
${style.text()}
|
||||
${script.text()}
|
||||
${noscript.text()}
|
||||
</head>
|
||||
<body ${bodyAttrs.text()}>
|
||||
${html}
|
||||
<script src="/assets/vendor.bundle.js"></script>
|
||||
<script src="/assets/client.bundle.js"></script>
|
||||
${script.text({ body: true })}
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
If you are using a separate template file, edit your head tag with
|
||||
|
||||
```html
|
||||
<head>
|
||||
{{{ meta.inject().title.text() }}}
|
||||
{{{ meta.inject().meta.text() }}}
|
||||
</head>
|
||||
```
|
||||
|
||||
Notice the use of `{{{` to avoid double escaping. Be extremely cautious when you use `{{{` with [`__dangerouslyDisableSanitizersByTagID`](/api/#dangerouslydisablesanitizersbytagid).
|
||||
|
||||
## Inject metadata into page stream
|
||||
|
||||
|
||||
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('*', (req, res) => {
|
||||
const context = { url: req.url }
|
||||
const renderStream = renderer.renderToStream(context)
|
||||
renderStream.once('data', () => {
|
||||
const {
|
||||
title, htmlAttrs, headAttrs, bodyAttrs, link,
|
||||
style, script, noscript, meta
|
||||
} = context.meta.inject()
|
||||
res.write(`
|
||||
<!doctype html>
|
||||
<html data-vue-meta-server-rendered ${htmlAttrs.text()}>
|
||||
<head ${headAttrs.text()}>
|
||||
${meta.text()}
|
||||
${title.text()}
|
||||
${link.text()}
|
||||
${style.text()}
|
||||
${script.text()}
|
||||
${noscript.text()}
|
||||
</head>
|
||||
<body ${bodyAttrs.text()}>
|
||||
`)
|
||||
})
|
||||
renderStream.on('data', (chunk) => {
|
||||
res.write(chunk)
|
||||
})
|
||||
renderStream.on('end', () => {
|
||||
res.end(`
|
||||
<script src="/assets/vendor.bundle.js"></script>
|
||||
<script src="/assets/client.bundle.js"></script>
|
||||
${script.text({ body: true })}
|
||||
</body>
|
||||
</html>
|
||||
`)
|
||||
})
|
||||
renderStream.on('error', (error) => {
|
||||
res.status(500).end(`<pre>${error.stack}</pre>`)
|
||||
})
|
||||
})
|
||||
```
|
||||
@@ -0,0 +1,43 @@
|
||||
# Installation
|
||||
|
||||
## Download / CDN
|
||||
|
||||
[https://unpkg.com/vue-meta/lib/vue-meta.js](https://unpkg.com/vue-meta/lib/vue-meta.js)
|
||||
|
||||
For the latest version in the v1.x branch you can use:<br/>
|
||||
[https://unpkg.com/vue-meta@1/lib/vue-meta.js](https://unpkg.com/vue-meta@1/lib/vue-meta.js)
|
||||
|
||||
Or you can replace `1` with the full version number you wish to use.
|
||||
|
||||
If you include vue-meta after vue it will install automatically
|
||||
|
||||
**Uncompressed:**
|
||||
```html
|
||||
<script src="https://unpkg.com/vue-meta/lib/vue-meta.js"></script>
|
||||
```
|
||||
|
||||
**Minified:**
|
||||
```html
|
||||
<script src="https://unpkg.com/vue-meta/lib/vue-meta.min.js"></script>
|
||||
```
|
||||
|
||||
## Package manager
|
||||
**Yarn**
|
||||
```sh
|
||||
$ yarn add vue-meta
|
||||
```
|
||||
|
||||
**npm**
|
||||
```sh
|
||||
$ npm install vue-meta --save
|
||||
```
|
||||
|
||||
### Install
|
||||
You need to explicitly install vue-meta when using a package manager
|
||||
|
||||
```js
|
||||
import Vue from 'vue'
|
||||
import VueMeta from 'vue-meta'
|
||||
|
||||
Vue.use(VueMeta)
|
||||
```
|
||||
+14
-4
@@ -39,6 +39,8 @@
|
||||
"build:es": "rimraf es && babel src --env-name es --out-dir es --ignore 'src/browser.js'",
|
||||
"build:other": "rimraf lib && rollup -c scripts/rollup.config.js",
|
||||
"codecov": "codecov",
|
||||
"docs": "vuepress dev --host 0.0.0.0 --port 3000 docs",
|
||||
"docs:build": "vuepress build docs",
|
||||
"predeploy": "git checkout master && git pull -r",
|
||||
"deploy": "npm version",
|
||||
"postdeploy": "git push origin master --follow-tags && npm run release",
|
||||
@@ -57,6 +59,9 @@
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.uniqueid": "^4.0.1"
|
||||
},
|
||||
"resolutions": {
|
||||
"webpack-dev-middleware": "3.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.3.3",
|
||||
@@ -91,9 +96,14 @@
|
||||
"rollup-plugin-node-resolve": "^4.0.0",
|
||||
"rollup-plugin-terser": "^4.0.4",
|
||||
"update-section": "^0.3.3",
|
||||
"vue": "^2.6.6",
|
||||
"vue-jest": "^3.0.3",
|
||||
"vue-server-renderer": "^2.6.6",
|
||||
"vue-template-compiler": "^2.6.6"
|
||||
"vue": "^2.6.8",
|
||||
"vue-jest": "^3.0.4",
|
||||
"vue-loader": "^15.7.0",
|
||||
"vue-router": "^3.0.2",
|
||||
"vue-server-renderer": "^2.6.8",
|
||||
"vue-template-compiler": "^2.6.8",
|
||||
"vuepress": "^0.14.10",
|
||||
"vuepress-theme-vue": "^1.1.0",
|
||||
"webpack": "^4.29.6"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user