Skip to content

Commit 165a14a

Browse files
fix(ssr): fix on-component directives rendering (#12661)
fix #10733
1 parent bba6b3d commit 165a14a

File tree

3 files changed

+103
-2
lines changed

3 files changed

+103
-2
lines changed

Diff for: packages/server-renderer/src/render.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ function renderComponentInner(node, isRoot, context) {
206206
type: 'Component',
207207
prevActive
208208
})
209+
if (isDef(node.data) && isDef(node.data.directives)) {
210+
childNode.data = childNode.data || {}
211+
childNode.data.directives = node.data.directives
212+
childNode.isComponentRootElement = true
213+
}
209214
renderNode(childNode, isRoot, context)
210215
}
211216

@@ -372,7 +377,10 @@ function renderStartingTag(node: VNode, context) {
372377
if (dirRenderer) {
373378
// directives mutate the node's data
374379
// which then gets rendered by modules
375-
dirRenderer(node, dirs[i])
380+
dirRenderer(
381+
node.isComponentRootElement ? node.parent : node,
382+
dirs[i]
383+
)
376384
}
377385
}
378386
}

Diff for: packages/server-renderer/test/ssr-string.spec.ts

+93-1
Original file line numberDiff line numberDiff line change
@@ -1086,7 +1086,7 @@ describe('SSR: renderToString', () => {
10861086
)
10871087
})
10881088

1089-
it('custom directives', done => {
1089+
it('custom directives on raw element', done => {
10901090
const renderer = createRenderer({
10911091
directives: {
10921092
'class-prefixer': (node, dir) => {
@@ -1129,6 +1129,98 @@ describe('SSR: renderToString', () => {
11291129
)
11301130
})
11311131

1132+
it('custom directives on component', done => {
1133+
const Test = {
1134+
template: '<span>hello world</span>'
1135+
}
1136+
const renderer = createRenderer({
1137+
directives: {
1138+
'class-prefixer': (node, dir) => {
1139+
if (node.data.class) {
1140+
node.data.class = `${dir.value}-${node.data.class}`
1141+
}
1142+
if (node.data.staticClass) {
1143+
node.data.staticClass = `${dir.value}-${node.data.staticClass}`
1144+
}
1145+
}
1146+
}
1147+
})
1148+
renderer.renderToString(
1149+
new Vue({
1150+
template:
1151+
'<p><Test v-class-prefixer="\'my\'" class="class1" :class="\'class2\'" /></p>',
1152+
components: { Test }
1153+
}),
1154+
(err, result) => {
1155+
expect(err).toBeNull()
1156+
expect(result).toContain(
1157+
'<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
1158+
)
1159+
done()
1160+
}
1161+
)
1162+
})
1163+
1164+
it('custom directives on element root of a component', done => {
1165+
const Test = {
1166+
template:
1167+
'<span v-class-prefixer="\'my\'" class="class1" :class="\'class2\'">hello world</span>'
1168+
}
1169+
const renderer = createRenderer({
1170+
directives: {
1171+
'class-prefixer': (node, dir) => {
1172+
if (node.data.class) {
1173+
node.data.class = `${dir.value}-${node.data.class}`
1174+
}
1175+
if (node.data.staticClass) {
1176+
node.data.staticClass = `${dir.value}-${node.data.staticClass}`
1177+
}
1178+
}
1179+
}
1180+
})
1181+
renderer.renderToString(
1182+
new Vue({
1183+
template: '<p><Test /></p>',
1184+
components: { Test }
1185+
}),
1186+
(err, result) => {
1187+
expect(err).toBeNull()
1188+
expect(result).toContain(
1189+
'<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
1190+
)
1191+
done()
1192+
}
1193+
)
1194+
})
1195+
1196+
it('custom directives on element with parent element', done => {
1197+
const renderer = createRenderer({
1198+
directives: {
1199+
'class-prefixer': (node, dir) => {
1200+
if (node.data.class) {
1201+
node.data.class = `${dir.value}-${node.data.class}`
1202+
}
1203+
if (node.data.staticClass) {
1204+
node.data.staticClass = `${dir.value}-${node.data.staticClass}`
1205+
}
1206+
}
1207+
}
1208+
})
1209+
renderer.renderToString(
1210+
new Vue({
1211+
template:
1212+
'<p><span v-class-prefixer="\'my\'" class="class1" :class="\'class2\'">hello world</span></p>'
1213+
}),
1214+
(err, result) => {
1215+
expect(err).toBeNull()
1216+
expect(result).toContain(
1217+
'<p data-server-rendered="true"><span class="my-class1 my-class2">hello world</span></p>'
1218+
)
1219+
done()
1220+
}
1221+
)
1222+
})
1223+
11321224
it('should not warn for custom directives that do not have server-side implementation', done => {
11331225
renderToString(
11341226
new Vue({

Diff for: src/core/vdom/vnode.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export default class VNode {
3333
fnOptions?: ComponentOptions | null // for SSR caching
3434
devtoolsMeta?: Object | null // used to store functional render context for devtools
3535
fnScopeId?: string | null // functional scope id support
36+
isComponentRootElement?: boolean | null // for SSR directives
3637

3738
constructor(
3839
tag?: string,

0 commit comments

Comments
 (0)