Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(runtime-vapor): component slot #143

Merged
merged 44 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
d435acb
feat(runtime-vapor): component slot
Ubugeeei Mar 4, 2024
711645e
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 4, 2024
61ada62
chore: render fn arg
Ubugeeei Mar 4, 2024
f4768bc
chore playground
Ubugeeei Mar 4, 2024
0321374
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 10, 2024
ffee672
test(runtime-vapor): component slots (def test cases)
Ubugeeei Mar 10, 2024
246badc
test(runtime-vapor): component slots [wip]
Ubugeeei Mar 10, 2024
60edb08
chore: add comment
Ubugeeei Mar 17, 2024
e3bfd50
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 17, 2024
d690331
chore: merge refactoring of component
Ubugeeei Mar 17, 2024
e967ed1
chore: fix test
Ubugeeei Mar 17, 2024
fd2d9ff
feat(runtime-vapor): dynamic slots
Ubugeeei Mar 17, 2024
a5d9945
chore: fmt
Ubugeeei Mar 17, 2024
3e0d646
chore: slot playground
Ubugeeei Mar 17, 2024
ab91662
chore: remove dead comments
Ubugeeei Mar 17, 2024
0159af9
chore: remove unnecessary diffs
Ubugeeei Mar 17, 2024
1e00d6c
chore: add comments
Ubugeeei Mar 17, 2024
d83f04c
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 18, 2024
ac237ea
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 18, 2024
5609644
chore: renderWatch -> renderEffect
Ubugeeei Mar 18, 2024
34ca9f5
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 18, 2024
6a7957d
chore(runtime-vapor): remove slot arg of createVaporApp
Ubugeeei Mar 19, 2024
d12c2ac
chore(runtime-vapor): provide slots to setup context
Ubugeeei Mar 19, 2024
a259039
Merge remote-tracking branch 'origin/main' into ubugeeei/feat/compone…
sxzz Mar 22, 2024
9c52df2
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 22, 2024
463ae38
Merge branch 'ubugeeei/feat/component-slot' of github.com:vuejs/core-…
Ubugeeei Mar 22, 2024
9dfc966
refactor: tidy
sxzz Mar 22, 2024
9c122eb
refactor(runtime-vapor): CompiledSlotDescriptor -> DynamicSlot
Ubugeeei Mar 23, 2024
9479dba
refactor(runtime-vapor): renderEffect -> baseWatch
Ubugeeei Mar 23, 2024
a54617c
refactor(runtime-vapor): no export createSlots
Ubugeeei Mar 23, 2024
082f6a1
refactor(runtime-vapor): create component slots
Ubugeeei Mar 23, 2024
4d3a8e8
Merge branch 'ubugeeei/feat/component-slot' of github.com:vuejs/core-…
Ubugeeei Mar 23, 2024
cf18747
chore(runtime-vapor): component slots tests
Ubugeeei Mar 23, 2024
38af9aa
feat(runtime-vapor): set dynamic slots updation scheduler
Ubugeeei Mar 23, 2024
13cc597
refactor(runtime-vapor): dynamic slots
Ubugeeei Mar 23, 2024
f1f8b42
chore(runtime-vapor): remove dead test
Ubugeeei Mar 23, 2024
35feb3c
Merge branch 'main' of github.com:vuejs/core-vapor into ubugeeei/feat…
Ubugeeei Mar 23, 2024
e04e00a
chore: remove tracking $slots
Ubugeeei Mar 23, 2024
f6afe50
refactor: for -> extend
Ubugeeei Mar 23, 2024
a8a7e47
chore: typo (DinamicSlots -> DynamicSlots)
Ubugeeei Mar 23, 2024
08cc2bc
chore: remove dead codes
Ubugeeei Mar 23, 2024
c633534
chore(runtime-vapor): make slotsProxy optional
Ubugeeei Mar 23, 2024
5b76208
chore(runtime-vapor): make attrsProxy optional
Ubugeeei Mar 23, 2024
9510748
chore: update
sxzz Mar 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/compiler-vapor/src/generators/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
import { genExpression } from './expression'
import { genPropKey } from './prop'

