Skip to content

Commit 024e7c5

Browse files
authored
feat: add basic support for multiple apps on one page (#373)
* feat: add an appId to tags to support multiple apps * feat: show warning on calling () on non-vuemeta components * feat: always use appId ssr for server-generated apps * test: update tests for appId * chore: update circleci to only run audit for dependencies * fix: dont set data-vue-meta attribute on title it has no use on the client as we use document.title there. Which also means the appId listed would be wrong once the title is updated by another app then the ssr app * chore: remove unused import * chore: improve not supported message
1 parent 34c6ad9 commit 024e7c5

23 files changed

+240
-60
lines changed

.circleci/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ jobs:
5252
- attach-project
5353
- run:
5454
name: Security Audit
55-
command: yarn audit
55+
command: yarn audit --groups dependencies
5656

5757
test-unit:
5858
executor: node

examples/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ <h1>Vue Meta Examples</h1>
1010
<li><a href="basic">Basic</a></li>
1111
<li><a href="basic-render">Basic Render</a></li>
1212
<li><a href="keep-alive">Keep alive</a></li>
13+
<li><a href="multiple-apps">Usage with multiple apps</a></li>
1314
<li><a href="vue-router">Usage with vue-router</a></li>
1415
<li><a href="vuex">Usage with vuex</a></li>
1516
<li><a href="vuex-async">Usage with vuex + async actions</a></li>

examples/multiple-apps/app.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import Vue from 'vue'
2+
import VueMeta from 'vue-meta'
3+
4+
Vue.use(VueMeta)
5+
6+
// index.html contains a manual SSR render
7+
8+
const app1 = new Vue({
9+
metaInfo() {
10+
return {
11+
title: 'App 1 title',
12+
bodyAttrs: {
13+
class: 'app-1'
14+
},
15+
meta: [
16+
{ name: 'description', content: 'Hello from app 1', vmid: 'test' },
17+
{ name: 'og:description', content: this.ogContent }
18+
],
19+
script: [
20+
{ innerHTML: 'var appId=1.1', body: true },
21+
{ innerHTML: 'var appId=1.2', vmid: 'app-id-body' },
22+
]
23+
}
24+
},
25+
data() {
26+
return {
27+
ogContent: 'Hello from ssr app'
28+
}
29+
},
30+
template: `
31+
<div id="app1"><h1>App 1</h1></div>
32+
`
33+
})
34+
35+
const app2 = new Vue({
36+
metaInfo: () => ({
37+
title: 'App 2 title',
38+
bodyAttrs: {
39+
class: 'app-2'
40+
},
41+
meta: [
42+
{ name: 'description', content: 'Hello from app 2', vmid: 'test' },
43+
{ name: 'og:description', content: 'Hello from app 2' }
44+
],
45+
script: [
46+
{ innerHTML: 'var appId=2.1', body: true },
47+
{ innerHTML: 'var appId=2.2', vmid: 'app-id-body', body: true },
48+
]
49+
}),
50+
template: `
51+
<div id="app2"><h1>App 2</h1></div>
52+
`
53+
}).$mount('#app2')
54+
55+
app1.$mount('#app1')
56+
57+
const app3 = new Vue({
58+
template: `
59+
<div id="app3"><h1>App 3 (empty metaInfo)</h1></div>
60+
`
61+
}).$mount('#app3')
62+
63+
64+
setTimeout(() => {
65+
console.log('trigger app 1')
66+
app1.$data.ogContent = 'Hello from app 1'
67+
}, 2500)
68+
69+
setTimeout(() => {
70+
console.log('trigger app 2')
71+
app2.$meta().refresh()
72+
}, 5000)
73+
74+
setTimeout(() => {
75+
console.log('trigger app 3')
76+
app3.$meta().refresh()
77+
}, 7500)
78+
setTimeout(() => {
79+
console.log('trigger app 4')
80+
const App = Vue.extend({ template: `<div>app 4</div>` })
81+
const app4 = new App().$mount()
82+
}, 10000)

examples/multiple-apps/index.html

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<!DOCTYPE html>
2+
<html data-vue-meta-server-rendered>
3+
<link rel="stylesheet" href="/global.css">
4+
<title data-vue-meta="ssr">App 1 title</title>
5+
<meta data-vue-meta="ssr" name="og:description" content="Hello from app 1">
6+
</html>
7+
<body>
8+
<a href="/">&larr; Examples index</a>
9+
<div id="app1" data-server-rendered="true"><h1>App 1</h1></div>
10+
<hr />
11+
<div id="app2"></div>
12+
<hr />
13+
<div id="app3"></div>
14+
<script src="/__build__/multiple-apps.js"></script>
15+
<script data-vue-meta="ssr" data-body="true">var appId=1.1</script>
16+
</body>
17+
</html>

examples/package.json

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"dev": "cross-env NODE_ENV=development babel-node server.js",
99
"start": "babel-node server.js",
10-
"ssr": "babel-node ssr"
10+
"ssr": "cross-env NODE_ENV=development babel-node ssr"
1111
},
1212
"repository": {
1313
"type": "git",
@@ -20,27 +20,27 @@
2020
},
2121
"homepage": "https://github.com/nuxt/vue-meta#readme",
2222
"devDependencies": {
23-
"@babel/core": "^7.3.3",
24-
"@babel/node": "^7.2.2",
23+
"@babel/core": "^7.4.5",
24+
"@babel/node": "^7.4.5",
2525
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
26-
"@babel/preset-env": "^7.3.1",
27-
"babel-loader": "^8.0.5",
26+
"@babel/preset-env": "^7.4.5",
27+
"babel-loader": "^8.0.6",
2828
"babel-plugin-dynamic-import-node": "^2.2.0",
29-
"consola": "^2.5.6",
29+
"consola": "^2.7.1",
3030
"cross-env": "^5.2.0",
31-
"express": "^4.16.4",
31+
"express": "^4.17.1",
3232
"express-urlrewrite": "^1.2.0",
33-
"fs-extra": "^7.0.1",
33+
"fs-extra": "^8.0.1",
3434
"lodash": "^4.17.11",
35-
"vue": "^2.6.6",
36-
"vue-loader": "^15.6.4",
37-
"vue-meta": "^1.5.8",
38-
"vue-router": "^3.0.2",
39-
"vue-server-renderer": "^2.6.8",
40-
"vue-template-compiler": "^2.6.6",
41-
"vuex": "^3.1.0",
42-
"webpack": "^4.29.5",
43-
"webpack-dev-server": "^3.2.0",
44-
"webpackbar": "^3.1.5"
35+
"vue": "^2.6.10",
36+
"vue-loader": "^15.7.0",
37+
"vue-meta": "^1.6.0",
38+
"vue-router": "^3.0.6",
39+
"vue-server-renderer": "^2.6.10",
40+
"vue-template-compiler": "^2.6.10",
41+
"vuex": "^3.1.1",
42+
"webpack": "^4.32.2",
43+
"webpack-dev-server": "^3.5.0",
44+
"webpackbar": "^3.2.0"
4545
}
4646
}

