refactor(compiler-core): follow up on #276

This commit is contained in:
Evan You 2019-10-15 11:51:52 -04:00
parent 68a3879b88
commit 4cee06ddab
4 changed files with 48 additions and 21 deletions

View File

@ -173,7 +173,7 @@ exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static t
"const _Vue = Vue "const _Vue = Vue
const _createVNode = Vue.createVNode const _createVNode = Vue.createVNode
const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(2)]) const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(true)])
return function render() { return function render() {
with (this) { with (this) {
@ -203,7 +203,7 @@ return function render() {
}" }"
`; `;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable (2) 1`] = ` exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (2) 1`] = `
"const _Vue = Vue "const _Vue = Vue
return function render() { return function render() {
@ -221,7 +221,24 @@ return function render() {
}" }"
`; `;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable 1`] = ` exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables (v-slot) 1`] = `
"const _Vue = Vue
return function render() {
with (this) {
const { toString: _toString, resolveComponent: _resolveComponent, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
const _component_Comp = _resolveComponent(\\"Comp\\")
return (_openBlock(), _createBlock(_component_Comp, null, {
default: ({ foo }) => [_toString(_ctx.foo)],
_compiled: true
}))
}
}"
`;
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that refer scope variables 1`] = `
"const _Vue = Vue "const _Vue = Vue
return function render() { return function render() {

View File

@ -446,7 +446,7 @@ describe('compiler: hoistStatic transform', () => {
describe('prefixIdentifiers', () => { describe('prefixIdentifiers', () => {
test('hoist nested static tree with static interpolation', () => { test('hoist nested static tree with static interpolation', () => {
const { root, args } = transformWithHoist( const { root, args } = transformWithHoist(
`<div><span>foo {{ 1 }} {{ 2 }}</span></div>`, `<div><span>foo {{ 1 }} {{ true }}</span></div>`,
{ {
prefixIdentifiers: true prefixIdentifiers: true
} }
@ -474,7 +474,7 @@ describe('compiler: hoistStatic transform', () => {
{ {
type: NodeTypes.INTERPOLATION, type: NodeTypes.INTERPOLATION,
content: { content: {
content: `2`, content: `true`,
isStatic: false, isStatic: false,
isConstant: true isConstant: true
} }
@ -600,7 +600,7 @@ describe('compiler: hoistStatic transform', () => {
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
}) })
test('should NOT hoist expressions that with scope variable', () => { test('should NOT hoist expressions that refer scope variables', () => {
const { root } = transformWithHoist( const { root } = transformWithHoist(
`<div><p v-for="o in list"><span>{{ o }}</span></p></div>`, `<div><p v-for="o in list"><span>{{ o }}</span></p></div>`,
{ {
@ -612,7 +612,7 @@ describe('compiler: hoistStatic transform', () => {
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
}) })
test('should NOT hoist expressions that with scope variable (2)', () => { test('should NOT hoist expressions that refer scope variables (2)', () => {
const { root } = transformWithHoist( const { root } = transformWithHoist(
`<div><p v-for="o in list"><span>{{ o + 'foo' }}</span></p></div>`, `<div><p v-for="o in list"><span>{{ o + 'foo' }}</span></p></div>`,
{ {
@ -623,5 +623,17 @@ describe('compiler: hoistStatic transform', () => {
expect(root.hoists.length).toBe(0) expect(root.hoists.length).toBe(0)
expect(generate(root).code).toMatchSnapshot() expect(generate(root).code).toMatchSnapshot()
}) })
test('should NOT hoist expressions that refer scope variables (v-slot)', () => {
const { root } = transformWithHoist(
`<Comp v-slot="{ foo }">{{ foo }}</Comp>`,
{
prefixIdentifiers: true
}
)
expect(root.hoists.length).toBe(0)
expect(generate(root).code).toMatchSnapshot()
})
}) })
}) })

View File

@ -564,12 +564,9 @@ function parseAttribute(
) )
let content = match[2] let content = match[2]
let isStatic = true let isStatic = true
// Non-dynamic arg is a constant.
let isConstant = true
if (content.startsWith('[')) { if (content.startsWith('[')) {
isStatic = false isStatic = false
isConstant = false
if (!content.endsWith(']')) { if (!content.endsWith(']')) {
emitError( emitError(
@ -585,7 +582,7 @@ function parseAttribute(
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content, content,
isStatic, isStatic,
isConstant, isConstant: isStatic,
loc loc
} }
} }
@ -611,7 +608,8 @@ function parseAttribute(
type: NodeTypes.SIMPLE_EXPRESSION, type: NodeTypes.SIMPLE_EXPRESSION,
content: value.content, content: value.content,
isStatic: false, isStatic: false,
// Set `isConstant` to false by default and will decide in transformExpression // Treat as non-constant by default. This can be potentially set to
// true by `transformExpression` to make it eligible for hoisting.
isConstant: false, isConstant: false,
loc: value.loc loc: value.loc
}, },

View File

@ -91,6 +91,9 @@ export function processExpression(
!literalsWhitelist.has(rawExp) !literalsWhitelist.has(rawExp)
) { ) {
node.content = `_ctx.${rawExp}` node.content = `_ctx.${rawExp}`
} else if (!context.identifiers[rawExp]) {
// mark node constant for hoisting unless it's referring a scope variable
node.isConstant = true
} }
return node return node
} }
@ -109,13 +112,13 @@ export function processExpression(
const ids: (Identifier & PrefixMeta)[] = [] const ids: (Identifier & PrefixMeta)[] = []
const knownIds = Object.create(context.identifiers) const knownIds = Object.create(context.identifiers)
let isConstant = true
// walk the AST and look for identifiers that need to be prefixed with `_ctx.`. // walk the AST and look for identifiers that need to be prefixed with `_ctx.`.
walkJS(ast, { walkJS(ast, {
enter(node: Node & PrefixMeta, parent) { enter(node: Node & PrefixMeta, parent) {
if (node.type === 'Identifier') { if (node.type === 'Identifier') {
if (!ids.includes(node)) { if (!ids.includes(node)) {
if (!knownIds[node.name] && shouldPrefix(node, parent)) { const needPrefix = shouldPrefix(node, parent)
if (!knownIds[node.name] && needPrefix) {
if (isPropertyShorthand(node, parent)) { if (isPropertyShorthand(node, parent)) {
// property shorthand like { foo }, we need to add the key since we // property shorthand like { foo }, we need to add the key since we
// rewrite the value // rewrite the value
@ -123,14 +126,11 @@ export function processExpression(
} }
node.name = `_ctx.${node.name}` node.name = `_ctx.${node.name}`
node.isConstant = false node.isConstant = false
isConstant = false
ids.push(node) ids.push(node)
} else if (!isStaticPropertyKey(node, parent)) { } else if (!isStaticPropertyKey(node, parent)) {
// This means this identifier is pointing to a scope variable (a v-for alias, or a v-slot prop) // The identifier is considered constant unless it's pointing to a
// which is also dynamic and cannot be hoisted. // scope variable (a v-for alias, or a v-slot prop)
node.isConstant = !( node.isConstant = !(needPrefix && knownIds[node.name])
knownIds[node.name] && shouldPrefix(node, parent)
)
// also generate sub-expressions for other identifiers for better // also generate sub-expressions for other identifiers for better
// source map support. (except for property keys which are static) // source map support. (except for property keys which are static)
ids.push(node) ids.push(node)
@ -220,7 +220,7 @@ export function processExpression(
ret = createCompoundExpression(children, node.loc) ret = createCompoundExpression(children, node.loc)
} else { } else {
ret = node ret = node
ret.isConstant = isConstant ret.isConstant = true
} }
ret.identifiers = Object.keys(knownIds) ret.identifiers = Object.keys(knownIds)
return ret return ret