diff --git a/docs/api/README.md b/docs/api/README.md index 890d80b..48297ad 100644 --- a/docs/api/README.md +++ b/docs/api/README.md @@ -190,12 +190,16 @@ Updates the current metadata with new metadata. Useful when updating metadata as the result of an asynchronous action that resolves after the initial render takes place. ### $meta().inject +- arguments + - injectOptions (type: `object`) - returns [`metaInfo`](/api/#metaInfo-properties) :::tip SSR only `inject` is available in the server plugin only and is not available on the client ::: +You can pass an object to inject with global inject options. See [SSR injection method arguments](/api/#noscript-text) for a list of available options. + 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 @@ -809,7 +813,9 @@ See the [SSR guide](/guide/ssr.html#inject-metadata-into-page-string) for more i ### script.text ### noscript.text - arguments - - options (type: `object`, default: `{ ln: false , body: false, pbody: false }`) + - options (type: `object`, default: `{ isSSR: true, ln: false , body: false, pbody: false }`) + +Set `isSSR: false` if you generate a SPA on server side and want to use the default appId `1` instead of [ssrAppId](/api/#ssrappid) The `body` and `pbody` props can be used to support positioning of elements in your template, see [SSR Support](#ssr-support) diff --git a/src/server/generateServerInjector.js b/src/server/generateServerInjector.js index a00818d..bd30b73 100644 --- a/src/server/generateServerInjector.js +++ b/src/server/generateServerInjector.js @@ -9,7 +9,7 @@ import { titleGenerator, attributeGenerator, tagGenerator } from './generators' * @return {Object} - the new injector */ -export default function generateServerInjector (options, metaInfo) { +export default function generateServerInjector (options, metaInfo, globalInjectOptions) { const serverInjector = { data: metaInfo, extraData: undefined, @@ -23,15 +23,16 @@ export default function generateServerInjector (options, metaInfo) { // only call title for the head return (opts.body || opts.pbody ? '' : m.title.text(opts)) + m.meta.text(opts) + + m.base.text(opts) + m.link.text(opts) + m.style.text(opts) + m.script.text(opts) + m.noscript.text(opts) }, injectors: { - head: ln => serverInjector.callInjectors({ ln }), - bodyPrepend: ln => serverInjector.callInjectors({ ln, pbody: true }), - bodyAppend: ln => serverInjector.callInjectors({ ln, body: true }) + head: ln => serverInjector.callInjectors({ ...globalInjectOptions, ln }), + bodyPrepend: ln => serverInjector.callInjectors({ ...globalInjectOptions, ln, pbody: true }), + bodyAppend: ln => serverInjector.callInjectors({ ...globalInjectOptions, ln, body: true }) } } @@ -41,9 +42,17 @@ export default function generateServerInjector (options, metaInfo) { } serverInjector.injectors[type] = { - text (arg) { + text (injectOptions) { + const addSsrAttribute = injectOptions === true + + injectOptions = { + addSsrAttribute, + ...globalInjectOptions, + ...injectOptions + } + if (type === 'title') { - return titleGenerator(options, type, serverInjector.data[type], arg) + return titleGenerator(options, type, serverInjector.data[type], injectOptions) } if (metaInfoAttributeKeys.includes(type)) { @@ -51,9 +60,10 @@ export default function generateServerInjector (options, metaInfo) { const data = serverInjector.data[type] if (data) { + const appId = injectOptions.isSSR === false ? '1' : options.ssrAppId for (const attr in data) { attributeData[attr] = { - [options.ssrAppId]: data[attr] + [appId]: data[attr] } } } @@ -72,15 +82,15 @@ export default function generateServerInjector (options, metaInfo) { } } - return attributeGenerator(options, type, attributeData, arg) + return attributeGenerator(options, type, attributeData, injectOptions) } - let str = tagGenerator(options, type, serverInjector.data[type], arg) + let str = tagGenerator(options, type, serverInjector.data[type], injectOptions) if (serverInjector.extraData) { for (const appId in serverInjector.extraData) { const data = serverInjector.extraData[appId][type] - const extraStr = tagGenerator(options, type, data, { appId, ...arg }) + const extraStr = tagGenerator(options, type, data, { appId, ...injectOptions }) str = `${str}${extraStr}` } } diff --git a/src/server/generators/attribute.js b/src/server/generators/attribute.js index 873669b..d136b8e 100644 --- a/src/server/generators/attribute.js +++ b/src/server/generators/attribute.js @@ -7,7 +7,7 @@ import { booleanHtmlAttributes } from '../../shared/constants' * @param {Object} data - the attributes to generate * @return {Object} - the attribute generator */ -export default function attributeGenerator (options, type, data, addSrrAttribute) { +export default function attributeGenerator (options, type, data, { addSsrAttribute }) { const { attribute, ssrAttribute } = options || {} let attributeStr = '' @@ -32,7 +32,7 @@ export default function attributeGenerator (options, type, data, addSrrAttribute attributeStr += `${attribute}="${encodeURI(JSON.stringify(data))}"` } - if (type === 'htmlAttrs' && addSrrAttribute) { + if (type === 'htmlAttrs' && addSsrAttribute) { return `${ssrAttribute}${attributeStr ? ' ' : ''}${attributeStr}` } diff --git a/src/server/generators/tag.js b/src/server/generators/tag.js index 319fb56..312e3a6 100644 --- a/src/server/generators/tag.js +++ b/src/server/generators/tag.js @@ -16,7 +16,7 @@ import { */ export default function tagGenerator (options, type, tags, generatorOptions) { const { ssrAppId, attribute, tagIDKeyName } = options || {} - const { appId, body = false, pbody = false, ln = false } = generatorOptions || {} + const { appId, isSSR = true, body = false, pbody = false, ln = false } = generatorOptions || {} const dataAttributes = [tagIDKeyName, ...commonDataAttributes] @@ -40,7 +40,7 @@ export default function tagGenerator (options, type, tags, generatorOptions) { return tagsStr } - let attrs = tag.once ? '' : ` ${attribute}="${appId || ssrAppId}"` + let attrs = tag.once ? '' : ` ${attribute}="${appId || (isSSR === false ? '1' : ssrAppId)}"` // build a string containing all attributes of this tag for (const attr in tag) { diff --git a/src/server/inject.js b/src/server/inject.js index 5755ff9..b41d44f 100644 --- a/src/server/inject.js +++ b/src/server/inject.js @@ -13,7 +13,7 @@ import generateServerInjector from './generateServerInjector' * @vm {Object} - Vue instance - ideally the root component * @return {Object} - server meta info with `toString` methods */ -export default function inject (rootVm, options) { +export default function inject (rootVm, options, injectOptions) { // make sure vue-meta was initiated if (!rootVm[rootConfigKey]) { showWarningNotSupported() @@ -26,7 +26,7 @@ export default function inject (rootVm, options) { const metaInfo = getMetaInfo(options, rawInfo, serverSequences, rootVm) // generate server injector - const serverInjector = generateServerInjector(options, metaInfo) + const serverInjector = generateServerInjector(options, metaInfo, injectOptions) // add meta info from additional apps const appsMetaInfo = getAppsMetaInfo() diff --git a/src/shared/$meta.js b/src/shared/$meta.js index b237314..e59ef3c 100644 --- a/src/shared/$meta.js +++ b/src/shared/$meta.js @@ -38,7 +38,7 @@ export default function $meta (options) { } }, refresh: () => refresh($root, options), - inject: () => process.server ? inject($root, options) : showWarningNotSupportedInBrowserBundle('inject'), + inject: injectOptions => process.server ? inject($root, options, injectOptions) : showWarningNotSupportedInBrowserBundle('inject'), pause: () => pause($root), resume: () => resume($root), addApp: appId => addApp($root, appId, options) diff --git a/test/unit/components.test.js b/test/unit/components.test.js index 9dc35e2..87ee7e9 100644 --- a/test/unit/components.test.js +++ b/test/unit/components.test.js @@ -139,11 +139,20 @@ describe('components', () => { warn.mockRestore() }) - test('meta-info can be rendered with inject', () => { + test('meta-info can be rendered with inject (ssr)', () => { const wrapper = mount(HelloWorld, { localVue: Vue }) const metaInfo = wrapper.vm.$meta().inject() expect(metaInfo.title.text()).toEqual('Hello World') + expect(metaInfo.meta.text()).toContain('') + }) + + test('meta-info can be rendered with inject (spa)', () => { + const wrapper = mount(HelloWorld, { localVue: Vue }) + + const metaInfo = wrapper.vm.$meta().inject({ isSSR: false, ln: true }) + expect(metaInfo.title.text()).toEqual('Hello World\n') + expect(metaInfo.meta.text()).toContain('\n') }) test('inject also renders additional app info', () => { diff --git a/test/unit/generators.test.js b/test/unit/generators.test.js index 9dc2664..cc4383d 100644 --- a/test/unit/generators.test.js +++ b/test/unit/generators.test.js @@ -135,7 +135,7 @@ describe('extra tests', () => { expect(meta.bodyPrepend(true)).toBe('\n') expect(meta.bodyAppend()).toBe('') - expect(meta.htmlAttrs.text()).toBe('lang="en" data-vue-meta="%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D"') + expect(meta.htmlAttrs.text({ ln: true })).toBe('lang="en" data-vue-meta="%7B%22lang%22:%7B%22ssr%22:%22en%22%7D%7D"') expect(meta.bodyAttrs.text()).toBe('class="base-class extra-class" data-vue-meta="%7B%22class%22:%7B%22ssr%22:%22base-class%22,%22test-app%22:%22extra-class%22%7D%7D"') }) })