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

feat(core): respect $stable slots flag per RFC

Evan You 6 лет назад
Родитель
Сommit
43097987cf

+ 45 - 1
packages/runtime-core/__tests__/component.spec.ts

@@ -1,3 +1,5 @@
+import { h, ref, render, nodeOps, nextTick } from '@vue/runtime-test'
+
 describe('renderer: component', () => {
 describe('renderer: component', () => {
   test.todo('should work')
   test.todo('should work')
 
 
@@ -7,5 +9,47 @@ describe('renderer: component', () => {
 
 
   test.todo('componentProps')
   test.todo('componentProps')
 
 
-  test.todo('componentSlots')
+  describe('slots', () => {
+    test('should respect $stable flag', async () => {
+      const flag1 = ref(1)
+      const flag2 = ref(2)
+      const spy = jest.fn()
+
+      const Child = () => {
+        spy()
+        return 'child'
+      }
+
+      const App = {
+        setup() {
+          return () => [
+            flag1.value,
+            h(
+              Child,
+              { n: flag2.value },
+              {
+                foo: () => 'foo',
+                $stable: true
+              }
+            )
+          ]
+        }
+      }
+
+      render(h(App), nodeOps.createElement('div'))
+      expect(spy).toHaveBeenCalledTimes(1)
+
+      // parent re-render, props didn't change, slots are stasble
+      // -> child should not update
+      flag1.value++
+      await nextTick()
+      expect(spy).toHaveBeenCalledTimes(1)
+
+      // parent re-render, props changed
+      // -> child should update
+      flag2.value++
+      await nextTick()
+      expect(spy).toHaveBeenCalledTimes(2)
+    })
+  })
 })
 })

+ 3 - 1
packages/runtime-core/src/componentRenderUtils.ts

@@ -138,7 +138,9 @@ export function shouldUpdateComponent(
     // this path is only taken by manually written render functions
     // this path is only taken by manually written render functions
     // so presence of any children leads to a forced update
     // so presence of any children leads to a forced update
     if (prevChildren != null || nextChildren != null) {
     if (prevChildren != null || nextChildren != null) {
-      return true
+      if (nextChildren == null || !(nextChildren as any).$stable) {
+        return true
+      }
     }
     }
     if (prevProps === nextProps) {
     if (prevProps === nextProps) {
       return false
       return false

+ 4 - 0
packages/runtime-core/src/componentSlots.ts

@@ -15,6 +15,9 @@ export type Slots = Readonly<InternalSlots>
 
 
 export type RawSlots = {
 export type RawSlots = {
   [name: string]: unknown
   [name: string]: unknown
+  // manual render fn hint to skip forced children updates
+  $stable?: boolean
+  // internal, indicates compiler generated slots = can skip normalization
   _compiled?: boolean
   _compiled?: boolean
 }
 }
 
 
@@ -49,6 +52,7 @@ export function resolveSlots(
     } else {
     } else {
       slots = {}
       slots = {}
       for (const key in rawSlots) {
       for (const key in rawSlots) {
+        if (key === '$stable') continue
         const value = rawSlots[key]
         const value = rawSlots[key]
         if (isFunction(value)) {
         if (isFunction(value)) {
           slots[key] = normalizeSlot(key, value)
           slots[key] = normalizeSlot(key, value)