소스 검색

fix(ssr): avoid hydration mismatch warning for classes with different order

Evan You 2 년 전
부모
커밋
e585b0db43
2개의 변경된 파일29개의 추가작업 그리고 3개의 파일을 삭제
  1. 8 0
      packages/runtime-core/__tests__/hydration.spec.ts
  2. 21 3
      packages/runtime-core/src/hydration.ts

+ 8 - 0
packages/runtime-core/__tests__/hydration.spec.ts

@@ -1406,6 +1406,14 @@ describe('SSR hydration', () => {
       mountWithHydration(`<div class="foo bar"></div>`, () =>
         h('div', { class: 'foo bar' })
       )
+      // SVG classes
+      mountWithHydration(`<svg class="foo bar"></svg>`, () =>
+        h('svg', { class: 'foo bar' })
+      )
+      // class with different order
+      mountWithHydration(`<div class="foo bar"></svg>`, () =>
+        h('div', { class: 'bar foo' })
+      )
       expect(`Hydration class mismatch`).not.toHaveBeenWarned()
       mountWithHydration(`<div class="foo bar"></div>`, () =>
         h('div', { class: 'foo' })

+ 21 - 3
packages/runtime-core/src/hydration.ts

@@ -718,9 +718,11 @@ function propHasMismatch(el: Element, key: string, clientValue: any): boolean {
   let actual: any
   let expected: any
   if (key === 'class') {
-    actual = el.getAttribute('class')
-    expected = normalizeClass(clientValue)
-    if (actual !== expected) {
+    // classes might be in different order, but that doesn't affect cascade
+    // so we just need to check if the class lists contain the same classes.
+    actual = toClassSet(el.getAttribute('class') || '')
+    expected = toClassSet(normalizeClass(clientValue))
+    if (!isSetEqual(actual, expected)) {
       mismatchType = mismatchKey = `class`
     }
   } else if (key === 'style') {
@@ -765,3 +767,19 @@ function propHasMismatch(el: Element, key: string, clientValue: any): boolean {
   }
   return false
 }
+
+function toClassSet(str: string): Set<string> {
+  return new Set(str.trim().split(/\s+/))
+}
+
+function isSetEqual(a: Set<string>, b: Set<string>): boolean {
+  if (a.size !== b.size) {
+    return false
+  }
+  for (const s of a) {
+    if (!b.has(s)) {
+      return false
+    }
+  }
+  return true
+}