Просмотр исходного кода

fix(runtime-core): fix beforeUpdate call timing to allow state mutation

fix #1899
Evan You 5 лет назад
Родитель
Сommit
1eb6067a85
2 измененных файлов с 54 добавлено и 16 удалено
  1. 30 0
      packages/runtime-core/__tests__/apiLifecycle.spec.ts
  2. 24 16
      packages/runtime-core/src/renderer.ts

+ 30 - 0
packages/runtime-core/__tests__/apiLifecycle.spec.ts

@@ -74,6 +74,36 @@ describe('api: lifecycle hooks', () => {
     count.value++
     count.value++
     await nextTick()
     await nextTick()
     expect(fn).toHaveBeenCalledTimes(1)
     expect(fn).toHaveBeenCalledTimes(1)
+    expect(serializeInner(root)).toBe(`<div>1</div>`)
+  })
+
+  it('state mutation in onBeforeUpdate', async () => {
+    const count = ref(0)
+    const root = nodeOps.createElement('div')
+    const fn = jest.fn(() => {
+      // should be called before inner div is updated
+      expect(serializeInner(root)).toBe(`<div>0</div>`)
+      count.value++
+    })
+    const renderSpy = jest.fn()
+
+    const Comp = {
+      setup() {
+        onBeforeUpdate(fn)
+        return () => {
+          renderSpy()
+          return h('div', count.value)
+        }
+      }
+    }
+    render(h(Comp), root)
+    expect(renderSpy).toHaveBeenCalledTimes(1)
+
+    count.value++
+    await nextTick()
+    expect(fn).toHaveBeenCalledTimes(1)
+    expect(renderSpy).toHaveBeenCalledTimes(2)
+    expect(serializeInner(root)).toBe(`<div>2</div>`)
   })
   })
 
 
   it('onUpdated', async () => {
   it('onUpdated', async () => {

+ 24 - 16
packages/runtime-core/src/renderer.ts

@@ -1281,13 +1281,7 @@ function baseCreateRenderer(
         let vnodeHook: VNodeHook | null | undefined
         let vnodeHook: VNodeHook | null | undefined
         const { el, props } = initialVNode
         const { el, props } = initialVNode
         const { bm, m, parent } = instance
         const { bm, m, parent } = instance
-        if (__DEV__) {
-          startMeasure(instance, `render`)
-        }
-        const subTree = (instance.subTree = renderComponentRoot(instance))
-        if (__DEV__) {
-          endMeasure(instance, `render`)
-        }
+
         // beforeMount hook
         // beforeMount hook
         if (bm) {
         if (bm) {
           invokeArrayFns(bm)
           invokeArrayFns(bm)
@@ -1296,6 +1290,16 @@ function baseCreateRenderer(
         if ((vnodeHook = props && props.onVnodeBeforeMount)) {
         if ((vnodeHook = props && props.onVnodeBeforeMount)) {
           invokeVNodeHook(vnodeHook, parent, initialVNode)
           invokeVNodeHook(vnodeHook, parent, initialVNode)
         }
         }
+
+        // render
+        if (__DEV__) {
+          startMeasure(instance, `render`)
+        }
+        const subTree = (instance.subTree = renderComponentRoot(instance))
+        if (__DEV__) {
+          endMeasure(instance, `render`)
+        }
+
         if (el && hydrateNode) {
         if (el && hydrateNode) {
           if (__DEV__) {
           if (__DEV__) {
             startMeasure(instance, `hydrate`)
             startMeasure(instance, `hydrate`)
@@ -1365,16 +1369,8 @@ function baseCreateRenderer(
         } else {
         } else {
           next = vnode
           next = vnode
         }
         }
-        if (__DEV__) {
-          startMeasure(instance, `render`)
-        }
-        const nextTree = renderComponentRoot(instance)
-        if (__DEV__) {
-          endMeasure(instance, `render`)
-        }
-        const prevTree = instance.subTree
-        instance.subTree = nextTree
         next.el = vnode.el
         next.el = vnode.el
+
         // beforeUpdate hook
         // beforeUpdate hook
         if (bu) {
         if (bu) {
           invokeArrayFns(bu)
           invokeArrayFns(bu)
@@ -1383,6 +1379,18 @@ function baseCreateRenderer(
         if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
         if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
           invokeVNodeHook(vnodeHook, parent, next, vnode)
           invokeVNodeHook(vnodeHook, parent, next, vnode)
         }
         }
+
+        // render
+        if (__DEV__) {
+          startMeasure(instance, `render`)
+        }
+        const nextTree = renderComponentRoot(instance)
+        if (__DEV__) {
+          endMeasure(instance, `render`)
+        }
+        const prevTree = instance.subTree
+        instance.subTree = nextTree
+
         // reset refs
         // reset refs
         // only needed if previous patch had refs
         // only needed if previous patch had refs
         if (instance.refs !== EMPTY_OBJ) {
         if (instance.refs !== EMPTY_OBJ) {