Skip to content

Commit 240df14

Browse files
HerringtonDarkholmeyyx990803
authored andcommitted
Generate style on custom component in SSR (fix vuejs#4055) (vuejs#4076)
* fix vuejs#4055, generate style on custom component * add test for custom component style * add synthetic data for module processing
1 parent c23c5c5 commit 240df14

File tree

3 files changed

+126
-15
lines changed

3 files changed

+126
-15
lines changed

src/platforms/web/server/modules/style.js

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,43 @@
22

33
import { hyphenate, toObject } from 'shared/util'
44

5-
export default function renderStyle (node: VNodeWithData): ?string {
5+
function concatStyleString (former: string, latter: string) {
6+
if (former === '' || latter === '' || former.charAt(former.length - 1) === ';') {
7+
return former + latter
8+
}
9+
return former + ';' + latter
10+
}
11+
12+
function generateStyleText (node) {
613
const staticStyle = node.data.attrs && node.data.attrs.style
7-
if (node.data.style || staticStyle) {
8-
let styles = node.data.style
9-
let res = ''
10-
if (styles) {
11-
if (typeof styles === 'string') {
12-
res += styles
13-
} else {
14-
if (Array.isArray(styles)) {
15-
styles = toObject(styles)
16-
}
17-
for (const key in styles) {
18-
res += `${hyphenate(key)}:${styles[key]};`
19-
}
20-
res += staticStyle || ''
14+
let styles = node.data.style
15+
const parentStyle = node.parent ? generateStyleText(node.parent) : ''
16+
17+
if (!styles && !staticStyle) {
18+
return parentStyle
19+
}
20+
21+
let dynamicStyle = ''
22+
if (styles) {
23+
if (typeof styles === 'string') {
24+
dynamicStyle += styles
25+
} else {
26+
if (Array.isArray(styles)) {
27+
styles = toObject(styles)
28+
}
29+
for (const key in styles) {
30+
dynamicStyle += `${hyphenate(key)}:${styles[key]};`
2131
}
2232
}
33+
}
34+
35+
dynamicStyle = concatStyleString(parentStyle, dynamicStyle)
36+
return concatStyleString(dynamicStyle, staticStyle || '')
37+
}
38+
39+
export default function renderStyle (node: VNodeWithData): ?string {
40+
const res = generateStyleText(node)
41+
if (res) {
2342
return ` style=${JSON.stringify(res)}`
2443
}
2544
}

src/server/render.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,20 @@ function renderElement (el, isRoot, context) {
150150
}
151151
}
152152

153+
function hasAncestorData (node: VNode) {
154+
const parentNode = node.parent
155+
return parentNode && (parentNode.data || hasAncestorData(parentNode))
156+
}
157+
153158
function renderStartingTag (node: VNode, context) {
154159
let markup = `<${node.tag}`
155160
const { directives, modules } = context
161+
162+
// construct synthetic data for module processing
163+
// because modules like style also produce code by parent VNode data
164+
if (!node.data && hasAncestorData(node)) {
165+
node.data = {}
166+
}
156167
if (node.data) {
157168
// check directives
158169
const dirs = node.data.directives

test/ssr/ssr-string.spec.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,87 @@ describe('SSR: renderToString', () => {
8686
})
8787
})
8888

89+
it('custom component style', done => {
90+
renderVmWithOptions({
91+
template: '<section><comp :style="style"></comp></section>',
92+
data: {
93+
style: 'color:red'
94+
},
95+
components: {
96+
comp: {
97+
template: '<div></div>'
98+
}
99+
}
100+
}, result => {
101+
expect(result).toContain(
102+
'<section server-rendered="true"><div style="color:red"></div></section>'
103+
)
104+
done()
105+
})
106+
})
107+
108+
it('nested custom component style', done => {
109+
renderVmWithOptions({
110+
template: '<comp :style="style"></comp>',
111+
data: {
112+
style: 'color:red'
113+
},
114+
components: {
115+
comp: {
116+
template: '<nested style="font-size:520rem"></nested>',
117+
components: {
118+
nested: {
119+
template: '<div></div>'
120+
}
121+
}
122+
}
123+
}
124+
}, result => {
125+
expect(result).toContain(
126+
'<div server-rendered="true" style="color:red;font-size:520rem"></div>'
127+
)
128+
done()
129+
})
130+
})
131+
132+
it('component style not passed to child', done => {
133+
renderVmWithOptions({
134+
template: '<comp :style="style"></comp>',
135+
data: {
136+
style: 'color:red'
137+
},
138+
components: {
139+
comp: {
140+
template: '<div><div></div></div>'
141+
}
142+
}
143+
}, result => {
144+
expect(result).toContain(
145+
'<div server-rendered="true" style="color:red"><div></div></div>'
146+
)
147+
done()
148+
})
149+
})
150+
151+
it('component style not passed to slot', done => {
152+
renderVmWithOptions({
153+
template: '<comp :style="style"><span style="color:black"></span></comp>',
154+
data: {
155+
style: 'color:red'
156+
},
157+
components: {
158+
comp: {
159+
template: '<div><slot></slot></div>'
160+
}
161+
}
162+
}, result => {
163+
expect(result).toContain(
164+
'<div server-rendered="true" style="color:red"><span style="color:black"></span></div>'
165+
)
166+
done()
167+
})
168+
})
169+
89170
it('text interpolation', done => {
90171
renderVmWithOptions({
91172
template: '<div>{{ foo }} side {{ bar }}</div>',

0 commit comments

Comments
 (0)