diff --git a/src/platforms/web/server/modules/style.js b/src/platforms/web/server/modules/style.js index 7822023f7a3..a98dec68be7 100644 --- a/src/platforms/web/server/modules/style.js +++ b/src/platforms/web/server/modules/style.js @@ -2,24 +2,43 @@ import { hyphenate, toObject } from 'shared/util' -export default function renderStyle (node: VNodeWithData): ?string { +function concatStyleString (former: string, latter: string) { + if (former === '' || latter === '' || former.charAt(former.length - 1) === ';') { + return former + latter + } + return former + ';' + latter +} + +function generateStyleText (node) { const staticStyle = node.data.attrs && node.data.attrs.style - if (node.data.style || staticStyle) { - let styles = node.data.style - let res = '' - if (styles) { - if (typeof styles === 'string') { - res += styles - } else { - if (Array.isArray(styles)) { - styles = toObject(styles) - } - for (const key in styles) { - res += `${hyphenate(key)}:${styles[key]};` - } - res += staticStyle || '' + let styles = node.data.style + const parentStyle = node.parent ? generateStyleText(node.parent) : '' + + if (!styles && !staticStyle) { + return parentStyle + } + + let dynamicStyle = '' + if (styles) { + if (typeof styles === 'string') { + dynamicStyle += styles + } else { + if (Array.isArray(styles)) { + styles = toObject(styles) + } + for (const key in styles) { + dynamicStyle += `${hyphenate(key)}:${styles[key]};` } } + } + + dynamicStyle = concatStyleString(parentStyle, dynamicStyle) + return concatStyleString(dynamicStyle, staticStyle || '') +} + +export default function renderStyle (node: VNodeWithData): ?string { + const res = generateStyleText(node) + if (res) { return ` style=${JSON.stringify(res)}` } } diff --git a/src/server/render.js b/src/server/render.js index 0aec0bf8fea..0b12ad5484f 100644 --- a/src/server/render.js +++ b/src/server/render.js @@ -150,9 +150,20 @@ function renderElement (el, isRoot, context) { } } +function hasAncestorData (node: VNode) { + const parentNode = node.parent + return parentNode && (parentNode.data || hasAncestorData(parentNode)) +} + function renderStartingTag (node: VNode, context) { let markup = `<${node.tag}` const { directives, modules } = context + + // construct synthetic data for module processing + // because modules like style also produce code by parent VNode data + if (!node.data && hasAncestorData(node)) { + node.data = {} + } if (node.data) { // check directives const dirs = node.data.directives diff --git a/test/ssr/ssr-string.spec.js b/test/ssr/ssr-string.spec.js index c9d7965f4cd..6ca84e6948b 100644 --- a/test/ssr/ssr-string.spec.js +++ b/test/ssr/ssr-string.spec.js @@ -86,6 +86,87 @@ describe('SSR: renderToString', () => { }) }) + it('custom component style', done => { + renderVmWithOptions({ + template: '
', + data: { + style: 'color:red' + }, + components: { + comp: { + template: '
' + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + + it('nested custom component style', done => { + renderVmWithOptions({ + template: '', + data: { + style: 'color:red' + }, + components: { + comp: { + template: '', + components: { + nested: { + template: '
' + } + } + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + + it('component style not passed to child', done => { + renderVmWithOptions({ + template: '', + data: { + style: 'color:red' + }, + components: { + comp: { + template: '
' + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + + it('component style not passed to slot', done => { + renderVmWithOptions({ + template: '', + data: { + style: 'color:red' + }, + components: { + comp: { + template: '
' + } + } + }, result => { + expect(result).toContain( + '
' + ) + done() + }) + }) + it('text interpolation', done => { renderVmWithOptions({ template: '
{{ foo }} side {{ bar }}
',