]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat: v-once
authorEvan You <yyx990803@gmail.com>
Wed, 9 Oct 2019 21:32:58 +0000 (17:32 -0400)
committerEvan You <yyx990803@gmail.com>
Wed, 9 Oct 2019 21:32:58 +0000 (17:32 -0400)
Note: only compiler transform is tested - integration with runtime
still needs to be tested.

packages/compiler-core/__tests__/transforms/vOnce.spec.ts [new file with mode: 0644]
packages/compiler-core/src/index.ts
packages/compiler-core/src/transforms/vOnce.ts [new file with mode: 0644]
packages/compiler-core/src/utils.ts
packages/compiler-dom/__tests__/transforms/vCloak.spec.ts
packages/runtime-core/src/createRenderer.ts
packages/shared/src/index.ts

diff --git a/packages/compiler-core/__tests__/transforms/vOnce.spec.ts b/packages/compiler-core/__tests__/transforms/vOnce.spec.ts
new file mode 100644 (file)
index 0000000..44b6e69
--- /dev/null
@@ -0,0 +1,30 @@
+import { parse, transform, ElementNode, CallExpression } from '../../src'
+import { transformOnce } from '../../src/transforms/vOnce'
+import { transformElement } from '../../src/transforms/transformElement'
+import { createObjectMatcher } from '../testUtils'
+
+function transformWithCloak(template: string) {
+  const ast = parse(template)
+  transform(ast, {
+    nodeTransforms: [transformElement],
+    directiveTransforms: {
+      once: transformOnce
+    }
+  })
+  return ast.children[0] as ElementNode
+}
+
+describe('compiler: v-once transform', () => {
+  test('should add no props to DOM', () => {
+    const node = transformWithCloak(`<div v-once />`)
+    const codegenArgs = (node.codegenNode as CallExpression).arguments
+
+    // As v-cloak adds no properties the codegen should be identical to
+    // rendering a div with no props or reactive data (so just the tag as the arg)
+    expect(codegenArgs[1]).toMatchObject(
+      createObjectMatcher({
+        $once: `[true]`
+      })
+    )
+  })
+})
index e1fdec2b4122ae3549381514d0a77d92a128eb23..e1c967fece1bda5e8f2d23e46aee9077515e7ce3 100644 (file)
@@ -13,6 +13,7 @@ import { transformBind } from './transforms/vBind'
 import { defaultOnError, createCompilerError, ErrorCodes } from './errors'
 import { trackSlotScopes, trackVForSlotScopes } from './transforms/vSlot'
 import { optimizeText } from './transforms/optimizeText'
+import { transformOnce } from './transforms/vOnce'
 
 export type CompilerOptions = ParserOptions & TransformOptions & CodegenOptions
 
@@ -60,6 +61,7 @@ export function baseCompile(
     directiveTransforms: {
       on: transformOn,
       bind: transformBind,
+      once: transformOnce,
       ...(options.directiveTransforms || {}) // user transforms
     }
   })
diff --git a/packages/compiler-core/src/transforms/vOnce.ts b/packages/compiler-core/src/transforms/vOnce.ts
new file mode 100644 (file)
index 0000000..b83e040
--- /dev/null
@@ -0,0 +1,15 @@
+import {
+  DirectiveTransform,
+  createObjectProperty,
+  createSimpleExpression
+} from '@vue/compiler-core'
+
+export const transformOnce: DirectiveTransform = dir => {
+  return {
+    props: createObjectProperty(
+      createSimpleExpression(`$once`, true, dir.loc),
+      createSimpleExpression('true', false)
+    ),
+    needRuntime: false
+  }
+}
index 6ffae2caa5fef33e5bd47f72a50b2ed016fe193f..83a71bb8f799d12034d6c95dd977dc9916985583 100644 (file)
@@ -63,7 +63,7 @@ export const walkJS: typeof walk = (ast, walker) => {
 }
 
 export const isSimpleIdentifier = (name: string): boolean =>
-  !/^\d|[^\w]/.test(name)
+  !/^\d|[^\$\w]/.test(name)
 
 export function getInnerRange(
   loc: SourceLocation,
index a9c84a4652c6c5c74b761ad720e93860e6a754a4..955dde7315947fdf591c11dbfe2fc7132f3920cb 100644 (file)
@@ -1,26 +1,24 @@
 import {
   parse,
   transform,
-  CompilerOptions,
-  ElementNode
+  ElementNode,
+  CallExpression
 } from '@vue/compiler-core'
 import { transformCloak } from '../../src/transforms/vCloak'
 import { transformElement } from '../../../compiler-core/src/transforms/transformElement'
-import { CallExpression } from '../../src'
 
-function transformWithCloak(template: string, options: CompilerOptions = {}) {
+function transformWithCloak(template: string) {
   const ast = parse(template)
   transform(ast, {
     nodeTransforms: [transformElement],
     directiveTransforms: {
       cloak: transformCloak
-    },
-    ...options
+    }
   })
   return ast.children[0] as ElementNode
 }
 
-describe('compiler: `v-cloak` transform', () => {
+describe('compiler: v-cloak transform', () => {
   test('should add no props to DOM', () => {
     const node = transformWithCloak(`<div v-cloak/>`)
     const codegenArgs = (node.codegenNode as CallExpression).arguments
index 8211a4bdc7291935dd5896b089149f84cda9a5ea..647a4ac9069be170f8c5ec55862d1499e1029c71 100644 (file)
@@ -177,10 +177,15 @@ export function createRenderer<
     optimized: boolean = false
   ) {
     // patching & not same type, unmount old tree
-    if (n1 != null && !isSameType(n1, n2)) {
-      anchor = getNextHostNode(n1)
-      unmount(n1, parentComponent, parentSuspense, true)
-      n1 = null
+    if (n1 != null) {
+      if (!isSameType(n1, n2)) {
+        anchor = getNextHostNode(n1)
+        unmount(n1, parentComponent, parentSuspense, true)
+        n1 = null
+      } else if (n1.props && n1.props.$once) {
+        console.log(111)
+        return
+      }
     }
 
     const { type, shapeFlag } = n2
index 6e1b90c176201508eb0950a2db2573058855b2aa..485be989cd8a4bca1a457ce38cc47153a49f387c 100644 (file)
@@ -43,7 +43,7 @@ export const isPlainObject = (val: any): val is object =>
 
 const vnodeHooksRE = /^vnode/
 export const isReservedProp = (key: string): boolean =>
-  key === 'key' || key === 'ref' || vnodeHooksRE.test(key)
+  key === 'key' || key === 'ref' || key === '$once' || vnodeHooksRE.test(key)
 
 const camelizeRE = /-(\w)/g
 export const camelize = (str: string): string => {