瀏覽代碼

Optimize mutation observer to better handle move operations - ref #4450

Caleb Porzio 5 月之前
父節點
當前提交
8b6c31c4ef
共有 2 個文件被更改,包括 36 次插入14 次删除
  1. 19 11
      index.html
  2. 17 3
      packages/alpinejs/src/mutation.js

+ 19 - 11
index.html

@@ -11,17 +11,25 @@
     <!-- <script src="//cdn.tailwindcss.com"></script> -->
     <!-- <script src="//cdn.tailwindcss.com"></script> -->
 
-    <div x-data x-sort>
-        <div x-sort:item >foo</div>
-        <div >foo</div>
-        <div x-sort:item >foo</div>
-    </div>
+    <script>
+        let count = 0;
+        document.addEventListener("alpine:init", () => {
+            Alpine.directive("test", (el, _, { cleanup }) => {
+                el.textContent = "Initialized";
+                cleanup(() => {
+                    el.textContent = "Cleaned up";
+                });
+                // Alpine.nextTick(() => {
+                //     el.parentNode.replaceChildren(el);
+                // })
+            });
+        });
+    </script>
+    <div x-data>
+        <div x-ref="anchor"></div>
+
+        <div x-ref="target" class="bg-red-300 w-32 h-32" x-test></div>
 
-    <div x-data="{ val: true }"
-    >
-   <input type="text" x-model.boolean="val">
-   <input type="checkbox" x-model.boolean="val">
-   <input type="radio" name="foo" value="true" x-model.boolean="val">
-   <input type="radio" name="foo" value="false" x-model.boolean="val">
+        <button @click="$refs.anchor.before($refs.target)">Move</button>
     </div>
 </html>

+ 17 - 3
packages/alpinejs/src/mutation.js

@@ -112,6 +112,8 @@ export function flushAndStopDeferringMutations() {
 }
 
 function onMutate(mutations) {
+    console.log(mutations);
+
     if (isCollecting) {
         deferredMutations = deferredMutations.concat(mutations)
 
@@ -119,7 +121,7 @@ function onMutate(mutations) {
     }
 
     let addedNodes = []
-    let removedNodes = []
+    let removedNodes = new Set
     let addedAttributes = new Map
     let removedAttributes = new Map
 
@@ -129,14 +131,26 @@ function onMutate(mutations) {
         if (mutations[i].type === 'childList') {
             mutations[i].removedNodes.forEach(node => {
                 if (node.nodeType !== 1) return
+
+                // No need to process removed nodes that haven't been initialized by Alpine...
                 if (! node._x_marker) return
 
-                removedNodes.push(node)
+                removedNodes.add(node)
             })
 
             mutations[i].addedNodes.forEach(node => {
                 if (node.nodeType !== 1) return
 
+                // If the node is a removal as well, that means it's a "move" operation and we'll leave it alone...
+                if (removedNodes.has(node)) {
+                    removedNodes.delete(node)
+
+                    return
+                }
+
+                // If the node has already been initialized, we'll leave it alone...
+                if (node._x_marker) return;
+
                 addedNodes.push(node)
             })
         }
@@ -197,7 +211,7 @@ function onMutate(mutations) {
 
     for (let node of addedNodes) {
         if (! node.isConnected) continue
-        if (node._x_marker) return;
+
         onElAddeds.forEach(i => i(node))
     }