diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
index 79e2867ad69..9c985379c1e 100644
--- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
+++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
@@ -20,6 +20,7 @@ import {
render,
withModifiers,
} from '@vue/runtime-dom'
+import { createApp } from 'vue'
import { PatchFlags } from '@vue/shared'
describe('attribute fallthrough', () => {
@@ -783,4 +784,31 @@ describe('attribute fallthrough', () => {
expect(textBar).toBe('from GrandChild')
expect(textFoo).toBe('from Child')
})
+
+ // covers uncaught regression #10710
+ it('should track this.$attrs access in slots', async () => {
+ const GrandChild = {
+ template: ``,
+ }
+ const Child = {
+ components: { GrandChild },
+ template: `
{{ $attrs.foo }}
`,
+ }
+
+ const obj = ref(1)
+ const App = {
+ render() {
+ return h(Child, { foo: obj.value })
+ },
+ }
+
+ const root = document.createElement('div')
+ createApp(App).mount(root)
+
+ expect(root.innerHTML).toBe('1
')
+
+ obj.value = 2
+ await nextTick()
+ expect(root.innerHTML).toBe('2
')
+ })
})
diff --git a/packages/runtime-core/src/componentPublicInstance.ts b/packages/runtime-core/src/componentPublicInstance.ts
index a1b45e4f9cc..b43accfa0a3 100644
--- a/packages/runtime-core/src/componentPublicInstance.ts
+++ b/packages/runtime-core/src/componentPublicInstance.ts
@@ -368,9 +368,10 @@ export const PublicInstanceProxyHandlers: ProxyHandler = {
// public $xxx properties
if (publicGetter) {
if (key === '$attrs') {
- track(instance, TrackOpTypes.GET, key)
+ track(instance.attrs, TrackOpTypes.GET, '')
__DEV__ && markAttrsAccessed()
} else if (__DEV__ && key === '$slots') {
+ // for HMR only
track(instance, TrackOpTypes.GET, key)
}
return publicGetter(instance)