Kaynağa Gözat

feat: support usage of `v-once` with `v-if` (#200)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
Lulu 1 yıl önce
ebeveyn
işleme
b3cb392f5c

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

@@ -46,3 +46,33 @@ export function render(_ctx) {
   return n1
   return n1
 }"
 }"
 `;
 `;
+
+exports[`compiler: v-once > with v-if 1`] = `
+"import { createIf as _createIf, template as _template } from 'vue/vapor';
+const t0 = _template("<div></div>")
+
+export function render(_ctx) {
+  const n0 = _createIf(() => (_ctx.expr), () => {
+    const n2 = t0()
+    return n2
+  }, null, true)
+  return n0
+}"
+`;
+
+exports[`compiler: v-once > with v-if/else 1`] = `
+"import { createIf as _createIf, template as _template } from 'vue/vapor';
+const t0 = _template("<div></div>")
+const t1 = _template("<p></p>")
+
+export function render(_ctx) {
+  const n0 = _createIf(() => (_ctx.expr), () => {
+    const n2 = t0()
+    return n2
+  }, () => {
+    const n4 = t1()
+    return n4
+  }, true)
+  return n0
+}"
+`;

+ 59 - 1
packages/compiler-vapor/__tests__/transforms/vOnce.spec.ts

@@ -146,6 +146,64 @@ describe('compiler: v-once', () => {
   })
   })
 
 
   test.todo('with hoistStatic: true')
   test.todo('with hoistStatic: true')
-  test.todo('with v-if/else')
+
+  test('with v-if', () => {
+    const { ir, code, helpers } = compileWithOnce(`<div v-if="expr" v-once />`)
+    expect(code).toMatchSnapshot()
+    expect(helpers).lengthOf(0)
+    expect(ir.block.effect).lengthOf(0)
+    expect(ir.block.operation).toMatchObject([
+      {
+        type: IRNodeTypes.IF,
+        id: 0,
+        once: true,
+        condition: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: 'expr',
+          isStatic: false,
+        },
+        positive: {
+          type: IRNodeTypes.BLOCK,
+          dynamic: {
+            children: [{ template: 0 }],
+          },
+        },
+      },
+    ])
+  })
+
+  test('with v-if/else', () => {
+    const { ir, code, helpers } = compileWithOnce(
+      `<div v-if="expr" v-once /><p v-else/>`,
+    )
+    expect(code).toMatchSnapshot()
+    expect(helpers).lengthOf(0)
+    expect(ir.block.effect).lengthOf(0)
+    expect(ir.block.operation).toMatchObject([
+      {
+        type: IRNodeTypes.IF,
+        id: 0,
+        once: true,
+        condition: {
+          type: NodeTypes.SIMPLE_EXPRESSION,
+          content: 'expr',
+          isStatic: false,
+        },
+        positive: {
+          type: IRNodeTypes.BLOCK,
+          dynamic: {
+            children: [{ template: 0 }],
+          },
+        },
+        negative: {
+          type: IRNodeTypes.BLOCK,
+          dynamic: {
+            children: [{ template: 1 }],
+          },
+        },
+      },
+    ])
+  })
+
   test.todo('with v-for')
   test.todo('with v-for')
 })
 })

+ 3 - 2
packages/compiler-vapor/src/generators/if.ts

@@ -10,7 +10,7 @@ export function genIf(
   isNested = false,
   isNested = false,
 ): CodeFragment[] {
 ): CodeFragment[] {
   const { vaporHelper } = context
   const { vaporHelper } = context
-  const { condition, positive, negative } = oper
+  const { condition, positive, negative, once } = oper
   const [frag, push] = buildCodeFragment()
   const [frag, push] = buildCodeFragment()
 
 
   const conditionExpr: CodeFragment[] = [
   const conditionExpr: CodeFragment[] = [
@@ -36,7 +36,8 @@ export function genIf(
       vaporHelper('createIf'),
       vaporHelper('createIf'),
       conditionExpr,
       conditionExpr,
       positiveArg,
       positiveArg,
-      negativeArg,
+      negativeArg || (once ? 'null' : false),
+      once && 'true',
     ),
     ),
   )
   )
 
 

+ 1 - 0
packages/compiler-vapor/src/ir.ts

@@ -69,6 +69,7 @@ export interface IfIRNode extends BaseIRNode {
   condition: SimpleExpressionNode
   condition: SimpleExpressionNode
   positive: BlockIRNode
   positive: BlockIRNode
   negative?: BlockIRNode | IfIRNode
   negative?: BlockIRNode | IfIRNode
+  once?: boolean
 }
 }
 
 
 export interface ForIRNode extends BaseIRNode {
 export interface ForIRNode extends BaseIRNode {

+ 2 - 0
packages/compiler-vapor/src/transforms/vIf.ts

@@ -49,6 +49,7 @@ export function processIf(
         id,
         id,
         condition: dir.exp!,
         condition: dir.exp!,
         positive: branch,
         positive: branch,
+        once: context.inVOnce,
       })
       })
     }
     }
   } else {
   } else {
@@ -101,6 +102,7 @@ export function processIf(
         id: -1,
         id: -1,
         condition: dir.exp!,
         condition: dir.exp!,
         positive: branch,
         positive: branch,
+        once: context.inVOnce,
       }
       }
     }
     }
 
 

+ 9 - 2
packages/runtime-vapor/src/apiCreateIf.ts

@@ -10,6 +10,7 @@ export const createIf = (
   condition: () => any,
   condition: () => any,
   b1: BlockFn,
   b1: BlockFn,
   b2?: BlockFn,
   b2?: BlockFn,
+  once?: boolean,
   // hydrationNode?: Node,
   // hydrationNode?: Node,
 ): Fragment => {
 ): Fragment => {
   let newValue: any
   let newValue: any
@@ -31,7 +32,13 @@ export const createIf = (
   //   setCurrentHydrationNode(hydrationNode!)
   //   setCurrentHydrationNode(hydrationNode!)
   // }
   // }
 
 
-  renderEffect(() => {
+  if (once) {
+    doIf()
+  } else {
+    renderEffect(() => doIf())
+  }
+
+  function doIf() {
     if ((newValue = !!condition()) !== oldValue) {
     if ((newValue = !!condition()) !== oldValue) {
       parent ||= anchor.parentNode
       parent ||= anchor.parentNode
       if (block) {
       if (block) {
@@ -47,7 +54,7 @@ export const createIf = (
         fragment.nodes = []
         fragment.nodes = []
       }
       }
     }
     }
-  })
+  }
 
 
   // TODO: SSR
   // TODO: SSR
   // if (isHydrating) {
   // if (isHydrating) {