Преглед изворни кода

fix(runtime-vapor): respect allowed prop mismatches during hydration (#14833)

edison пре 4 недеља
родитељ
комит
608b71ec71

+ 81 - 16
packages/runtime-vapor/__tests__/hydration.spec.ts

@@ -7726,34 +7726,99 @@ describe('data-allow-mismatch', () => {
   //   expect(`Hydration node mismatch`).not.toHaveBeenWarned()
   // })
   test('class mismatch', async () => {
-    await mountWithHydration(
-      `<div class="foo bar" data-allow-mismatch="class"></div>`,
-      `<div :class="data"></div>`,
-      ref('foo'),
+    const data = ref('foo')
+    const { container } = await mountWithHydration(
+      `<section><div class="bar" data-allow-mismatch="class"></div></section>`,
+      `<section><div :class="data"></div></section>`,
+      data,
+    )
+    expect(container.innerHTML).toBe(
+      '<section><div class="bar" data-allow-mismatch="class"></div></section>',
+    )
+
+    data.value = 'baz'
+    await nextTick()
+    expect(container.innerHTML).toBe(
+      '<section><div class="baz" data-allow-mismatch="class"></div></section>',
     )
     expect(`Hydration class mismatch`).not.toHaveBeenWarned()
   })
 
   test('style mismatch', async () => {
-    await mountWithHydration(
-      `<div style="color:red;" data-allow-mismatch="style"></div>`,
-      `<div :style="data"></div>`,
-      ref({ color: 'green' }),
+    const data = ref({ color: 'green' })
+    const { container } = await mountWithHydration(
+      `<section><div style="color:red;" data-allow-mismatch="style"></div></section>`,
+      `<section><div :style="data"></div></section>`,
+      data,
+    )
+    expect(container.innerHTML).toBe(
+      '<section><div style="color:red;" data-allow-mismatch="style"></div></section>',
+    )
+
+    data.value = { color: 'blue' }
+    await nextTick()
+    expect(container.innerHTML).toBe(
+      '<section><div style="color: blue;" data-allow-mismatch="style"></div></section>',
+    )
+    expect(`Hydration style mismatch`).not.toHaveBeenWarned()
+  })
+
+  test('style mismatch w/ v-show', async () => {
+    const data = ref(false)
+    const { container } = await mountWithHydration(
+      `<section><div style="color:red;" data-allow-mismatch="style"></div></section>`,
+      `<section><div v-show="data" style="color: red;"></div></section>`,
+      data,
+    )
+    expect(container.innerHTML).toBe(
+      '<section><div style="color:red;" data-allow-mismatch="style"></div></section>',
+    )
+
+    data.value = true
+    await nextTick()
+    expect(container.innerHTML).toBe(
+      '<section><div style="color:red;" data-allow-mismatch="style"></div></section>',
+    )
+
+    data.value = false
+    await nextTick()
+    expect(container.innerHTML).toBe(
+      '<section><div style="color: red; display: none;" data-allow-mismatch="style"></div></section>',
     )
     expect(`Hydration style mismatch`).not.toHaveBeenWarned()
   })
 
   test('attr mismatch', async () => {
-    await mountWithHydration(
-      `<div data-allow-mismatch="attribute"></div>`,
-      `<div :id="data"></div>`,
-      ref('foo'),
+    const missing = ref('foo')
+    const { container: missingContainer } = await mountWithHydration(
+      `<section><div data-allow-mismatch="attribute"></div></section>`,
+      `<section><div :id="data"></div></section>`,
+      missing,
+    )
+    expect(missingContainer.innerHTML).toBe(
+      '<section><div data-allow-mismatch="attribute"></div></section>',
     )
 
-    await mountWithHydration(
-      `<div id="bar" data-allow-mismatch="attribute"></div>`,
-      `<div :id="data"></div>`,
-      ref('foo'),
+    missing.value = 'baz'
+    await nextTick()
+    expect(missingContainer.innerHTML).toBe(
+      '<section><div data-allow-mismatch="attribute" id="baz"></div></section>',
+    )
+
+    const mismatched = ref('foo')
+    const { container: mismatchedContainer } = await mountWithHydration(
+      `<section><div id="bar" data-allow-mismatch="attribute"></div></section>`,
+      `<section><div :id="data"></div></section>`,
+      mismatched,
+    )
+    expect(mismatchedContainer.innerHTML).toBe(
+      '<section><div id="bar" data-allow-mismatch="attribute"></div></section>',
+    )
+
+    mismatched.value = 'baz'
+    await nextTick()
+    expect(mismatchedContainer.innerHTML).toBe(
+      '<section><div id="baz" data-allow-mismatch="attribute"></div></section>',
     )
 
     expect(`Hydration attribute mismatch`).not.toHaveBeenWarned()

+ 6 - 5
packages/runtime-vapor/src/directives/vShow.ts

@@ -111,17 +111,18 @@ function setDisplay(target: Block, value: unknown): void {
         isHydrating
       ) {
         if (!value && el.style.display !== 'none') {
-          warnPropMismatch(
+          const hasMismatch = warnPropMismatch(
             el,
             'style',
             MismatchTypes.STYLE,
             `display: ${el.style.display}`,
             'display: none',
           )
-          logMismatchError()
-
-          el.style.display = 'none'
-          el[vShowOriginalDisplay] = ''
+          if (hasMismatch) {
+            logMismatchError()
+            el.style.display = 'none'
+            el[vShowOriginalDisplay] = ''
+          }
         }
       } else {
         el.style.display = value ? el[vShowOriginalDisplay]! : 'none'

+ 14 - 9
packages/runtime-vapor/src/dom/prop.ts

@@ -645,9 +645,10 @@ function classHasMismatch(
   }
 
   if (hasMismatch) {
-    warnPropMismatch(el, 'class', MismatchTypes.CLASS, actual, expected)
-    logMismatchError()
-    return true
+    if (warnPropMismatch(el, 'class', MismatchTypes.CLASS, actual, expected)) {
+      logMismatchError()
+      return true
+    }
   }
 
   return false
@@ -688,9 +689,10 @@ function styleHasMismatch(
   }
 
   if (hasMismatch) {
-    warnPropMismatch(el, 'style', MismatchTypes.STYLE, actual, expected)
-    logMismatchError()
-    return true
+    if (warnPropMismatch(el, 'style', MismatchTypes.STYLE, actual, expected)) {
+      logMismatchError()
+      return true
+    }
   }
 
   return false
@@ -733,9 +735,12 @@ function attributeHasMismatch(el: any, key: string, value: any): boolean {
   if (isValidHtmlOrSvgAttribute(el, key)) {
     const { actual, expected } = getAttributeMismatch(el, key, value)
     if (actual !== expected) {
-      warnPropMismatch(el, key, MismatchTypes.ATTRIBUTE, actual, expected)
-      logMismatchError()
-      return true
+      if (
+        warnPropMismatch(el, key, MismatchTypes.ATTRIBUTE, actual, expected)
+      ) {
+        logMismatchError()
+        return true
+      }
     }
   }
   return false