// TODO: generate component slots
export function genCreateComponent(
oper: CreateComponentIRNode,
context: CodegenContext,
Expand Down
1 change: 0 additions & 1 deletion packages/runtime-vapor/__tests__/apiInject.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
createComponent,
createTextNode,
createVaporApp,
getCurrentInstance,
hasInjectionContext,
inject,
nextTick,
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime-vapor/__tests__/componentAttrs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ describe('attribute fallthrough', () => {
id: () => _ctx.id,
},
],
null,
null,
true,
)
},
Expand Down Expand Up @@ -85,6 +87,8 @@ describe('attribute fallthrough', () => {
id: () => _ctx.id,
},
],
null,
null,
true,
)
},
Expand Down Expand Up @@ -123,6 +127,8 @@ describe('attribute fallthrough', () => {
'custom-attr': () => 'custom-attr',
},
],
null,
null,
true,
)
return n0
Expand All @@ -144,6 +150,8 @@ describe('attribute fallthrough', () => {
id: () => _ctx.id,
},
],
null,
null,
true,
)
},
Expand Down
2 changes: 2 additions & 0 deletions packages/runtime-vapor/__tests__/componentProps.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,8 @@ describe('component: props', () => {
foo: () => _ctx.foo,
id: () => _ctx.id,
},
null,
null,
true,
)
},
Expand Down
191 changes: 191 additions & 0 deletions packages/runtime-vapor/__tests__/componentSlots.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
// NOTE: This test is implemented based on the case of `runtime-core/__test__/componentSlots.spec.ts`.

import {
createComponent,
createVaporApp,
defineComponent,
getCurrentInstance,
nextTick,
ref,
template,
} from '../src'
import { makeRender } from './_utils'

const define = makeRender<any>()
function renderWithSlots(slots: any): any {
let instance: any
const Comp = defineComponent({
render() {
const t0 = template('<div></div>')
const n0 = t0()
instance = getCurrentInstance()
return n0
},
})

const { render } = define({
render() {
return createComponent(Comp, {}, slots)
},
})

render()
return instance
}

describe('component: slots', () => {
test('initSlots: instance.slots should be set correctly', () => {
const { slots } = renderWithSlots({ _: 1 })
expect(slots).toMatchObject({ _: 1 })
})

// NOTE: slot normalization is not supported
test.todo(
'initSlots: should normalize object slots (when value is null, string, array)',
() => {},
)
test.todo(
'initSlots: should normalize object slots (when value is function)',
() => {},
)

test('initSlots: instance.slots should be set correctly', () => {
let instance: any
const Comp = defineComponent({
render() {
const t0 = template('<div></div>')
const n0 = t0()
instance = getCurrentInstance()
return n0
},
})

const { render } = define({
render() {
return createComponent(Comp, {}, { header: () => template('header')() })
},
})

render()

expect(instance.slots.header()).toMatchObject(
document.createTextNode('header'),
)
})

// runtime-core's "initSlots: instance.slots should be set correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)"
test('initSlots: instance.slots should be set correctly', () => {
const { slots } = renderWithSlots({
default: () => template('<span></span>')(),
})

// expect(
// '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.',
// ).toHaveBeenWarned()

expect(slots.default()).toMatchObject(document.createElement('span'))
})

test('updateSlots: instance.slots should be updated correctly', async () => {
const flag1 = ref(true)

let instance: any
const Child = () => {
instance = getCurrentInstance()
return template('child')()
}

const { render } = define({
render() {
return createComponent(Child, {}, { _: 2 as any }, () => [
flag1.value
? { name: 'one', fn: () => template('<span></span>')() }
: { name: 'two', fn: () => template('<div></div>')() },
])
},
})

render()

expect(instance.slots).toHaveProperty('one')
expect(instance.slots).not.toHaveProperty('two')

flag1.value = false
await nextTick()

expect(instance.slots).not.toHaveProperty('one')
expect(instance.slots).toHaveProperty('two')
})

// NOTE: it is not supported
// test('updateSlots: instance.slots should be updated correctly (when slotType is null)', () => {})

// runtime-core's "updateSlots: instance.slots should be update correctly (when vnode.shapeFlag is not SLOTS_CHILDREN)"
test('updateSlots: instance.slots should be update correctly', async () => {
const flag1 = ref(true)

let instance: any
const Child = () => {
instance = getCurrentInstance()
return template('child')()
}

const { render } = define({
setup() {
return createComponent(Child, {}, {}, () => [
flag1.value
? [{ name: 'header', fn: () => template('header')() }]
: [{ name: 'footer', fn: () => template('footer')() }],
])
},
})
render()

expect(instance.slots).toHaveProperty('header')
flag1.value = false
await nextTick()

// expect(
// '[Vue warn]: Non-function value encountered for default slot. Prefer function slots for better performance.',
// ).toHaveBeenWarned()

expect(instance.slots).toHaveProperty('footer')
})

test.todo('should respect $stable flag', async () => {
// TODO: $stable flag?
})

test.todo('should not warn when mounting another app in setup', () => {
// TODO: warning
const Comp = defineComponent({
render() {
const i = getCurrentInstance()
return i!.slots.default!()
},
})
const mountComp = () => {
createVaporApp({
render() {
return createComponent(
Comp,
{},
{ default: () => template('msg')() },
)!
},
})
}
const App = {
setup() {
mountComp()
},
render() {
return null!
},
}
createVaporApp(App).mount(document.createElement('div'))
expect(
'Slot "default" invoked outside of the render function',
).not.toHaveBeenWarned()
})
})
5 changes: 5 additions & 0 deletions packages/runtime-vapor/src/apiCreateComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,22 @@ import {
} from './component'
import { setupComponent } from './apiRender'
import type { RawProps } from './componentProps'
import type { DynamicSlots, Slots } from './componentSlots'
import { withAttrs } from './componentAttrs'

