]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
fix(ssr): avoid hydration mismatch warning for classes with different order
authorEvan You <yyx990803@gmail.com>
Wed, 20 Dec 2023 02:48:01 +0000 (10:48 +0800)
committerEvan You <yyx990803@gmail.com>
Wed, 20 Dec 2023 02:48:01 +0000 (10:48 +0800)
packages/runtime-core/__tests__/hydration.spec.ts
packages/runtime-core/src/hydration.ts

index fa069b925309087c48200cfa4c645264c95b1640..6bbe4118295c7575343eef21509e2878ca9ba6c3 100644 (file)
@@ -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' })
index 57ced5118a63896f010e60dacae73ecd9750e89e..d42a9952f25145aec1128374399961dc8e00fb66 100644 (file)
@@ -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
+}