From 165a14a6c6c406176037465d2961259c5c980399 Mon Sep 17 00:00:00 2001 From: Alexander Lichter Date: Thu, 18 Aug 2022 10:22:55 +0200 Subject: [PATCH] fix(ssr): fix on-component directives rendering (#12661) fix #10733 --- packages/server-renderer/src/render.ts | 10 +- .../server-renderer/test/ssr-string.spec.ts | 94 ++++++++++++++++++- src/core/vdom/vnode.ts | 1 + 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts index 907b795d797..b1116840ed5 100644 --- a/packages/server-renderer/src/render.ts +++ b/packages/server-renderer/src/render.ts @@ -206,6 +206,11 @@ function renderComponentInner(node, isRoot, context) { type: 'Component', prevActive }) + if (isDef(node.data) && isDef(node.data.directives)) { + childNode.data = childNode.data || {} + childNode.data.directives = node.data.directives + childNode.isComponentRootElement = true + } renderNode(childNode, isRoot, context) } @@ -372,7 +377,10 @@ function renderStartingTag(node: VNode, context) { if (dirRenderer) { // directives mutate the node's data // which then gets rendered by modules - dirRenderer(node, dirs[i]) + dirRenderer( + node.isComponentRootElement ? node.parent : node, + dirs[i] + ) } } } diff --git a/packages/server-renderer/test/ssr-string.spec.ts b/packages/server-renderer/test/ssr-string.spec.ts index 35810c8ed58..391671c1e52 100644 --- a/packages/server-renderer/test/ssr-string.spec.ts +++ b/packages/server-renderer/test/ssr-string.spec.ts @@ -1086,7 +1086,7 @@ describe('SSR: renderToString', () => { ) }) - it('custom directives', done => { + it('custom directives on raw element', done => { const renderer = createRenderer({ directives: { 'class-prefixer': (node, dir) => { @@ -1129,6 +1129,98 @@ describe('SSR: renderToString', () => { ) }) + it('custom directives on component', done => { + const Test = { + template: 'hello world' + } + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: + '

', + components: { Test } + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + + it('custom directives on element root of a component', done => { + const Test = { + template: + 'hello world' + } + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: '

', + components: { Test } + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + + it('custom directives on element with parent element', done => { + const renderer = createRenderer({ + directives: { + 'class-prefixer': (node, dir) => { + if (node.data.class) { + node.data.class = `${dir.value}-${node.data.class}` + } + if (node.data.staticClass) { + node.data.staticClass = `${dir.value}-${node.data.staticClass}` + } + } + } + }) + renderer.renderToString( + new Vue({ + template: + '

hello world

' + }), + (err, result) => { + expect(err).toBeNull() + expect(result).toContain( + '

hello world

' + ) + done() + } + ) + }) + it('should not warn for custom directives that do not have server-side implementation', done => { renderToString( new Vue({ diff --git a/src/core/vdom/vnode.ts b/src/core/vdom/vnode.ts index 80f784167ba..3b57f9aca0c 100644 --- a/src/core/vdom/vnode.ts +++ b/src/core/vdom/vnode.ts @@ -33,6 +33,7 @@ export default class VNode { fnOptions?: ComponentOptions | null // for SSR caching devtoolsMeta?: Object | null // used to store functional render context for devtools fnScopeId?: string | null // functional scope id support + isComponentRootElement?: boolean | null // for SSR directives constructor( tag?: string,