export function createComponent(
comp: Component,
rawProps: RawProps | null = null,
slots: Slots | null = null,
dynamicSlots: DynamicSlots | null = null,
singleRoot: boolean = false,
) {
const current = currentInstance!
const instance = createComponentInstance(
comp,
singleRoot ? withAttrs(rawProps) : rawProps,
slots,
dynamicSlots,
)
setupComponent(instance, singleRoot)

Expand Down
8 changes: 7 additions & 1 deletion packages/runtime-vapor/src/apiCreateVaporApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ export function createVaporApp(

mount(rootContainer): any {
if (!instance) {
instance = createComponentInstance(rootComponent, rootProps, context)
instance = createComponentInstance(
rootComponent,
rootProps,
null,
null,
context,
)
setupComponent(instance)
render(instance, rootContainer)
return instance
Expand Down
36 changes: 32 additions & 4 deletions packages/runtime-vapor/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ import {
emit,
normalizeEmitsOptions,
} from './componentEmits'
import {
type DynamicSlots,
type InternalSlots,
type Slots,
initSlots,
} from './componentSlots'
import { VaporLifecycleHooks } from './apiLifecycle'
import { warn } from './warning'
import { type AppContext, createAppContext } from './apiCreateVaporApp'
Expand All @@ -32,7 +38,7 @@ export type SetupContext<E = EmitsOptions> = E extends any
attrs: Data
emit: EmitFn<E>
expose: (exposed?: Record<string, any>) => void
// TODO slots
slots: Readonly<InternalSlots>
}
: never

Expand All @@ -46,6 +52,9 @@ export function createSetupContext(
get attrs() {
return getAttrsProxy(instance)
},
get slots() {
return getSlotsProxy(instance)
},
get emit() {
return (event: string, ...args: any[]) => instance.emit(event, ...args)
},
Expand All @@ -57,6 +66,7 @@ export function createSetupContext(
return getAttrsProxy(instance)
},
emit: instance.emit,
slots: instance.slots,
expose: NOOP,
}
}
Expand Down Expand Up @@ -102,9 +112,11 @@ export interface ComponentInternalInstance {
emit: EmitFn
emitted: Record<string, boolean> | null
attrs: Data
slots: InternalSlots
refs: Data

attrsProxy: Data | null
attrsProxy?: Data
slotsProxy?: Slots

// lifecycle
isMounted: boolean
Expand Down Expand Up @@ -188,6 +200,8 @@ let uid = 0
export function createComponentInstance(
component: ObjectComponent | FunctionalComponent,
rawProps: RawProps | null,
slots: Slots | null = null,
dynamicSlots: DynamicSlots | null = null,
// application root node only
appContext: AppContext | null = null,
): ComponentInternalInstance {
Expand Down Expand Up @@ -224,10 +238,9 @@ export function createComponentInstance(
emit: null!,
emitted: null,
attrs: EMPTY_OBJ,
slots: EMPTY_OBJ,
refs: EMPTY_OBJ,

attrsProxy: null,

// lifecycle
isMounted: false,
isUnmounted: false,
Expand Down Expand Up @@ -283,6 +296,7 @@ export function createComponentInstance(
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
}
initProps(instance, rawProps, !isFunction(component))
initSlots(instance, slots, dynamicSlots)
instance.emit = emit.bind(null, instance)

return instance
Expand Down Expand Up @@ -315,3 +329,17 @@ function getAttrsProxy(instance: ComponentInternalInstance): Data {
))
)
}

/**
* Dev-only
*/
function getSlotsProxy(instance: ComponentInternalInstance): Slots {
return (
instance.slotsProxy ||
(instance.slotsProxy = new Proxy(instance.slots, {
get(target, key: string) {
return target[key]
},
}))
)
}
Loading
Loading