Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.

Commit cbd9a1b

Browse files
committed
refactor: bindings should be merged
1 parent 9ebc7d5 commit cbd9a1b

File tree

3 files changed

+50
-47
lines changed

3 files changed

+50
-47
lines changed

packages/runtime-vapor/__tests__/componentSlots.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,31 @@ describe('component: slots', () => {
348348
expect(host.innerHTML).toBe('<div><h1>header</h1></div>')
349349
})
350350

351+
test('slot class binding should be merged', async () => {
352+
let props: any
353+
354+
const enable = ref(true)
355+
const Comp = defineComponent(() =>
356+
createSlot('default', [
357+
{ class: () => 'foo' },
358+
() => ({ class: ['baz', 'qux'] }),
359+
{ class: () => ({ bar: enable.value }) },
360+
]),
361+
)
362+
define(() =>
363+
createComponent(
364+
Comp,
365+
{},
366+
{ default: _props => ((props = _props), []) },
367+
),
368+
).render()
369+
370+
expect(props).toEqual({ class: 'foo baz qux bar' })
371+
enable.value = false
372+
await nextTick()
373+
expect(props).toEqual({ class: 'foo baz qux' })
374+
})
375+
351376
test('dynamic slot should be render correctly with binds', async () => {
352377
const Comp = defineComponent(() => {
353378
const n0 = template('<div></div>')()

packages/runtime-vapor/src/componentSlots.ts

Lines changed: 24 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,8 @@
1-
import {
2-
type IfAny,
3-
isArray,
4-
isFunction,
5-
isOn,
6-
normalizeClass,
7-
normalizeStyle,
8-
} from '@vue/shared'
1+
import { type IfAny, isArray, isFunction } from '@vue/shared'
92
import {
103
type EffectScope,
114
effectScope,
125
isReactive,
13-
pauseTracking,
14-
resetTracking,
156
shallowReactive,
167
} from '@vue/reactivity'
178
import {
@@ -25,6 +16,7 @@ import { createComment, createTextNode, insert, remove } from './dom/element'
2516
import { VaporErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
2617
import type { NormalizedRawProps } from './componentProps'
2718
import type { Data } from '@vue/runtime-shared'
19+
import { mergeProps } from './dom/prop'
2820

2921
// TODO: SSR
3022

@@ -176,58 +168,44 @@ export function createSlot(
176168

177169
function normalizeSlotProps(rawPropsList: NormalizedRawProps) {
178170
const ret = shallowReactive<Data>({})
171+
const { length } = rawPropsList
172+
const isNeedToMerge = length > 1
173+
const dataCache = isNeedToMerge ? shallowReactive<Data[]>([]) : undefined
179174

180-
for (let i = 0; i < rawPropsList.length; i++) {
175+
for (let i = 0; i < length; i++) {
181176
const rawProps = rawPropsList[i]
182177
if (isFunction(rawProps)) {
183178
renderEffect(() => {
184179
const props = rawProps()
185-
for (const key in props) {
186-
setValue(key, props[key])
180+
if (isNeedToMerge) {
181+
dataCache![i] = props
182+
} else {
183+
for (const key in props) {
184+
ret[key] = props[key]
185+
}
187186
}
188187
})
189188
} else {
189+
const itemRet = isNeedToMerge
190+
? (dataCache![i] = shallowReactive<Data>({}))
191+
: ret
190192
for (const key in rawProps) {
191193
const valueSource = rawProps[key]
192194
renderEffect(() => {
193-
setValue(key, valueSource())
195+
itemRet[key] = valueSource()
194196
})
195197
}
196198
}
197199
}
198-
return ret
199-
200-
// In multiple effects, get and set the same reactive may cause stack overflow.
201-
// So we need to pause tracking before get and reset tracking after set.
202-
function getValue(key: string) {
203-
pauseTracking()
204-
const value = ret[key]
205-
resetTracking()
206-
return value
207-
}
208200

209-
function setValue(key: string, value: unknown) {
210-
if (key === 'class') {
211-
const existing = getValue('class')
212-
if (existing !== value) {
213-
ret.class = normalizeClass([existing, value])
201+
if (isNeedToMerge) {
202+
renderEffect(() => {
203+
const props = mergeProps(...dataCache!)
204+
for (const key in props) {
205+
ret[key] = props[key]
214206
}
215-
} else if (key === 'style') {
216-
ret.style = normalizeStyle([getValue('class'), value])
217-
} else if (isOn(key)) {
218-
const existing = getValue(key)
219-
const incoming = value
220-
if (
221-
incoming &&
222-
existing !== incoming &&
223-
!(isArray(existing) && existing.includes(incoming))
224-
) {
225-
ret[key] = existing
226-
? [].concat(existing as any, incoming as any)
227-
: incoming
228-
}
229-
} else if (key !== '') {
230-
ret[key] = value
231-
}
207+
})
232208
}
209+
210+
return ret
233211
}

packages/runtime-vapor/src/dom/prop.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ export function setDynamicProps(el: Element, ...args: any) {
154154
}
155155

156156
// TODO copied from runtime-core
157-
function mergeProps(...args: Data[]) {
157+
export function mergeProps(...args: Data[]) {
158158
const ret: Data = {}
159159
for (let i = 0; i < args.length; i++) {
160160
const toMerge = args[i]

0 commit comments

Comments
 (0)