Skip to content

Commit b776f92

Browse files
Jevon617sxzz
andauthored
feat: v-once for component and v-for (#201)
* feat: v-once with v-for / v-once for component * refactor * refactor --------- Co-authored-by: 三咲智子 Kevin Deng <[email protected]>
1 parent f5f1150 commit b776f92

File tree

12 files changed

+96
-20
lines changed

12 files changed

+96
-20
lines changed

packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ export function render(_ctx) {
3535
}"
3636
`;
3737

38+
exports[`compiler: v-once > on component 1`] = `
39+
"import { resolveComponent as _resolveComponent, createComponent as _createComponent, insert as _insert, template as _template } from 'vue/vapor';
40+
const t0 = _template("<div></div>")
41+
42+
export function render(_ctx) {
43+
const _component_Comp = _resolveComponent("Comp")
44+
const n1 = t0()
45+
const n0 = _createComponent(_component_Comp, [
46+
{ id: () => (_ctx.foo) }
47+
], null, null, null, true)
48+
_insert(n0, n1)
49+
return n1
50+
}"
51+
`;
52+
3853
exports[`compiler: v-once > on nested plain element 1`] = `
3954
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
4055
const t0 = _template("<div><div></div></div>")
@@ -47,6 +62,19 @@ export function render(_ctx) {
4762
}"
4863
`;
4964

65+
exports[`compiler: v-once > with v-for 1`] = `
66+
"import { createFor as _createFor, template as _template } from 'vue/vapor';
67+
const t0 = _template("<div></div>")
68+
69+
export function render(_ctx) {
70+
const n0 = _createFor(() => (_ctx.list), (_block) => {
71+
const n2 = t0()
72+
return [n2, () => {}]
73+
}, null, null, null, true)
74+
return n0
75+
}"
76+
`;
77+
5078
exports[`compiler: v-once > with v-if 1`] = `
5179
"import { createIf as _createIf, template as _template } from 'vue/vapor';
5280
const t0 = _template("<div></div>")

packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,25 @@ describe('compiler: v-once', () => {
131131
])
132132
})
133133

134-
test.todo('on component')
134+
test('on component', () => {
135+
const { ir, code } = compileWithOnce(`<div><Comp :id="foo" v-once /></div>`)
136+
expect(code).toMatchSnapshot()
137+
expect(ir.block.effect).lengthOf(0)
138+
expect(ir.block.operation).toMatchObject([
139+
{
140+
type: IRNodeTypes.CREATE_COMPONENT_NODE,
141+
id: 0,
142+
tag: 'Comp',
143+
once: true,
144+
},
145+
{
146+
type: IRNodeTypes.INSERT_NODE,
147+
elements: [0],
148+
parent: 1,
149+
},
150+
])
151+
})
152+
135153
test.todo('on slot outlet')
136154

137155
test('inside v-once', () => {
@@ -205,5 +223,16 @@ describe('compiler: v-once', () => {
205223
])
206224
})
207225

208-
test.todo('with v-for')
226+
test('with v-for', () => {
227+
const { ir, code } = compileWithOnce(`<div v-for="i in list" v-once />`)
228+
expect(code).toMatchSnapshot()
229+
expect(ir.block.effect).lengthOf(0)
230+
expect(ir.block.operation).toMatchObject([
231+
{
232+
type: IRNodeTypes.FOR,
233+
id: 0,
234+
once: true,
235+
},
236+
])
237+
})
209238
})

packages/compiler-vapor/src/generators/component.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export function genCreateComponent(
3434
const { vaporHelper } = context
3535

3636
const tag = genTag()
37-
const { root, slots, dynamicSlots } = oper
37+
const { root, slots, dynamicSlots, once } = oper
3838
const rawProps = genRawProps(oper.props, context)
3939

4040
return [
@@ -46,7 +46,8 @@ export function genCreateComponent(
4646
rawProps,
4747
slots && genSlots(slots, context),
4848
dynamicSlots && genDynamicSlots(dynamicSlots, context),
49-
root && 'true',
49+
root ? 'true' : false,
50+
once && 'true',
5051
),
5152
...genDirectivesForElement(oper.id, context),
5253
]

packages/compiler-vapor/src/generators/for.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function genFor(
1919
context: CodegenContext,
2020
): CodeFragment[] {
2121
const { vaporHelper, genEffects } = context
22-
const { source, value, key, index, render, keyProp } = oper
22+
const { source, value, key, index, render, keyProp, once } = oper
2323

2424
const rawValue = value && value.content
2525
const rawKey = key && key.content
@@ -67,11 +67,18 @@ export function genFor(
6767
}
6868

6969
genEffects.pop()
70-
7170
return [
7271
NEWLINE,
7372
`const n${oper.id} = `,
74-
...genCall(vaporHelper('createFor'), sourceExpr, blockFn, getKeyFn),
73+
...genCall(
74+
vaporHelper('createFor'),
75+
sourceExpr,
76+
blockFn,
77+
getKeyFn,
78+
false, // todo: getMemo
79+
false, // todo: hydrationNode
80+
once && 'true',
81+
),
7582
]
7683

7784
function genEffectInFor(effects: IREffect[]): CodeFragment[] {

packages/compiler-vapor/src/ir.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export interface ForIRNode extends BaseIRNode {
8282
index?: SimpleExpressionNode
8383
keyProp?: SimpleExpressionNode
8484
render: BlockIRNode
85+
once: boolean
8586
}
8687

8788
export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
@@ -224,6 +225,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
224225

225226
asset: boolean
226227
root: boolean
228+
once: boolean
227229
}
228230

229231
export interface DeclareOldRefIRNode extends BaseIRNode {

packages/compiler-vapor/src/transforms/transformElement.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ function transformComponentElement(
106106
root,
107107
slots: context.slots,
108108
dynamicSlots: context.dynamicSlots,
109+
once: context.inVOnce,
109110
})
110111
context.slots = undefined
111112
context.dynamicSlots = undefined

packages/compiler-vapor/src/transforms/vFor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export function processFor(
6363
index: index as SimpleExpressionNode | undefined,
6464
keyProp: keyProperty,
6565
render,
66+
once: context.inVOnce,
6667
})
6768
}
6869
}

