diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
index 37d9d3147..f24f4c875 100644
--- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
+++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap
@@ -35,6 +35,21 @@ export function render(_ctx) {
}"
`;
+exports[`compiler: v-once > on component 1`] = `
+"import { resolveComponent as _resolveComponent, createComponent as _createComponent, insert as _insert, template as _template } from 'vue/vapor';
+const t0 = _template("
")
+
+export function render(_ctx) {
+ const _component_Comp = _resolveComponent("Comp")
+ const n1 = t0()
+ const n0 = _createComponent(_component_Comp, [
+ { id: () => (_ctx.foo) }
+ ], null, null, null, true)
+ _insert(n0, n1)
+ return n1
+}"
+`;
+
exports[`compiler: v-once > on nested plain element 1`] = `
"import { setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("")
@@ -47,6 +62,19 @@ export function render(_ctx) {
}"
`;
+exports[`compiler: v-once > with v-for 1`] = `
+"import { createFor as _createFor, template as _template } from 'vue/vapor';
+const t0 = _template("")
+
+export function render(_ctx) {
+ const n0 = _createFor(() => (_ctx.list), (_block) => {
+ const n2 = t0()
+ return [n2, () => {}]
+ }, null, null, null, true)
+ return n0
+}"
+`;
+
exports[`compiler: v-once > with v-if 1`] = `
"import { createIf as _createIf, template as _template } from 'vue/vapor';
const t0 = _template("")
diff --git a/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
index 34c58358c..19c306aaa 100644
--- a/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
+++ b/packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts
@@ -131,7 +131,25 @@ describe('compiler: v-once', () => {
])
})
- test.todo('on component')
+ test('on component', () => {
+ const { ir, code } = compileWithOnce(`
`)
+ expect(code).toMatchSnapshot()
+ expect(ir.block.effect).lengthOf(0)
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.CREATE_COMPONENT_NODE,
+ id: 0,
+ tag: 'Comp',
+ once: true,
+ },
+ {
+ type: IRNodeTypes.INSERT_NODE,
+ elements: [0],
+ parent: 1,
+ },
+ ])
+ })
+
test.todo('on slot outlet')
test('inside v-once', () => {
@@ -205,5 +223,16 @@ describe('compiler: v-once', () => {
])
})
- test.todo('with v-for')
+ test('with v-for', () => {
+ const { ir, code } = compileWithOnce(``)
+ expect(code).toMatchSnapshot()
+ expect(ir.block.effect).lengthOf(0)
+ expect(ir.block.operation).toMatchObject([
+ {
+ type: IRNodeTypes.FOR,
+ id: 0,
+ once: true,
+ },
+ ])
+ })
})
diff --git a/packages/compiler-vapor/src/generators/component.ts b/packages/compiler-vapor/src/generators/component.ts
index ea626505c..74602d70e 100644
--- a/packages/compiler-vapor/src/generators/component.ts
+++ b/packages/compiler-vapor/src/generators/component.ts
@@ -34,7 +34,7 @@ export function genCreateComponent(
const { vaporHelper } = context
const tag = genTag()
- const { root, slots, dynamicSlots } = oper
+ const { root, slots, dynamicSlots, once } = oper
const rawProps = genRawProps(oper.props, context)
return [
@@ -46,7 +46,8 @@ export function genCreateComponent(
rawProps,
slots && genSlots(slots, context),
dynamicSlots && genDynamicSlots(dynamicSlots, context),
- root && 'true',
+ root ? 'true' : false,
+ once && 'true',
),
...genDirectivesForElement(oper.id, context),
]
diff --git a/packages/compiler-vapor/src/generators/for.ts b/packages/compiler-vapor/src/generators/for.ts
index 96eef4067..738ef12e6 100644
--- a/packages/compiler-vapor/src/generators/for.ts
+++ b/packages/compiler-vapor/src/generators/for.ts
@@ -19,7 +19,7 @@ export function genFor(
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper, genEffects } = context
- const { source, value, key, index, render, keyProp } = oper
+ const { source, value, key, index, render, keyProp, once } = oper
const rawValue = value && value.content
const rawKey = key && key.content
@@ -67,11 +67,18 @@ export function genFor(
}
genEffects.pop()
-
return [
NEWLINE,
`const n${oper.id} = `,
- ...genCall(vaporHelper('createFor'), sourceExpr, blockFn, getKeyFn),
+ ...genCall(
+ vaporHelper('createFor'),
+ sourceExpr,
+ blockFn,
+ getKeyFn,
+ false, // todo: getMemo
+ false, // todo: hydrationNode
+ once && 'true',
+ ),
]
function genEffectInFor(effects: IREffect[]): CodeFragment[] {
diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts
index 27420d7f6..c86e7c2ea 100644
--- a/packages/compiler-vapor/src/ir.ts
+++ b/packages/compiler-vapor/src/ir.ts
@@ -82,6 +82,7 @@ export interface ForIRNode extends BaseIRNode {
index?: SimpleExpressionNode
keyProp?: SimpleExpressionNode
render: BlockIRNode
+ once: boolean
}
export interface IRProp extends Omit {
@@ -224,6 +225,7 @@ export interface CreateComponentIRNode extends BaseIRNode {
asset: boolean
root: boolean
+ once: boolean
}
export interface DeclareOldRefIRNode extends BaseIRNode {
diff --git a/packages/compiler-vapor/src/transforms/transformElement.ts b/packages/compiler-vapor/src/transforms/transformElement.ts
index f79102405..173cdb58f 100644
--- a/packages/compiler-vapor/src/transforms/transformElement.ts
+++ b/packages/compiler-vapor/src/transforms/transformElement.ts
@@ -106,6 +106,7 @@ function transformComponentElement(
root,
slots: context.slots,
dynamicSlots: context.dynamicSlots,
+ once: context.inVOnce,
})
context.slots = undefined
context.dynamicSlots = undefined
diff --git a/packages/compiler-vapor/src/transforms/vFor.ts b/packages/compiler-vapor/src/transforms/vFor.ts
index 2abcfea36..0b896ba0b 100644
--- a/packages/compiler-vapor/src/transforms/vFor.ts
+++ b/packages/compiler-vapor/src/transforms/vFor.ts
@@ -63,6 +63,7 @@ export function processFor(
index: index as SimpleExpressionNode | undefined,
keyProp: keyProperty,
render,
+ once: context.inVOnce,
})
}
}
diff --git a/packages/runtime-vapor/src/apiCreateComponent.ts b/packages/runtime-vapor/src/apiCreateComponent.ts
index cf282706b..64180729f 100644
--- a/packages/runtime-vapor/src/apiCreateComponent.ts
+++ b/packages/runtime-vapor/src/apiCreateComponent.ts
@@ -14,6 +14,7 @@ export function createComponent(
slots: Slots | null = null,
dynamicSlots: DynamicSlots | null = null,
singleRoot: boolean = false,
+ once: boolean = false,
) {
const current = currentInstance!
const instance = createComponentInstance(
@@ -21,6 +22,7 @@ export function createComponent(
singleRoot ? withAttrs(rawProps) : rawProps,
slots,
dynamicSlots,
+ once,
)
setupComponent(instance, singleRoot)
diff --git a/packages/runtime-vapor/src/apiCreateFor.ts b/packages/runtime-vapor/src/apiCreateFor.ts
index 362ac762c..e487e14ac 100644
--- a/packages/runtime-vapor/src/apiCreateFor.ts
+++ b/packages/runtime-vapor/src/apiCreateFor.ts
@@ -22,6 +22,7 @@ export const createFor = (
getKey?: (item: any, key: any, index?: number) => any,
getMemo?: (item: any, key: any, index?: number) => any[],
hydrationNode?: Node,
+ once?: boolean,
): Fragment => {
let isMounted = false
let oldBlocks: ForBlock[] = []
@@ -32,9 +33,12 @@ export const createFor = (
nodes: oldBlocks,
[fragmentKey]: true,
}
-
const update = getMemo ? updateWithMemo : updateWithoutMemo
- renderEffect(() => {
+ once ? renderList() : renderEffect(renderList)
+
+ return ref
+
+ function renderList() {
const source = src()
const newLength = getLength(source)
const oldLength = oldBlocks.length
@@ -213,9 +217,7 @@ export const createFor = (
}
ref.nodes = [(oldBlocks = newBlocks), parentAnchor]
- })
-
- return ref
+ }
function mount(
source: any,
diff --git a/packages/runtime-vapor/src/apiCreateVaporApp.ts b/packages/runtime-vapor/src/apiCreateVaporApp.ts
index 7a5392424..f317002b0 100644
--- a/packages/runtime-vapor/src/apiCreateVaporApp.ts
+++ b/packages/runtime-vapor/src/apiCreateVaporApp.ts
@@ -47,6 +47,7 @@ export function createVaporApp(
rootProps,
null,
null,
+ false,
context,
)
setupComponent(instance)
diff --git a/packages/runtime-vapor/src/component.ts b/packages/runtime-vapor/src/component.ts
index d435fc561..bb98adc85 100644
--- a/packages/runtime-vapor/src/component.ts
+++ b/packages/runtime-vapor/src/component.ts
@@ -261,6 +261,7 @@ export function createComponentInstance(
rawProps: RawProps | null,
slots: Slots | null,
dynamicSlots: DynamicSlots | null,
+ once: boolean = false,
// application root node only
appContext?: AppContext,
): ComponentInternalInstance {
@@ -354,7 +355,7 @@ export function createComponentInstance(
*/
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
}
- initProps(instance, rawProps, !isFunction(component))
+ initProps(instance, rawProps, !isFunction(component), once)
initSlots(instance, slots, dynamicSlots)
instance.emit = emit.bind(null, instance)
diff --git a/packages/runtime-vapor/src/componentProps.ts b/packages/runtime-vapor/src/componentProps.ts
index 363f66726..e8224ab6a 100644
--- a/packages/runtime-vapor/src/componentProps.ts
+++ b/packages/runtime-vapor/src/componentProps.ts
@@ -81,6 +81,7 @@ export function initProps(
instance: ComponentInternalInstance,
rawProps: RawProps,
isStateful: boolean,
+ once: boolean,
) {
if (!rawProps) rawProps = []
else if (!isArray(rawProps)) rawProps = [rawProps]
@@ -97,16 +98,16 @@ export function initProps(
for (const key in options) {
const getter = () =>
getDynamicPropValue(rawProps as NormalizedRawProps, key)
- registerProp(instance, props, key, getter, true)
+ registerProp(instance, once, props, key, getter, true)
}
} else {
const staticProps = rawProps[0] as StaticProps
for (const key in options) {
const rawKey = staticProps && getRawKey(staticProps, key)
if (rawKey) {
- registerProp(instance, props, key, staticProps[rawKey])
+ registerProp(instance, once, props, key, staticProps[rawKey])
} else {
- registerProp(instance, props, key, undefined, false, true)
+ registerProp(instance, once, props, key, undefined, false, true)
}
}
}
@@ -133,6 +134,7 @@ export function initProps(
function registerProp(
instance: ComponentInternalInstance,
+ once: boolean,
props: Data,
rawKey: string,
getter?: (() => unknown) | (() => DynamicPropResult),
@@ -158,10 +160,9 @@ function registerProp(
? () => withCast(getter!())
: getter!
- Object.defineProperty(props, key, {
- get,
- enumerable: true,
- })
+ const descriptor: PropertyDescriptor = once ? { value: get() } : { get }
+ descriptor.enumerable = true
+ Object.defineProperty(props, key, descriptor)
}
}