mirror of
https://github.com/tenrok/vue-meta.git
synced 2026-06-11 21:22:24 +03:00
Merge pull request #15 from declandewet/fix/taglist-duplicate-overrides
Favor function syntax over per-attribute function syntax
This commit is contained in:
@@ -75,8 +75,7 @@
|
||||
- [Performance](#performance)
|
||||
- [How to prevent the update on the initial page render](#how-to-prevent-the-update-on-the-initial-page-render)
|
||||
- [FAQ](#faq)
|
||||
- [How do I use component data in `metaInfo`?](#how-do-i-use-component-data-in-metainfo)
|
||||
- [How do I use component props in `metaInfo`?](#how-do-i-use-component-props-in-metainfo)
|
||||
- [How do I use component props and/or component data in `metaInfo`?](#how-do-i-use-component-props-andor-component-data-in-metainfo)
|
||||
- [How do I populate `metaInfo` from the result of an asynchronous action?](#how-do-i-populate-metainfo-from-the-result-of-an-asynchronous-action)
|
||||
- [Examples](#examples)
|
||||
|
||||
@@ -610,71 +609,56 @@ Add the `data-vue-meta-server-rendered` attribute to the `<html>` tag on the ser
|
||||
|
||||
Here are some answers to some frequently asked questions.
|
||||
|
||||
## How do I use component data in `metaInfo`?
|
||||
Specify a function instead of an object. It will need to return the same type as its definition.
|
||||
## How do I use component props and/or component data in `metaInfo`?
|
||||
|
||||
**BlogPost.vue:**
|
||||
Easy. Instead of defining `metaInfo` as an object, define it as a function and access `this` as usual:
|
||||
|
||||
**Post.vue:**
|
||||
```html
|
||||
<template>
|
||||
<div id="page">
|
||||
<div>
|
||||
<h1>{{ title }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BlogPost',
|
||||
data: () => ({
|
||||
title: 'Sample blog post'
|
||||
}),
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.title
|
||||
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>
|
||||
```
|
||||
|
||||
## How do I use component props in `metaInfo`?
|
||||
The same way you use data - specify a function instead of an object. It will need to return the same type as its definition.
|
||||
|
||||
**BlogPostWrapper.vue**
|
||||
**PostContainer.vue:**
|
||||
```html
|
||||
<template>
|
||||
<div id="page">
|
||||
<blog-post :title="title"></blog-post>
|
||||
<div>
|
||||
<post :title="title"></post>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import BlogPost from './BlogPost.vue'
|
||||
import Post from './Post.vue'
|
||||
|
||||
export default {
|
||||
name: 'BlogPostWrapper',
|
||||
components: { BlogPost },
|
||||
data: () => ({
|
||||
title: 'Example blog post'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
**BlogPost.vue**
|
||||
```html
|
||||
<template>
|
||||
<div id="page">
|
||||
<h1>{{ title }}</h1>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'BlogPost',
|
||||
props: ['title'],
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.title
|
||||
name: 'post-container',
|
||||
components: { Post },
|
||||
data () {
|
||||
return {
|
||||
title: 'Example blog post'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ Vue.component('child', {
|
||||
render (h) {
|
||||
return h('h3', null, this.page)
|
||||
},
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.page
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.page
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -10,12 +10,12 @@ new Vue({
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
`,
|
||||
metaInfo: {
|
||||
metaInfo: () => ({
|
||||
title: 'Basic',
|
||||
titleTemplate: '%s | Vue Meta Examples',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
amp: undefined
|
||||
}
|
||||
}
|
||||
})
|
||||
}).$mount('#app')
|
||||
|
||||
@@ -10,9 +10,9 @@ const ChildComponent = {
|
||||
name: `child-component`,
|
||||
props: ['page'],
|
||||
template: `<h3>You're looking at the <strong>{{ page }}</strong> page</h3>`,
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.page
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,13 @@
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ vmid: 'charset', charset: 'utf-8' }
|
||||
]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
postsCount: 'publishedPostsCount'
|
||||
}),
|
||||
metaInfo: {
|
||||
title: 'Home'
|
||||
title: 'Home',
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: 'The home page' }
|
||||
]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -28,9 +28,12 @@
|
||||
'isLoading',
|
||||
'post'
|
||||
]),
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.isLoading ? 'Loading...' : this.post.title
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.isLoading ? 'Loading...' : this.post.title,
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: this.post.title }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,3 +5,13 @@
|
||||
<p>Inspect Element to see the meta info</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
metaInfo: {
|
||||
meta: [
|
||||
{ vmid: 'charset', charset: 'utf-8' }
|
||||
]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
postsCount: 'publishedPostsCount'
|
||||
}),
|
||||
metaInfo: {
|
||||
title: 'Home'
|
||||
title: 'Home',
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: 'The home page' }
|
||||
]
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -18,9 +18,12 @@
|
||||
computed: mapGetters([
|
||||
'post'
|
||||
]),
|
||||
metaInfo: {
|
||||
title () {
|
||||
return this.post.title
|
||||
metaInfo () {
|
||||
return {
|
||||
title: this.post.title,
|
||||
meta: [
|
||||
{ vmid: 'description', name: 'description', content: this.post.title }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,26 +13,28 @@ import deepmerge from 'deepmerge'
|
||||
* @param {Function} opts.arrayMerge - how should arrays be merged?
|
||||
* @param {Object} [result={}] - result so far
|
||||
* @return {Object} result - final aggregated result
|
||||
* @return {Object} result.mergedOption - the actual merged options
|
||||
* @return {Object} result.deepestComponentWithMetaInfo - the deepest component in the heirarchy that has a `metaInfo` instance property
|
||||
*/
|
||||
export default function getComponentOption (opts, result = { mergedOption: {} }) {
|
||||
export default function getComponentOption (opts, result = {}) {
|
||||
const { component, option, deep, arrayMerge } = opts
|
||||
const { $options } = component
|
||||
|
||||
// only collect option data if it exists
|
||||
if (typeof $options[option] !== 'undefined' && $options[option] !== null) {
|
||||
const data = $options[option]
|
||||
let data = $options[option]
|
||||
|
||||
// if option is a function, replace it with it's result
|
||||
if (typeof data === 'function') {
|
||||
data = data.call(component)
|
||||
}
|
||||
|
||||
if (typeof data === 'object') {
|
||||
// merge with existing options
|
||||
result.mergedOption = deepmerge(result.mergedOption, data, {
|
||||
result = deepmerge(result, data, {
|
||||
clone: true,
|
||||
arrayMerge
|
||||
})
|
||||
result.deepestComponentWithMetaInfo = component
|
||||
} else {
|
||||
result.mergedOption = data
|
||||
result = data
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function getMetaInfo (component) {
|
||||
}
|
||||
|
||||
// collect & aggregate all metaInfo $options
|
||||
const { mergedOption: info, deepestComponentWithMetaInfo } = getComponentOption({
|
||||
const info = getComponentOption({
|
||||
component,
|
||||
option: 'metaInfo',
|
||||
deep: true,
|
||||
@@ -71,15 +71,5 @@ export default function getMetaInfo (component) {
|
||||
info.base = Object.keys(info.base).length ? [info.base] : []
|
||||
}
|
||||
|
||||
const metaInfo = deepmerge(defaultInfo, info)
|
||||
|
||||
// inject component context into functions & call to normalize data
|
||||
Object.keys(metaInfo).forEach((key) => {
|
||||
const val = metaInfo[key]
|
||||
if (typeof val === 'function') {
|
||||
metaInfo[key] = val.call(deepestComponentWithMetaInfo)
|
||||
}
|
||||
})
|
||||
|
||||
return metaInfo
|
||||
return deepmerge(defaultInfo, info)
|
||||
}
|
||||
|
||||
@@ -9,16 +9,27 @@ describe('getComponentOption', () => {
|
||||
|
||||
it('returns an empty object when no matching options are found', () => {
|
||||
component = new Vue()
|
||||
const { mergedOption } = getComponentOption({ component, option: 'noop' })
|
||||
const mergedOption = getComponentOption({ component, option: 'noop' })
|
||||
expect(mergedOption).to.eql({})
|
||||
})
|
||||
|
||||
it('fetches the given option from the given component', () => {
|
||||
component = new Vue({ someOption: 'foo' })
|
||||
const { mergedOption } = getComponentOption({ component, option: 'someOption' })
|
||||
const mergedOption = getComponentOption({ component, option: 'someOption' })
|
||||
expect(mergedOption).to.eql('foo')
|
||||
})
|
||||
|
||||
it('calls a function option, injecting the component as context', () => {
|
||||
component = new Vue({
|
||||
name: 'foobar',
|
||||
someFunc () {
|
||||
return this.$options.name
|
||||
}
|
||||
})
|
||||
const mergedOption = getComponentOption({ component, option: 'someFunc' })
|
||||
expect(mergedOption).to.eql('foobar')
|
||||
})
|
||||
|
||||
it('fetches deeply nested component options and merges them', () => {
|
||||
Vue.component('merge-child', { template: '<div></div>', foo: { bar: 'baz' } })
|
||||
|
||||
@@ -28,7 +39,7 @@ describe('getComponentOption', () => {
|
||||
el: container
|
||||
})
|
||||
|
||||
const { mergedOption } = getComponentOption({ component, option: 'foo', deep: true })
|
||||
const mergedOption = getComponentOption({ component, option: 'foo', deep: true })
|
||||
expect(mergedOption).to.eql({ bar: 'baz', fizz: 'buzz' })
|
||||
})
|
||||
|
||||
@@ -48,7 +59,7 @@ describe('getComponentOption', () => {
|
||||
el: container
|
||||
})
|
||||
|
||||
const { mergedOption } = getComponentOption({
|
||||
const mergedOption = getComponentOption({
|
||||
component,
|
||||
option: 'foo',
|
||||
deep: true,
|
||||
|
||||
Reference in New Issue
Block a user