Caleb Porzio 3 년 전
부모
커밋
52f39abf46
3개의 변경된 파일60개의 추가작업 그리고 6개의 파일을 삭제
  1. 4 2
      packages/alpinejs/src/alpine.js
  2. 27 4
      packages/alpinejs/src/mutation.js
  3. 29 0
      tests/cypress/integration/mutation.spec.js

+ 4 - 2
packages/alpinejs/src/alpine.js

@@ -1,11 +1,11 @@
 import { setReactivityEngine, disableEffectScheduling, reactive, effect, release, raw } from './reactivity'
+import { mutateDom, deferMutations, flushAndStopDeferringMutations } from './mutation'
 import { mapAttributes, directive, setPrefix as prefix } from './directives'
-import { setEvaluator, evaluate, evaluateLater } from './evaluator'
 import { start, addRootSelector, closestRoot, initTree } from './lifecycle'
+import { setEvaluator, evaluate, evaluateLater } from './evaluator'
 import { interceptor } from './interceptor'
 import { debounce } from './utils/debounce'
 import { throttle } from './utils/throttle'
-import { mutateDom } from './mutation'
 import { nextTick } from './nextTick'
 import { plugin } from './plugin'
 import { magic } from './magics'
@@ -19,9 +19,11 @@ let Alpine = {
     get effect() { return effect },
     get raw() { return raw },
     version: ALPINE_VERSION,
+    flushAndStopDeferringMutations,
     disableEffectScheduling,
     setReactivityEngine,
     addRootSelector,
+    deferMutations,
     mapAttributes,
     evaluateLater,
     setEvaluator,

+ 27 - 4
packages/alpinejs/src/mutation.js

@@ -25,9 +25,11 @@ export function cleanupAttributes(el, names) {
     if (! el._x_attributeCleanups) return
 
     Object.entries(el._x_attributeCleanups).forEach(([name, value]) => {
-        (names === undefined || names.includes(name)) && value.forEach(i => i())
+        if (names === undefined || names.includes(name)) {
+            value.forEach(i => i())
 
-        delete el._x_attributeCleanups[name]
+            delete el._x_attributeCleanups[name]
+        }
     })
 }
 
@@ -42,6 +44,8 @@ export function startObservingMutations() {
 }
 
 export function stopObservingMutations() {
+    flushObserver()
+
     observer.disconnect()
 
     currentlyObserving = false
@@ -73,8 +77,6 @@ function processRecordQueue() {
 export function mutateDom(callback) {
     if (! currentlyObserving) return callback()
 
-    flushObserver()
-
     stopObservingMutations()
 
     let result = callback()
@@ -84,7 +86,28 @@ export function mutateDom(callback) {
     return result
 }
 
+let isCollecting = false
+let deferredMutations = []
+
+export function deferMutations() {
+    isCollecting = true
+}
+
+export function flushAndStopDeferringMutations() {
+    isCollecting = false
+
+    onMutate(deferredMutations)
+
+    deferredMutations = []
+}
+
 function onMutate(mutations) {
+    if (isCollecting) {
+        deferredMutations = deferredMutations.concat(mutations)
+
+        return
+    }
+
     let addedNodes = []
     let removedNodes = []
     let addedAttributes = new Map

+ 29 - 0
tests/cypress/integration/mutation.spec.js

@@ -81,3 +81,32 @@ test('can add new directive',
         get('span').should(haveText('bar'))
     }
 )
+
+test.only('can pause and queue mutations for later resuming/flushing',
+    html`
+        <div x-data="{ foo: 1 }">
+            <button x-on:click="setTimeout(() => foo++)" x-ref="btn">foo</button>
+            <h1 x-text="foo"></h1>
+
+            <a href="#" @click="$refs.btn.removeAttribute('x-on:click')" id="remove">remove</a>
+            <a href="#" @click="$refs.btn.setAttribute('x-on:click', 'foo++')" id="add">add</a>
+            <a href="#" @click="Alpine.deferMutations()" id="defer">add</a>
+            <a href="#" @click="Alpine.flushAndStopDeferringMutations()" id="flush">add</a>
+        </div>
+    `,
+    ({ get }) => {
+        get('h1').should(haveText('1'))
+        get('button').click()
+        get('h1').should(haveText('2'))
+        get('#remove').click()
+        get('button').click()
+        get('h1').should(haveText('2'))
+        get('#defer').click()
+        get('#add').click()
+        get('button').click()
+        get('h1').should(haveText('2'))
+        get('#flush').click()
+        get('button').click()
+        get('h1').should(haveText('3'))
+    }
+)