Ver código fonte

perf(runtime-vapor): skip unchanged primitive dynamic props

daiwei 1 mês atrás
pai
commit
0824a02897

+ 36 - 1
packages/runtime-vapor/__tests__/dom/prop.spec.ts

@@ -13,7 +13,11 @@ import {
   setValue,
 } from '../../src/dom/prop'
 import { setStyle } from '../../src/dom/prop'
-import { VaporComponentInstance, createComponent } from '../../src/component'
+import {
+  VaporComponentInstance,
+  applyFallthroughProps,
+  createComponent,
+} from '../../src/component'
 import { ref, setCurrentInstance, svgNS, xlinkNS } from '@vue/runtime-dom'
 import { makeRender } from '../_utils'
 import {
@@ -546,6 +550,37 @@ describe('patchProp', () => {
       expect(el.getAttribute('bar')).toBe('val')
       expect(el.getAttribute('foo')).toBeNull()
     })
+
+    test('should skip unchanged primitive dynamic props', () => {
+      const el = document.createElement('div')
+      let directSetCount = 0
+      let fallthroughSetCount = 0
+
+      Object.defineProperty(el, 'foo', {
+        set() {
+          directSetCount++
+        },
+      })
+      Object.defineProperty(el, 'bar', {
+        set() {
+          fallthroughSetCount++
+        },
+      })
+
+      setDynamicProps(el, [{ ['.foo']: 'val' }])
+      setDynamicProps(el, [{ ['.foo']: 'val' }])
+      expect(directSetCount).toBe(1)
+
+      setDynamicProps(el, [{ ['.foo']: 'next' }])
+      expect(directSetCount).toBe(2)
+
+      applyFallthroughProps(el, { ['.bar']: 'fallthrough' })
+      applyFallthroughProps(el, { ['.bar']: 'fallthrough' })
+      expect(fallthroughSetCount).toBe(1)
+
+      applyFallthroughProps(el, { ['.bar']: 'next' })
+      expect(fallthroughSetCount).toBe(2)
+    })
   })
 
   describe('setText', () => {

+ 21 - 5
packages/runtime-vapor/src/dom/prop.ts

@@ -474,19 +474,35 @@ function setHtmlToBlock(block: Block, value: any): void {
 export function setDynamicProps(el: any, args: any[], isSVG?: boolean): void {
   const props = args.length > 1 ? mergeProps(...args) : args[0]
   const cacheKey = `$dprops${isApplyingFallthroughProps ? '$' : ''}`
-  const prevKeys = el[cacheKey] as string[]
+  const prevProps = el[cacheKey] as Record<string, any> | undefined
+  const nextProps: Record<string, any> = Object.create(null)
 
-  if (prevKeys) {
-    for (const key of prevKeys) {
+  if (prevProps) {
+    for (const key in prevProps) {
       if (!(key in props)) {
         setDynamicProp(el, key, null, isSVG)
       }
     }
   }
 
-  for (const key of (el[cacheKey] = Object.keys(props))) {
-    setDynamicProp(el, key, props[key], isSVG)
+  for (const key of Object.keys(props)) {
+    const value = props[key]
+    nextProps[key] = value
+    // Events and objects can have stable identity with mutable internals, so
+    // only skip unchanged primitive values.
+    if (
+      prevProps &&
+      key in prevProps &&
+      !isOn(key) &&
+      (value == null || typeof value !== 'object') &&
+      Object.is(prevProps[key], value)
+    ) {
+      continue
+    }
+    setDynamicProp(el, key, value, isSVG)
   }
+
+  el[cacheKey] = nextProps
 }
 
 /**