Skip to content

Commit

Permalink
feat: implement v-once
Browse files Browse the repository at this point in the history
  • Loading branch information
Jevon617 committed Apr 30, 2024
1 parent fb58e65 commit c4ad155
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 11 deletions.
20 changes: 20 additions & 0 deletions packages/compiler-vapor/src/generators/once.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { CodegenContext } from '../generate'
import type { OnceIRNode } from '../ir'
import { genBlock } from './block'
import { type CodeFragment, NEWLINE, buildCodeFragment, genCall } from './utils'

export function genCreateOnce(
oper: OnceIRNode,
context: CodegenContext,
): CodeFragment[] {
const { vaporHelper } = context
const { block } = oper
const [frag, push] = buildCodeFragment()

let arg = genBlock(block, context)

push(NEWLINE, `const n${oper.id} = `)
push(...genCall(vaporHelper('createOnce'), arg))

return frag
}
3 changes: 3 additions & 0 deletions packages/compiler-vapor/src/generators/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
buildCodeFragment,
} from './utils'
import { genCreateComponent } from './component'
import { genCreateOnce } from './once'

export function genOperations(opers: OperationNode[], context: CodegenContext) {
const [frag, push] = buildCodeFragment()
Expand Down Expand Up @@ -59,6 +60,8 @@ export function genOperation(
return genFor(oper, context)
case IRNodeTypes.CREATE_COMPONENT_NODE:
return genCreateComponent(oper, context)
case IRNodeTypes.ONCE:
return genCreateOnce(oper, context)
}

return []
Expand Down
8 changes: 8 additions & 0 deletions packages/compiler-vapor/src/ir.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export enum IRNodeTypes {

IF,
FOR,
ONCE,
}

export interface BaseIRNode {
Expand Down Expand Up @@ -80,6 +81,12 @@ export interface ForIRNode extends BaseIRNode {
render: BlockIRNode
}

export interface OnceIRNode extends BaseIRNode {
type: IRNodeTypes.ONCE
id: number
block: BlockIRNode
}

export interface IRProp extends Omit<DirectiveTransformResult, 'value'> {
values: SimpleExpressionNode[]
}
Expand Down Expand Up @@ -211,6 +218,7 @@ export type OperationNode =
| IfIRNode
| ForIRNode
| CreateComponentIRNode
| OnceIRNode

export enum DynamicFlag {
NONE = 0,
Expand Down
50 changes: 40 additions & 10 deletions packages/compiler-vapor/src/transforms/vOnce.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
import { NodeTypes, findDir } from '@vue/compiler-dom'
import type { NodeTransform } from '../transform'
import { type ElementNode, NodeTypes, findDir } from '@vue/compiler-dom'
import {
type TransformContext,
createStructuralDirectiveTransform,
} from '../transform'
import { type BlockIRNode, DynamicFlag, IRNodeTypes } from '../ir'
import { newBlock, wrapTemplate } from './utils'

const seen = new WeakSet()
export const transformOnce = createStructuralDirectiveTransform(
['once'],
(node, _, context) => {
if (
node.type === NodeTypes.ELEMENT &&
findDir(node, 'once', true) &&
!context.inVOnce
) {
context.inVOnce = true

export const transformOnce: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
if (seen.has(node) || context.inVOnce /* || context.inSSR */) {
return
const id = context.reference()
context.dynamic.flags |= DynamicFlag.INSERT
const [block, onExit] = createNewBlock(node, context)

return () => {
onExit()
context.registerOperation({
type: IRNodeTypes.ONCE,
id,
block,
})
}
}
seen.add(node)
context.inVOnce = true
}
},
)

function createNewBlock(
node: ElementNode,
context: TransformContext<ElementNode>,
): [BlockIRNode, () => void] {
context.node = node = wrapTemplate(node, ['once'])
const block: BlockIRNode = newBlock(node)
const exitBlock = context.enterBlock(block)
context.reference()
return [block, exitBlock]
}
2 changes: 1 addition & 1 deletion packages/runtime-vapor/src/apiCreateIf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { type Block, type Fragment, fragmentKey } from './apiRender'
import { type EffectScope, effectScope } from '@vue/reactivity'
import { createComment, createTextNode, insert, remove } from './dom/element'

type BlockFn = () => Block
export type BlockFn = () => Block

/*! #__NO_SIDE_EFFECTS__ */
export const createIf = (
Expand Down
34 changes: 34 additions & 0 deletions packages/runtime-vapor/src/apiCreateOnce.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { type Block, type Fragment, fragmentKey } from './apiRender'
import { effectScope } from '@vue/reactivity'
import { createComment, createTextNode } from './dom/element'
import { type ComponentInternalInstance, isVaporComponent } from './component'
import { isArray } from '@vue/shared'
import type { BlockFn } from './apiCreateIf'

/*! #__NO_SIDE_EFFECTS__ */
export const createOnce = (blockFn: BlockFn): Fragment => {
let scope = effectScope()
const anchor = __DEV__ ? createComment('once') : createTextNode()
const fragment: Fragment = {
nodes: scope.run(() => {
const block = blockFn()
const components = getComponents(block)
components.forEach(comp => comp.scope.stop())
return block
})!,
anchor,
[fragmentKey]: true,
}
scope.stop()
return fragment
}

function getComponents(block: Block | null) {
let components: ComponentInternalInstance[] = []
if (isVaporComponent(block)) {
components.push(block, ...getComponents(block.block))
} else if (isArray(block)) {
block.forEach(child => components.push(...getComponents(child)))
}
return components
}
1 change: 1 addition & 0 deletions packages/runtime-vapor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ export {
export { createIf } from './apiCreateIf'
export { createFor } from './apiCreateFor'
export { createComponent } from './apiCreateComponent'
export { createOnce } from './apiCreateOnce'

export { resolveComponent, resolveDirective } from './helpers/resolveAssets'

Expand Down

0 comments on commit c4ad155

Please sign in to comment.