transform,
NodeTypes,
generate,
- CompilerOptions
+ CompilerOptions,
+ getBaseTransformPreset
} from '../../src'
-import { transformOnce } from '../../src/transforms/vOnce'
-import { transformElement } from '../../src/transforms/transformElement'
import { RENDER_SLOT, SET_BLOCK_TRACKING } from '../../src/runtimeHelpers'
-import { transformBind } from '../../src/transforms/vBind'
-import { transformSlotOutlet } from '../../src/transforms/transformSlotOutlet'
function transformWithOnce(template: string, options: CompilerOptions = {}) {
const ast = parse(template)
+ const [nodeTransforms, directiveTransforms] = getBaseTransformPreset()
transform(ast, {
- nodeTransforms: [transformOnce, transformElement, transformSlotOutlet],
- directiveTransforms: {
- bind: transformBind
- },
+ nodeTransforms,
+ directiveTransforms,
...options
})
return ast
})
expect(generate(root).code).toMatchSnapshot()
})
+
+ test('with v-if', () => {
+ const root = transformWithOnce(`<div v-if="true" v-once />`)
+ expect(root.cached).toBe(1)
+ expect(root.helpers).toContain(SET_BLOCK_TRACKING)
+ expect(root.children[0]).toMatchObject({
+ type: NodeTypes.IF,
+ // should cache the entire v-if expression, not just a single branch
+ codegenNode: {
+ type: NodeTypes.JS_CACHE_EXPRESSION
+ }
+ })
+ })
+
+ test('with v-for', () => {
+ const root = transformWithOnce(`<div v-for="i in list" v-once />`)
+ expect(root.cached).toBe(1)
+ expect(root.helpers).toContain(SET_BLOCK_TRACKING)
+ expect(root.children[0]).toMatchObject({
+ type: NodeTypes.FOR,
+ // should cache the entire v-for expression, not just a single branch
+ codegenNode: {
+ type: NodeTypes.JS_CACHE_EXPRESSION
+ }
+ })
+ })
})
}
// exit transforms
+ context.currentNode = node
let i = exitFns.length
while (i--) {
exitFns[i]()
import { NodeTransform } from '../transform'
import { findDir } from '../utils'
-import { NodeTypes } from '../ast'
+import { ElementNode, ForNode, IfNode, NodeTypes } from '../ast'
import { SET_BLOCK_TRACKING } from '../runtimeHelpers'
+const seen = new WeakSet()
+
export const transformOnce: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
+ if (seen.has(node)) {
+ return
+ }
+ seen.add(node)
context.helper(SET_BLOCK_TRACKING)
return () => {
- if (node.codegenNode) {
- node.codegenNode = context.cache(node.codegenNode, true /* isVNode */)
+ const cur = context.currentNode as ElementNode | IfNode | ForNode
+ if (cur.codegenNode) {
+ cur.codegenNode = context.cache(cur.codegenNode, true /* isVNode */)
}
}
}
-import { createApp, ref, nextTick } from '../src'
+import { createApp, ref, nextTick, reactive } from '../src'
describe('compiler + runtime integration', () => {
it('should support runtime template compilation', () => {
document.querySelector = origin
})
+
+ test('v-if + v-once', async () => {
+ const ok = ref(true)
+ const App = {
+ setup() {
+ return { ok }
+ },
+ template: `<div>{{ ok }}<div v-if="ok" v-once>{{ ok }}</div></div>`
+ }
+ const container = document.createElement('div')
+ createApp(App).mount(container)
+
+ expect(container.innerHTML).toBe(`<div>true<div>true</div></div>`)
+ ok.value = false
+ await nextTick()
+ expect(container.innerHTML).toBe(`<div>false<div>true</div></div>`)
+ })
+
+ test('v-for + v-once', async () => {
+ const list = reactive([1])
+ const App = {
+ setup() {
+ return { list }
+ },
+ template: `<div>{{ list.length }}<div v-for="i in list" v-once>{{ i }}</div></div>`
+ }
+ const container = document.createElement('div')
+ createApp(App).mount(container)
+
+ expect(container.innerHTML).toBe(`<div>1<div>1</div></div>`)
+ list.push(2)
+ await nextTick()
+ expect(container.innerHTML).toBe(`<div>2<div>1</div></div>`)
+ })
})