2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-24 02:40:33 +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:
Declan de Wet
2016-11-10 15:58:02 +02:00
committed by GitHub
13 changed files with 102 additions and 83 deletions
+28 -44
View File
@@ -75,8 +75,7 @@
- [Performance](#performance) - [Performance](#performance)
- [How to prevent the update on the initial page render](#how-to-prevent-the-update-on-the-initial-page-render) - [How to prevent the update on the initial page render](#how-to-prevent-the-update-on-the-initial-page-render)
- [FAQ](#faq) - [FAQ](#faq)
- [How do I use component data in `metaInfo`?](#how-do-i-use-component-data-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 use component props in `metaInfo`?](#how-do-i-use-component-props-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) - [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) - [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. Here are some answers to some frequently asked questions.
## How do I use component data in `metaInfo`? ## How do I use component props and/or component data in `metaInfo`?
Specify a function instead of an object. It will need to return the same type as its definition.
**BlogPost.vue:** Easy. Instead of defining `metaInfo` as an object, define it as a function and access `this` as usual:
**Post.vue:**
```html ```html
<template> <template>
<div id="page"> <div>
<h1>{{ title }}</h1> <h1>{{ title }}</h1>
</div> </div>
</template> </template>
<script> <script>
export default { export default {
name: 'BlogPost', name: 'post',
data: () => ({ props: ['title'],
title: 'Sample blog post' data () {
}), return {
metaInfo: { description: 'A blog post about some stuff'
title () { }
return this.title },
metaInfo ()
return {
title: this.title,
meta: [
{ vmid: 'description', name: 'description', content: this.description }
]
} }
} }
} }
</script> </script>
``` ```
## How do I use component props in `metaInfo`? **PostContainer.vue:**
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**
```html ```html
<template> <template>
<div id="page"> <div>
<blog-post :title="title"></blog-post> <post :title="title"></post>
</div> </div>
</template> </template>
<script> <script>
import BlogPost from './BlogPost.vue' import Post from './Post.vue'
export default { export default {
name: 'BlogPostWrapper', name: 'post-container',
components: { BlogPost }, components: { Post },
data: () => ({ data () {
title: 'Example blog post' return {
}) 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
} }
} }
} }
+3 -3
View File
@@ -9,9 +9,9 @@ Vue.component('child', {
render (h) { render (h) {
return h('h3', null, this.page) return h('h3', null, this.page)
}, },
metaInfo: { metaInfo () {
title () { return {
return this.page title: this.page
} }
} }
}) })
+2 -2
View File
@@ -10,12 +10,12 @@ new Vue({
<p>Inspect Element to see the meta info</p> <p>Inspect Element to see the meta info</p>
</div> </div>
`, `,
metaInfo: { metaInfo: () => ({
title: 'Basic', title: 'Basic',
titleTemplate: '%s | Vue Meta Examples', titleTemplate: '%s | Vue Meta Examples',
htmlAttrs: { htmlAttrs: {
lang: 'en', lang: 'en',
amp: undefined amp: undefined
} }
} })
}).$mount('#app') }).$mount('#app')
+3 -3
View File
@@ -10,9 +10,9 @@ const ChildComponent = {
name: `child-component`, name: `child-component`,
props: ['page'], props: ['page'],
template: `<h3>You're looking at the <strong>{{ page }}</strong> page</h3>`, template: `<h3>You're looking at the <strong>{{ page }}</strong> page</h3>`,
metaInfo: { metaInfo () {
title () { return {
return this.page title: this.page
} }
} }
} }
+10
View File
@@ -5,3 +5,13 @@
<p>Inspect Element to see the meta info</p> <p>Inspect Element to see the meta info</p>
</div> </div>
</template> </template>
<script>
export default {
metaInfo: {
meta: [
{ vmid: 'charset', charset: 'utf-8' }
]
}
}
</script>
+4 -1
View File
@@ -20,7 +20,10 @@
postsCount: 'publishedPostsCount' postsCount: 'publishedPostsCount'
}), }),
metaInfo: { metaInfo: {
title: 'Home' title: 'Home',
meta: [
{ vmid: 'description', name: 'description', content: 'The home page' }
]
} }
} }
</script> </script>
+6 -3
View File
@@ -28,9 +28,12 @@
'isLoading', 'isLoading',
'post' 'post'
]), ]),
metaInfo: { metaInfo () {
title () { return {
return this.isLoading ? 'Loading...' : this.post.title title: this.isLoading ? 'Loading...' : this.post.title,
meta: [
{ vmid: 'description', name: 'description', content: this.post.title }
]
} }
} }
} }
+10
View File
@@ -5,3 +5,13 @@
<p>Inspect Element to see the meta info</p> <p>Inspect Element to see the meta info</p>
</div> </div>
</template> </template>
<script>
export default {
metaInfo: {
meta: [
{ vmid: 'charset', charset: 'utf-8' }
]
}
}
</script>
+4 -1
View File
@@ -20,7 +20,10 @@
postsCount: 'publishedPostsCount' postsCount: 'publishedPostsCount'
}), }),
metaInfo: { metaInfo: {
title: 'Home' title: 'Home',
meta: [
{ vmid: 'description', name: 'description', content: 'The home page' }
]
} }
} }
</script> </script>
+6 -3
View File
@@ -18,9 +18,12 @@
computed: mapGetters([ computed: mapGetters([
'post' 'post'
]), ]),
metaInfo: { metaInfo () {
title () { return {
return this.post.title title: this.post.title,
meta: [
{ vmid: 'description', name: 'description', content: this.post.title }
]
} }
} }
} }
+9 -7
View File
@@ -13,26 +13,28 @@ import deepmerge from 'deepmerge'
* @param {Function} opts.arrayMerge - how should arrays be merged? * @param {Function} opts.arrayMerge - how should arrays be merged?
* @param {Object} [result={}] - result so far * @param {Object} [result={}] - result so far
* @return {Object} result - final aggregated result * @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 { component, option, deep, arrayMerge } = opts
const { $options } = component const { $options } = component
// only collect option data if it exists // only collect option data if it exists
if (typeof $options[option] !== 'undefined' && $options[option] !== null) { 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') { if (typeof data === 'object') {
// merge with existing options // merge with existing options
result.mergedOption = deepmerge(result.mergedOption, data, { result = deepmerge(result, data, {
clone: true, clone: true,
arrayMerge arrayMerge
}) })
result.deepestComponentWithMetaInfo = component
} else { } else {
result.mergedOption = data result = data
} }
} }
+2 -12
View File
@@ -25,7 +25,7 @@ export default function getMetaInfo (component) {
} }
// collect & aggregate all metaInfo $options // collect & aggregate all metaInfo $options
const { mergedOption: info, deepestComponentWithMetaInfo } = getComponentOption({ const info = getComponentOption({
component, component,
option: 'metaInfo', option: 'metaInfo',
deep: true, deep: true,
@@ -71,15 +71,5 @@ export default function getMetaInfo (component) {
info.base = Object.keys(info.base).length ? [info.base] : [] info.base = Object.keys(info.base).length ? [info.base] : []
} }
const metaInfo = deepmerge(defaultInfo, info) return 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
} }
+15 -4
View File
@@ -9,16 +9,27 @@ describe('getComponentOption', () => {
it('returns an empty object when no matching options are found', () => { it('returns an empty object when no matching options are found', () => {
component = new Vue() component = new Vue()
const { mergedOption } = getComponentOption({ component, option: 'noop' }) const mergedOption = getComponentOption({ component, option: 'noop' })
expect(mergedOption).to.eql({}) expect(mergedOption).to.eql({})
}) })
it('fetches the given option from the given component', () => { it('fetches the given option from the given component', () => {
component = new Vue({ someOption: 'foo' }) component = new Vue({ someOption: 'foo' })
const { mergedOption } = getComponentOption({ component, option: 'someOption' }) const mergedOption = getComponentOption({ component, option: 'someOption' })
expect(mergedOption).to.eql('foo') 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', () => { it('fetches deeply nested component options and merges them', () => {
Vue.component('merge-child', { template: '<div></div>', foo: { bar: 'baz' } }) Vue.component('merge-child', { template: '<div></div>', foo: { bar: 'baz' } })
@@ -28,7 +39,7 @@ describe('getComponentOption', () => {
el: container 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' }) expect(mergedOption).to.eql({ bar: 'baz', fizz: 'buzz' })
}) })
@@ -48,7 +59,7 @@ describe('getComponentOption', () => {
el: container el: container
}) })
const { mergedOption } = getComponentOption({ const mergedOption = getComponentOption({
component, component,
option: 'foo', option: 'foo',
deep: true, deep: true,