From c95db5de50cd3de40619112bfc1977dec8e7c379 Mon Sep 17 00:00:00 2001
From: Rairn <958414905@qq.com>
Date: Mon, 19 Sep 2022 22:43:17 +0800
Subject: [PATCH 1/3] test(runtime-core): the result of client render should be
the same as server render
---
packages/compiler-dom/src/index.ts | 7 +++++--
.../runtime-core/__tests__/hydration.spec.ts | 21 +++++++++++++++++++
2 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/packages/compiler-dom/src/index.ts b/packages/compiler-dom/src/index.ts
index 2c6f71cefbb..89c005d8e50 100644
--- a/packages/compiler-dom/src/index.ts
+++ b/packages/compiler-dom/src/index.ts
@@ -17,11 +17,14 @@ import { transformModel } from './transforms/vModel'
import { transformOn } from './transforms/vOn'
import { transformShow } from './transforms/vShow'
import { transformTransition } from './transforms/Transition'
-import { stringifyStatic } from './transforms/stringifyStatic'
+import {
+ stringifyStatic,
+ StringifyThresholds
+} from './transforms/stringifyStatic'
import { ignoreSideEffectTags } from './transforms/ignoreSideEffectTags'
import { extend } from '@vue/shared'
-export { parserOptions }
+export { parserOptions, StringifyThresholds }
export const DOMNodeTransforms: NodeTransform[] = [
transformStyle,
diff --git a/packages/runtime-core/__tests__/hydration.spec.ts b/packages/runtime-core/__tests__/hydration.spec.ts
index 17ffbb40092..551f5e6925a 100644
--- a/packages/runtime-core/__tests__/hydration.spec.ts
+++ b/packages/runtime-core/__tests__/hydration.spec.ts
@@ -17,6 +17,7 @@ import {
renderSlot
} from '@vue/runtime-dom'
import { renderToString, SSRContext } from '@vue/server-renderer'
+import { StringifyThresholds } from '@vue/compiler-dom'
import { PatchFlags } from '../../shared/src'
function mountWithHydration(html: string, render: () => any) {
@@ -545,6 +546,26 @@ describe('SSR hydration', () => {
expect(text.textContent).toBe('bye')
})
+ // #6637
+ test('the result of client render should be the same as server render', async () => {
+ const App = {
+ // the quantity must reach the threshold to be reproduce
+ template: `
`.repeat(StringifyThresholds.NODE_COUNT)
+ }
+ const container = document.createElement('div')
+
+ // server render
+ const serverSide = await renderToString(h(App))
+ container.innerHTML = serverSide
+
+ // client render
+ createSSRApp(App).mount(container)
+ const clientSide = container.innerHTML
+
+ expect(`Hydration node mismatch`).not.toHaveBeenWarned()
+ expect(serverSide).toBe(clientSide)
+ })
+
test('handle click error in ssr mode', async () => {
const App = {
setup() {
From 5790adf7b2f5ebe9bbad9bb96b9f59d468be7f0d Mon Sep 17 00:00:00 2001
From: Rairn <958414905@qq.com>
Date: Tue, 20 Sep 2022 04:44:16 +0800
Subject: [PATCH 2/3] fix(compiler-core): avoid discrepancies with server-side
rendering
---
packages/compiler-core/src/transform.ts | 59 +++++++++++--------------
packages/compiler-core/src/utils.ts | 36 +++++++++++++--
2 files changed, 60 insertions(+), 35 deletions(-)
diff --git a/packages/compiler-core/src/transform.ts b/packages/compiler-core/src/transform.ts
index 6397df92f3c..446fde609d4 100644
--- a/packages/compiler-core/src/transform.ts
+++ b/packages/compiler-core/src/transform.ts
@@ -15,7 +15,6 @@ import {
CacheExpression,
createCacheExpression,
TemplateLiteral,
- createVNodeCall,
ConstantTypes,
ArrayExpression
} from './ast'
@@ -23,8 +22,6 @@ import {
isString,
isArray,
NOOP,
- PatchFlags,
- PatchFlagNames,
EMPTY_OBJ,
capitalize,
camelize
@@ -32,11 +29,11 @@ import {
import { defaultOnError, defaultOnWarn } from './errors'
import {
TO_DISPLAY_STRING,
- FRAGMENT,
helperNameMap,
- CREATE_COMMENT
+ CREATE_COMMENT,
+ CREATE_STATIC
} from './runtimeHelpers'
-import { isVSlot, makeBlock } from './utils'
+import { isVSlot, makeBlock, makeFragmentBlock } from './utils'
import { hoistStatic, isSingleElementRoot } from './transforms/hoistStatic'
import { CompilerCompatOptions } from './compat/compatConfig'
@@ -122,6 +119,20 @@ export interface TransformContext
filters?: Set
}
+const prefix = '_hoisted_'
+const isSingleHoistStaticRoot = (
+ root: RootNode,
+ context: TransformContext
+): boolean => {
+ const { children } = root
+ const { hoists } = context
+ return (
+ children.length === 1 &&
+ (children[0] as any).codegenNode?.content?.startsWith(prefix) &&
+ (hoists[0] as any)?.callee === CREATE_STATIC
+ )
+}
+
export function createTransformContext(
root: RootNode,
{
@@ -282,7 +293,7 @@ export function createTransformContext(
if (isString(exp)) exp = createSimpleExpression(exp)
context.hoists.push(exp)
const identifier = createSimpleExpression(
- `_hoisted_${context.hoists.length}`,
+ `${prefix}${context.hoists.length}`,
false,
exp.loc,
ConstantTypes.CAN_HOIST
@@ -338,12 +349,18 @@ export function transform(root: RootNode, options: TransformOptions) {
}
function createRootCodegen(root: RootNode, context: TransformContext) {
- const { helper } = context
const { children } = root
if (children.length === 1) {
const child = children[0]
+ // #6637
+ if (isSingleHoistStaticRoot(root, context)) {
+ // when the root is only one child and it is a static node,
+ // we need to return a fragment block to keep in line with
+ // the server-side rendering behavior to prevent warning when hydrating
+ makeFragmentBlock(root, context)
+ }
// if the single child is an element, turn it into a block.
- if (isSingleElementRoot(root, child) && child.codegenNode) {
+ else if (isSingleElementRoot(root, child) && child.codegenNode) {
// single element root is never hoisted so codegenNode will never be
// SimpleExpressionNode
const codegenNode = child.codegenNode
@@ -359,29 +376,7 @@ function createRootCodegen(root: RootNode, context: TransformContext) {
}
} else if (children.length > 1) {
// root has multiple nodes - return a fragment block.
- let patchFlag = PatchFlags.STABLE_FRAGMENT
- let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
- // check if the fragment actually contains a single valid child with
- // the rest being comments
- if (
- __DEV__ &&
- children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
- ) {
- patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
- patchFlagText += `, ${PatchFlagNames[PatchFlags.DEV_ROOT_FRAGMENT]}`
- }
- root.codegenNode = createVNodeCall(
- context,
- helper(FRAGMENT),
- undefined,
- root.children,
- patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``),
- undefined,
- undefined,
- true,
- undefined,
- false /* isComponent */
- )
+ makeFragmentBlock(root, context)
} else {
// no children = noop. codegen will return null.
}
diff --git a/packages/compiler-core/src/utils.ts b/packages/compiler-core/src/utils.ts
index c9e310fe089..1fd6069640b 100644
--- a/packages/compiler-core/src/utils.ts
+++ b/packages/compiler-core/src/utils.ts
@@ -23,7 +23,8 @@ import {
VNodeCall,
SimpleExpressionNode,
BlockCodegenNode,
- MemoExpression
+ MemoExpression,
+ createVNodeCall
} from './ast'
import { TransformContext } from './transform'
import {
@@ -40,9 +41,10 @@ import {
CREATE_VNODE,
CREATE_ELEMENT_VNODE,
WITH_MEMO,
- OPEN_BLOCK
+ OPEN_BLOCK,
+ FRAGMENT
} from './runtimeHelpers'
-import { isString, isObject, hyphenate, extend, NOOP } from '@vue/shared'
+import { isString, isObject, hyphenate, extend, NOOP, PatchFlags, PatchFlagNames } from '@vue/shared'
import { PropsExpression } from './transforms/transformElement'
import { parseExpression } from '@babel/parser'
import { Expression } from '@babel/types'
@@ -537,3 +539,31 @@ export function makeBlock(
helper(getVNodeBlockHelper(inSSR, node.isComponent))
}
}
+
+export function makeFragmentBlock(root: RootNode, context: TransformContext) {
+ const { helper } = context
+ const { children } = root
+ let patchFlag = PatchFlags.STABLE_FRAGMENT
+ let patchFlagText = PatchFlagNames[PatchFlags.STABLE_FRAGMENT]
+ // check if the fragment actually contains a single valid child with
+ // the rest being comments
+ if (
+ __DEV__ &&
+ children.filter(c => c.type !== NodeTypes.COMMENT).length === 1
+ ) {
+ patchFlag |= PatchFlags.DEV_ROOT_FRAGMENT
+ patchFlagText += `, ${PatchFlagNames[PatchFlags.DEV_ROOT_FRAGMENT]}`
+ }
+ root.codegenNode = createVNodeCall(
+ context,
+ helper(FRAGMENT),
+ undefined,
+ root.children,
+ patchFlag + (__DEV__ ? ` /* ${patchFlagText} */` : ``),
+ undefined,
+ undefined,
+ true,
+ undefined,
+ false
+ )
+}
From 3586d7b81701ba706f6a4a3a601d4f014f85593f Mon Sep 17 00:00:00 2001
From: Rairn <958414905@qq.com>
Date: Tue, 20 Sep 2022 04:55:04 +0800
Subject: [PATCH 3/3] test(compiler-dom): update snap
---
.../__snapshots__/stringifyStatic.spec.ts.snap | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap
index 8427b38fcec..8004cfbe5a9 100644
--- a/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap
+++ b/packages/compiler-dom/__tests__/transforms/__snapshots__/stringifyStatic.spec.ts.snap
@@ -34,21 +34,25 @@ return function render(_ctx, _cache) {
`;
exports[`stringify static html stringify v-html 1`] = `
-"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
+"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"show-it
12
\\", 2)
return function render(_ctx, _cache) {
- return _hoisted_1
+ return (_openBlock(), _createElementBlock(_Fragment, null, [
+ _hoisted_1
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
}"
`;
exports[`stringify static html stringify v-text 1`] = `
-"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode } = Vue
+"const { createElementVNode: _createElementVNode, createStaticVNode: _createStaticVNode, Fragment: _Fragment, openBlock: _openBlock, createElementBlock: _createElementBlock } = Vue
const _hoisted_1 = /*#__PURE__*/_createStaticVNode(\\"<span>show-it </span>
12
\\", 2)
return function render(_ctx, _cache) {
- return _hoisted_1
+ return (_openBlock(), _createElementBlock(_Fragment, null, [
+ _hoisted_1
+ ], 2112 /* STABLE_FRAGMENT, DEV_ROOT_FRAGMENT */))
}"
`;