Skip to content

Commit a43090d

Browse files
edwardnycneiyichao03antfu
authored
fix: changing prop causes rerender to lose attributes #840 (#843)
Co-authored-by: neiyichao03 <[email protected]> Co-authored-by: Anthony Fu <[email protected]>
1 parent dc74c4d commit a43090d

File tree

4 files changed

+94
-9
lines changed

4 files changed

+94
-9
lines changed

src/mixin.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function mixin(Vue: VueConstructor) {
3434
updateTemplateRef(this)
3535
},
3636
beforeUpdate() {
37-
updateVmAttrs(this as ComponentInstance, this as SetupContext)
37+
updateVmAttrs(this as ComponentInstance)
3838
},
3939
updated(this: ComponentInstance) {
4040
updateTemplateRef(this)

src/utils/instance.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,22 @@ export function updateTemplateRef(vm: ComponentInstance) {
103103
vmStateManager.set(vm, 'refs', validNewKeys)
104104
}
105105

106-
export function updateVmAttrs(vm: ComponentInstance, ctx: SetupContext) {
107-
if (!vm || !ctx) {
106+
export function updateVmAttrs(vm: ComponentInstance, ctx?: SetupContext) {
107+
if (!vm) {
108108
return
109109
}
110110
let attrBindings = vmStateManager.get(vm, 'attrBindings')
111+
if (!attrBindings && !ctx) {
112+
// fix 840
113+
return
114+
}
111115
if (!attrBindings) {
112116
const observedData = reactive({})
113-
vmStateManager.set(vm, 'attrBindings', observedData)
114-
attrBindings = observedData
117+
attrBindings = { ctx: ctx!, data: observedData }
118+
vmStateManager.set(vm, 'attrBindings', attrBindings)
115119
proxy(ctx, 'attrs', {
116120
get: () => {
117-
return attrBindings
121+
return attrBindings?.data
118122
},
119123
set() {
120124
__DEV__ &&
@@ -128,8 +132,8 @@ export function updateVmAttrs(vm: ComponentInstance, ctx: SetupContext) {
128132

129133
const source = vm.$attrs
130134
for (const attr of Object.keys(source)) {
131-
if (!hasOwn(attrBindings!, attr)) {
132-
proxy(attrBindings, attr, {
135+
if (!hasOwn(attrBindings.data, attr)) {
136+
proxy(attrBindings.data, attr, {
133137
get: () => {
134138
// to ensure it always return the latest value
135139
return vm.$attrs[attr]

src/utils/vmStateManager.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import { ComponentInstance, Data } from '../component'
2+
import { SetupContext } from '../runtimeContext'
23

34
export interface VfaState {
45
refs?: string[]
56
rawBindings?: Data
6-
attrBindings?: Data
7+
attrBindings?: {
8+
ctx: SetupContext
9+
data: Data
10+
}
711
slots?: string[]
812
}
913

test/setup.spec.js

+77
Original file line numberDiff line numberDiff line change
@@ -1304,4 +1304,81 @@ describe('setup', () => {
13041304
lastName: 'xiao',
13051305
})
13061306
})
1307+
1308+
// #840
1309+
it('changing prop causes rerender to lose attributes', async () => {
1310+
let childAttrs = []
1311+
const Parent = {
1312+
computed: {
1313+
attrs() {
1314+
return {
1315+
'data-type': this.type,
1316+
}
1317+
},
1318+
},
1319+
props: {
1320+
type: {
1321+
type: String,
1322+
required: true,
1323+
},
1324+
},
1325+
mounted() {
1326+
childAttrs.push(this.attrs)
1327+
},
1328+
updated() {
1329+
childAttrs.push(this.attrs)
1330+
},
1331+
template: '<div v-bind="attrs">Parent<slot /></div>',
1332+
}
1333+
const Child = {
1334+
props: {
1335+
type: {
1336+
type: String,
1337+
required: true,
1338+
},
1339+
},
1340+
computed: {
1341+
attrs() {
1342+
return {
1343+
'data-type': this.type,
1344+
}
1345+
},
1346+
},
1347+
data() {
1348+
return {
1349+
update: 0,
1350+
}
1351+
},
1352+
template: ' <div v-bind="attrs">Child</div>',
1353+
}
1354+
1355+
const App = {
1356+
name: 'App',
1357+
data() {
1358+
return { parentType: 'parent' }
1359+
},
1360+
async mounted() {
1361+
await sleep(300)
1362+
this.parentType = 'parent-click'
1363+
},
1364+
1365+
components: {
1366+
Parent,
1367+
Child,
1368+
},
1369+
template: ` <div id="app">
1370+
<Parent :type="parentType">
1371+
<Child type="child"/>
1372+
</Parent>
1373+
</div>`,
1374+
}
1375+
const vm = new Vue(App).$mount()
1376+
1377+
await sleep(1000)
1378+
await vm.$nextTick()
1379+
expect(childAttrs).toStrictEqual([
1380+
{ 'data-type': 'parent' },
1381+
{ 'data-type': 'parent-click' },
1382+
])
1383+
})
13071384
})

0 commit comments

Comments
 (0)