examples/ssr/app.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import Vue from 'vue'
2-
// import VueMeta from 'vue-meta'
32

43
export default async function createApp() {
54
// the dynamic import is for this example only

src/client/$meta.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { showWarningNotSupported } from '../shared/constants'
12
import { getOptions } from '../shared/options'
23
import { pause, resume } from '../shared/pausing'
34
import refresh from './refresh'
@@ -12,6 +13,16 @@ export default function _$meta(options = {}) {
1213
* @return {Object} - injector
1314
*/
1415
return function $meta() {
16+
if (!this.$root._vueMeta) {
17+
return {
18+
getOptions: showWarningNotSupported,
19+
refresh: showWarningNotSupported,
20+
inject: showWarningNotSupported,
21+
pause: showWarningNotSupported,
22+
resume: showWarningNotSupported
23+
}
24+
}
25+
1526
return {
1627
getOptions: () => getOptions(options),
1728
refresh: _refresh.bind(this),

src/client/refresh.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ export default function _refresh(options = {}) {
1717
return function refresh() {
1818
const metaInfo = getMetaInfo(options, this.$root, clientSequences)
1919

20-
const tags = updateClientMetaInfo(options, metaInfo)
20+
const appId = this.$root._vueMeta.appId
21+
const tags = updateClientMetaInfo(appId, options, metaInfo)
2122
// emit "event" with new info
2223
if (tags && isFunction(metaInfo.changed)) {
2324
metaInfo.changed(metaInfo, tags.addedTags, tags.removedTags)

src/client/updateClientMetaInfo.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function getTag(tags, tag) {
1616
*
1717
* @param {Object} newInfo - the meta info to update to
1818
*/
19-
export default function updateClientMetaInfo(options = {}, newInfo) {
19+
export default function updateClientMetaInfo(appId, options = {}, newInfo) {
2020
const { ssrAttribute } = options
2121

2222
// only cache tags for current update
@@ -25,7 +25,7 @@ export default function updateClientMetaInfo(options = {}, newInfo) {
2525
const htmlTag = getTag(tags, 'html')
2626

2727
// if this is a server render, then dont update
28-
if (htmlTag.hasAttribute(ssrAttribute)) {
28+
if (appId === 'ssr' && htmlTag.hasAttribute(ssrAttribute)) {
2929
// remove the server render attribute so we can update on (next) changes
3030
htmlTag.removeAttribute(ssrAttribute)
3131
return false
@@ -59,6 +59,7 @@ export default function updateClientMetaInfo(options = {}, newInfo) {
5959
}
6060

6161
const { oldTags, newTags } = updateTag(
62+
appId,
6263
options,
6364
type,
6465
newInfo[type],

src/client/updaters/tag.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ import { toArray, includes } from '../../utils/array'
99
* @param {(Array<Object>|Object)} tags - an array of tag objects or a single object in case of base
1010
* @return {Object} - a representation of what tags changed
1111
*/
12-
export default function updateTag({ attribute, tagIDKeyName } = {}, type, tags, headTag, bodyTag) {
13-
const oldHeadTags = toArray(headTag.querySelectorAll(`${type}[${attribute}]`))
14-
const oldBodyTags = toArray(bodyTag.querySelectorAll(`${type}[${attribute}][data-body="true"]`))
12+
export default function updateTag(appId, { attribute, tagIDKeyName } = {}, type, tags, headTag, bodyTag) {
13+
const oldHeadTags = toArray(headTag.querySelectorAll(`${type}[${attribute}="${appId}"], ${type}[data-${tagIDKeyName}]`))
14+
const oldBodyTags = toArray(bodyTag.querySelectorAll(`${type}[${attribute}="${appId}"][data-body="true"], ${type}[data-${tagIDKeyName}][data-body="true"]`))
1515
const dataAttributes = [tagIDKeyName, 'body']
1616
const newTags = []
1717

@@ -31,7 +31,8 @@ export default function updateTag({ attribute, tagIDKeyName } = {}, type, tags,
3131
if (tags.length) {
3232
tags.forEach((tag) => {
3333
const newElement = document.createElement(type)
34-
newElement.setAttribute(attribute, 'true')
34+
35+
newElement.setAttribute(attribute, appId)
3536

3637
const oldTags = tag.body !== true ? oldHeadTags : oldBodyTags
3738

0 commit comments

Comments
 (0)