feat(compiler-core): re-implement v-once to use cache mechanism

This commit is contained in:
Evan You
2019-10-23 17:57:40 -04:00
parent 9291011456
commit af5a8e1154
21 changed files with 388 additions and 95 deletions

View File

@@ -4,12 +4,12 @@ import {
TemplateChildNode,
SimpleExpressionNode,
ElementTypes,
ElementCodegenNode,
PlainElementNode,
ComponentNode,
TemplateNode,
ElementNode,
PlainElementCodegenNode
PlainElementCodegenNode,
CodegenNodeWithDirective
} from '../ast'
import { TransformContext } from '../transform'
import { WITH_DIRECTIVES } from '../runtimeHelpers'
@@ -57,17 +57,20 @@ function walk(
} else {
// node may contain dynamic children, but its props may be eligible for
// hoisting.
const flag = getPatchFlag(child)
if (
(!flag ||
flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) &&
!hasDynamicKeyOrRef(child) &&
!hasCachedProps(child)
) {
const props = getNodeProps(child)
if (props && props !== `null`) {
getVNodeCall(child).arguments[1] = context.hoist(props)
const codegenNode = child.codegenNode!
if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) {
const flag = getPatchFlag(codegenNode)
if (
(!flag ||
flag === PatchFlags.NEED_PATCH ||
flag === PatchFlags.TEXT) &&
!hasDynamicKeyOrRef(child) &&
!hasCachedProps(child)
) {
const props = getNodeProps(child)
if (props && props !== `null`) {
getVNodeCall(codegenNode).arguments[1] = context.hoist(props)
}
}
}
}
@@ -100,7 +103,11 @@ export function isStaticNode(
if (cached !== undefined) {
return cached
}
const flag = getPatchFlag(node)
const codegenNode = node.codegenNode!
if (codegenNode.type !== NodeTypes.JS_CALL_EXPRESSION) {
return false
}
const flag = getPatchFlag(codegenNode)
if (!flag && !hasDynamicKeyOrRef(node) && !hasCachedProps(node)) {
// element self is static. check its children.
for (let i = 0; i < node.children.length; i++) {
@@ -165,26 +172,32 @@ function hasCachedProps(node: PlainElementNode): boolean {
return false
}
function getVNodeCall(node: PlainElementNode) {
let codegenNode = node.codegenNode as ElementCodegenNode
if (codegenNode.callee === WITH_DIRECTIVES) {
codegenNode = codegenNode.arguments[0]
function getNodeProps(node: PlainElementNode) {
const codegenNode = node.codegenNode!
if (codegenNode.type === NodeTypes.JS_CALL_EXPRESSION) {
return getVNodeArgAt(
codegenNode,
1
) as PlainElementCodegenNode['arguments'][1]
}
return codegenNode
}
type NonCachedCodegenNode =
| PlainElementCodegenNode
| CodegenNodeWithDirective<PlainElementCodegenNode>
function getVNodeArgAt(
node: PlainElementNode,
node: NonCachedCodegenNode,
index: number
): PlainElementCodegenNode['arguments'][number] {
return getVNodeCall(node).arguments[index]
}
function getPatchFlag(node: PlainElementNode): number | undefined {
function getVNodeCall(node: NonCachedCodegenNode) {
return node.callee === WITH_DIRECTIVES ? node.arguments[0] : node
}
function getPatchFlag(node: NonCachedCodegenNode): number | undefined {
const flag = getVNodeArgAt(node, 3) as string
return flag ? parseInt(flag, 10) : undefined
}
function getNodeProps(node: PlainElementNode) {
return getVNodeArgAt(node, 1) as PlainElementCodegenNode['arguments'][1]
}

View File

@@ -280,6 +280,11 @@ export function buildProps(
continue
}
// skip v-once - it is handled by its dedicated transform.
if (name === 'once') {
continue
}
// special case for v-bind and v-on with no argument
const isBind = name === 'bind'
const isOn = name === 'on'

View File

@@ -15,7 +15,8 @@ import {
createObjectExpression,
createObjectProperty,
ForCodegenNode,
ElementCodegenNode
ElementCodegenNode,
SlotOutletCodegenNode
} from '../ast'
import { createCompilerError, ErrorCodes } from '../errors'
import {
@@ -130,7 +131,7 @@ export const transformFor = createStructuralDirectiveTransform(
: null
if (slotOutlet) {
// <slot v-for="..."> or <template v-for="..."><slot/></template>
childBlock = slotOutlet.codegenNode!
childBlock = slotOutlet.codegenNode as SlotOutletCodegenNode
if (isTemplate && keyProperty) {
// <template v-for="..." :key="..."><slot/></template>
// we need to inject the key to the renderSlot() call.

View File

@@ -69,6 +69,7 @@ export const transformModel: DirectiveTransform = (dir, node, context) => {
if (
!__BROWSER__ &&
context.prefixIdentifiers &&
context.cacheHandlers &&
!hasScopeRef(exp, context.identifiers)
) {
props[1].value = context.cache(props[1].value)

View File

@@ -1,17 +1,15 @@
import {
DirectiveTransform,
createObjectProperty,
createSimpleExpression
} from '@vue/compiler-core'
import { NodeTransform } from '../transform'
import { findDir } from '../utils'
import { NodeTypes } from '../ast'
import { SET_BLOCK_TRACKING } from '../runtimeHelpers'
export const transformOnce: DirectiveTransform = dir => {
return {
props: [
createObjectProperty(
createSimpleExpression(`$once`, true, dir.loc),
createSimpleExpression('true', false)
)
],
needRuntime: false
export const transformOnce: NodeTransform = (node, context) => {
if (node.type === NodeTypes.ELEMENT && findDir(node, 'once', true)) {
context.helper(SET_BLOCK_TRACKING)
return () => {
if (node.codegenNode) {
node.codegenNode = context.cache(node.codegenNode, true /* isVNode */)
}
}
}
}