packages/runtime-vapor/src/apiCreateComponent.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ export function createComponent(
1414
slots: Slots | null = null,
1515
dynamicSlots: DynamicSlots | null = null,
1616
singleRoot: boolean = false,
17+
once: boolean = false,
1718
) {
1819
const current = currentInstance!
1920
const instance = createComponentInstance(
2021
comp,
2122
singleRoot ? withAttrs(rawProps) : rawProps,
2223
slots,
2324
dynamicSlots,
25+
once,
2426
)
2527
setupComponent(instance, singleRoot)
2628

packages/runtime-vapor/src/apiCreateFor.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export const createFor = (
2222
getKey?: (item: any, key: any, index?: number) => any,
2323
getMemo?: (item: any, key: any, index?: number) => any[],
2424
hydrationNode?: Node,
25+
once?: boolean,
2526
): Fragment => {
2627
let isMounted = false
2728
let oldBlocks: ForBlock[] = []
@@ -32,9 +33,12 @@ export const createFor = (
3233
nodes: oldBlocks,
3334
[fragmentKey]: true,
3435
}
35-
3636
const update = getMemo ? updateWithMemo : updateWithoutMemo
37-
renderEffect(() => {
37+
once ? renderList() : renderEffect(renderList)
38+
39+
return ref
40+
41+
function renderList() {
3842
const source = src()
3943
const newLength = getLength(source)
4044
const oldLength = oldBlocks.length
@@ -213,9 +217,7 @@ export const createFor = (
213217
}
214218

215219
ref.nodes = [(oldBlocks = newBlocks), parentAnchor]
216-
})
217-
218-
return ref
220+
}
219221

220222
function mount(
221223
source: any,

packages/runtime-vapor/src/apiCreateVaporApp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export function createVaporApp(
4747
rootProps,
4848
null,
4949
null,
50+
false,
5051
context,
5152
)
5253
setupComponent(instance)

packages/runtime-vapor/src/component.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ export function createComponentInstance(
261261
rawProps: RawProps | null,
262262
slots: Slots | null,
263263
dynamicSlots: DynamicSlots | null,
264+
once: boolean = false,
264265
// application root node only
265266
appContext?: AppContext,
266267
): ComponentInternalInstance {
@@ -354,7 +355,7 @@ export function createComponentInstance(
354355
*/
355356
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
356357
}
357-
initProps(instance, rawProps, !isFunction(component))
358+
initProps(instance, rawProps, !isFunction(component), once)
358359
initSlots(instance, slots, dynamicSlots)
359360
instance.emit = emit.bind(null, instance)
360361

packages/runtime-vapor/src/componentProps.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ export function initProps(
8181
instance: ComponentInternalInstance,
8282
rawProps: RawProps,
8383
isStateful: boolean,
84+
once: boolean,
8485
) {
8586
if (!rawProps) rawProps = []
8687
else if (!isArray(rawProps)) rawProps = [rawProps]
@@ -97,16 +98,16 @@ export function initProps(
9798
for (const key in options) {
9899
const getter = () =>
99100
getDynamicPropValue(rawProps as NormalizedRawProps, key)
100-
registerProp(instance, props, key, getter, true)
101+
registerProp(instance, once, props, key, getter, true)
101102
}
102103
} else {
103104
const staticProps = rawProps[0] as StaticProps
104105
for (const key in options) {
105106
const rawKey = staticProps && getRawKey(staticProps, key)
106107
if (rawKey) {
107-
registerProp(instance, props, key, staticProps[rawKey])
108+
registerProp(instance, once, props, key, staticProps[rawKey])
108109
} else {
109-
registerProp(instance, props, key, undefined, false, true)
110+
registerProp(instance, once, props, key, undefined, false, true)
110111
}
111112
}
112113
}
@@ -133,6 +134,7 @@ export function initProps(
133134

134135
function registerProp(
135136
instance: ComponentInternalInstance,
137+
once: boolean,
136138
props: Data,
137139
rawKey: string,
138140
getter?: (() => unknown) | (() => DynamicPropResult),
@@ -158,10 +160,9 @@ function registerProp(
158160
? () => withCast(getter!())
159161
: getter!
160162

161-
Object.defineProperty(props, key, {
162-
get,
163-
enumerable: true,
164-
})
163+
const descriptor: PropertyDescriptor = once ? { value: get() } : { get }
164+
descriptor.enumerable = true
165+
Object.defineProperty(props, key, descriptor)
165166
}
166167
}
167168

0 commit comments

Comments
 (0)