feat: v-once

Note: only compiler transform is tested - integration with runtime
still needs to be tested.
This commit is contained in:
Evan You 2019-10-09 17:32:58 -04:00
parent 5dfb271551
commit 93c6aa4c90
7 changed files with 63 additions and 13 deletions

View File

@ -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]`
})
)
})
})

View 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
}
})

View File

@ -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
}
}

View 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,

View 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

View File

@ -177,10 +177,15 @@ export function createRenderer<
optimized: boolean = false
) {
// patching & not same type, unmount old tree
if (n1 != null && !isSameType(n1, n2)) {
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

View 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 => {