Skip to content

Commit

Permalink
Generate style on custom component in SSR (fix #4055) (#4076)
Browse files Browse the repository at this point in the history
* fix #4055, generate style on custom component

* add test for custom component style

* add synthetic data for module processing
  • Loading branch information
HerringtonDarkholme authored and yyx990803 committed Nov 4, 2016
1 parent c23c5c5 commit 240df14
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 15 deletions.
49 changes: 34 additions & 15 deletions src/platforms/web/server/modules/style.js
Expand Up @@ -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)}`
}
}
11 changes: 11 additions & 0 deletions src/server/render.js
Expand Up @@ -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
Expand Down
81 changes: 81 additions & 0 deletions test/ssr/ssr-string.spec.js
Expand Up @@ -86,6 +86,87 @@ describe('SSR: renderToString', () => {
})
})

it('custom component style', done => {
renderVmWithOptions({
template: '<section><comp :style="style"></comp></section>',
data: {
style: 'color:red'
},
components: {
comp: {
template: '<div></div>'
}
}
}, result => {
expect(result).toContain(
'<section server-rendered="true"><div style="color:red"></div></section>'
)
done()
})
})

it('nested custom component style', done => {
renderVmWithOptions({
template: '<comp :style="style"></comp>',
data: {
style: 'color:red'
},
components: {
comp: {
template: '<nested style="font-size:520rem"></nested>',
components: {
nested: {
template: '<div></div>'
}
}
}
}
}, result => {
expect(result).toContain(
'<div server-rendered="true" style="color:red;font-size:520rem"></div>'
)
done()
})
})

it('component style not passed to child', done => {
renderVmWithOptions({
template: '<comp :style="style"></comp>',
data: {
style: 'color:red'
},
components: {
comp: {
template: '<div><div></div></div>'
}
}
}, result => {
expect(result).toContain(
'<div server-rendered="true" style="color:red"><div></div></div>'
)
done()
})
})

it('component style not passed to slot', done => {
renderVmWithOptions({
template: '<comp :style="style"><span style="color:black"></span></comp>',
data: {
style: 'color:red'
},
components: {
comp: {
template: '<div><slot></slot></div>'
}
}
}, result => {
expect(result).toContain(
'<div server-rendered="true" style="color:red"><span style="color:black"></span></div>'
)
done()
})
})

it('text interpolation', done => {
renderVmWithOptions({
template: '<div>{{ foo }} side {{ bar }}</div>',
Expand Down

0 comments on commit 240df14

Please sign in to comment.