feat(compiler-core): more hoisting optimizations (#276)
This commit is contained in:
@@ -3986,6 +3986,7 @@ Object {
|
||||
Object {
|
||||
"content": Object {
|
||||
"content": "a < b",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7151,6 +7152,7 @@ Object {
|
||||
Object {
|
||||
"content": Object {
|
||||
"content": "'</div>'",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7320,6 +7322,7 @@ Object {
|
||||
Object {
|
||||
"arg": Object {
|
||||
"content": "se",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7640,6 +7643,7 @@ Object {
|
||||
Object {
|
||||
"content": Object {
|
||||
"content": "",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7798,6 +7802,7 @@ Object {
|
||||
Object {
|
||||
"arg": Object {
|
||||
"content": "class",
|
||||
"isConstant": true,
|
||||
"isStatic": true,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7816,6 +7821,7 @@ Object {
|
||||
},
|
||||
"exp": Object {
|
||||
"content": "{ some: condition }",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7876,6 +7882,7 @@ Object {
|
||||
Object {
|
||||
"arg": Object {
|
||||
"content": "style",
|
||||
"isConstant": true,
|
||||
"isStatic": true,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7894,6 +7901,7 @@ Object {
|
||||
},
|
||||
"exp": Object {
|
||||
"content": "{ color: 'red' }",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -7983,6 +7991,7 @@ Object {
|
||||
Object {
|
||||
"arg": Object {
|
||||
"content": "style",
|
||||
"isConstant": true,
|
||||
"isStatic": true,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -8001,6 +8010,7 @@ Object {
|
||||
},
|
||||
"exp": Object {
|
||||
"content": "{ color: 'red' }",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -8080,6 +8090,7 @@ Object {
|
||||
Object {
|
||||
"arg": Object {
|
||||
"content": "class",
|
||||
"isConstant": true,
|
||||
"isStatic": true,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
@@ -8098,6 +8109,7 @@ Object {
|
||||
},
|
||||
"exp": Object {
|
||||
"content": "{ some: condition }",
|
||||
"isConstant": false,
|
||||
"isStatic": false,
|
||||
"loc": Object {
|
||||
"end": Object {
|
||||
|
||||
@@ -298,6 +298,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `message`,
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
loc: {
|
||||
start: { offset: 2, line: 1, column: 3 },
|
||||
end: { offset: 9, line: 1, column: 10 },
|
||||
@@ -322,6 +323,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `a<b`,
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
loc: {
|
||||
start: { offset: 3, line: 1, column: 4 },
|
||||
end: { offset: 6, line: 1, column: 7 },
|
||||
@@ -347,6 +349,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `a<b`,
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
loc: {
|
||||
start: { offset: 3, line: 1, column: 4 },
|
||||
end: { offset: 6, line: 1, column: 7 },
|
||||
@@ -365,6 +368,7 @@ describe('compiler: parse', () => {
|
||||
content: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
content: 'c>d',
|
||||
loc: {
|
||||
start: { offset: 12, line: 1, column: 13 },
|
||||
@@ -390,6 +394,8 @@ describe('compiler: parse', () => {
|
||||
content: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: false,
|
||||
// The `isConstant` is the default value and will be determined in `transformExpression`.
|
||||
isConstant: false,
|
||||
content: '"</div>"',
|
||||
loc: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
@@ -974,6 +980,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
loc: {
|
||||
start: { offset: 11, line: 1, column: 12 },
|
||||
end: { offset: 12, line: 1, column: 13 },
|
||||
@@ -999,6 +1006,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'click',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'click',
|
||||
@@ -1071,6 +1079,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'click',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'click',
|
||||
@@ -1107,6 +1116,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'a',
|
||||
@@ -1127,6 +1137,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'b',
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
|
||||
loc: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
@@ -1153,6 +1164,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'a',
|
||||
@@ -1173,6 +1185,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'b',
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
|
||||
loc: {
|
||||
start: { offset: 13, line: 1, column: 14 },
|
||||
@@ -1199,6 +1212,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'a',
|
||||
@@ -1219,6 +1233,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'b',
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
|
||||
loc: {
|
||||
start: { offset: 8, line: 1, column: 9 },
|
||||
@@ -1245,6 +1260,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
|
||||
loc: {
|
||||
source: 'a',
|
||||
@@ -1265,6 +1281,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'b',
|
||||
isStatic: false,
|
||||
isConstant: false,
|
||||
|
||||
loc: {
|
||||
start: { offset: 14, line: 1, column: 15 },
|
||||
@@ -1291,6 +1308,7 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: 'a',
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
loc: {
|
||||
source: 'a',
|
||||
start: {
|
||||
@@ -1310,6 +1328,8 @@ describe('compiler: parse', () => {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: '{ b }',
|
||||
isStatic: false,
|
||||
// The `isConstant` is the default value and will be determined in transformExpression
|
||||
isConstant: false,
|
||||
loc: {
|
||||
start: { offset: 10, line: 1, column: 11 },
|
||||
end: { offset: 15, line: 1, column: 16 },
|
||||
|
||||
@@ -152,6 +152,93 @@ return function render() {
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers hoist class with static object value 1`] = `
|
||||
"const _Vue = Vue
|
||||
const _createVNode = Vue.createVNode
|
||||
|
||||
const _hoisted_1 = { class: { foo: true }}
|
||||
|
||||
return function render() {
|
||||
with (this) {
|
||||
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
_createVNode(\\"span\\", _hoisted_1, _toString(_ctx.bar), 1 /* TEXT */)
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static interpolation 1`] = `
|
||||
"const _Vue = Vue
|
||||
const _createVNode = Vue.createVNode
|
||||
|
||||
const _hoisted_1 = _createVNode(\\"span\\", null, [\\"foo \\", _toString(1), _toString(2)])
|
||||
|
||||
return function render() {
|
||||
with (this) {
|
||||
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
_hoisted_1
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers hoist nested static tree with static prop value 1`] = `
|
||||
"const _Vue = Vue
|
||||
const _createVNode = Vue.createVNode
|
||||
|
||||
const _hoisted_1 = _createVNode(\\"span\\", { foo: 0 }, _toString(1), 1 /* TEXT */)
|
||||
|
||||
return function render() {
|
||||
with (this) {
|
||||
const { toString: _toString, createVNode: _createVNode, createBlock: _createBlock, openBlock: _openBlock } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
_hoisted_1
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable (2) 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
return function render() {
|
||||
with (this) {
|
||||
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, toString: _toString, createVNode: _createVNode } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
(_openBlock(), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
|
||||
return (_openBlock(), _createBlock(\\"p\\", null, [
|
||||
_createVNode(\\"span\\", null, _toString(o + 'foo'), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform prefixIdentifiers should NOT hoist expressions that with scope variable 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
return function render() {
|
||||
with (this) {
|
||||
const { renderList: _renderList, openBlock: _openBlock, createBlock: _createBlock, Fragment: _Fragment, toString: _toString, createVNode: _createVNode } = _Vue
|
||||
|
||||
return (_openBlock(), _createBlock(\\"div\\", null, [
|
||||
(_openBlock(), _createBlock(_Fragment, null, _renderList(_ctx.list, (o) => {
|
||||
return (_openBlock(), _createBlock(\\"p\\", null, [
|
||||
_createVNode(\\"span\\", null, _toString(o), 1 /* TEXT */)
|
||||
]))
|
||||
}), 128 /* UNKEYED_FRAGMENT */))
|
||||
]))
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler: hoistStatic transform should NOT hoist components 1`] = `
|
||||
"const _Vue = Vue
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
import { parse, transform, NodeTypes, generate } from '../../src'
|
||||
import {
|
||||
parse,
|
||||
transform,
|
||||
NodeTypes,
|
||||
generate,
|
||||
CompilerOptions
|
||||
} from '../../src'
|
||||
import {
|
||||
OPEN_BLOCK,
|
||||
CREATE_BLOCK,
|
||||
@@ -8,17 +14,24 @@ import {
|
||||
RENDER_LIST
|
||||
} from '../../src/runtimeHelpers'
|
||||
import { transformElement } from '../../src/transforms/transformElement'
|
||||
import { transformExpression } from '../../src/transforms/transformExpression'
|
||||
import { transformIf } from '../../src/transforms/vIf'
|
||||
import { transformFor } from '../../src/transforms/vFor'
|
||||
import { transformBind } from '../../src/transforms/vBind'
|
||||
import { createObjectMatcher, genFlagText } from '../testUtils'
|
||||
import { PatchFlags } from '@vue/shared'
|
||||
|
||||
function transformWithHoist(template: string) {
|
||||
function transformWithHoist(template: string, options: CompilerOptions = {}) {
|
||||
const ast = parse(template)
|
||||
transform(ast, {
|
||||
hoistStatic: true,
|
||||
nodeTransforms: [transformIf, transformFor, transformElement],
|
||||
prefixIdentifiers: options.prefixIdentifiers,
|
||||
nodeTransforms: [
|
||||
transformIf,
|
||||
transformFor,
|
||||
...(options.prefixIdentifiers ? [transformExpression] : []),
|
||||
transformElement
|
||||
],
|
||||
directiveTransforms: {
|
||||
bind: transformBind
|
||||
}
|
||||
@@ -429,4 +442,186 @@ describe('compiler: hoistStatic transform', () => {
|
||||
})
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
describe('prefixIdentifiers', () => {
|
||||
test('hoist nested static tree with static interpolation', () => {
|
||||
const { root, args } = transformWithHoist(
|
||||
`<div><span>foo {{ 1 }} {{ 2 }}</span></div>`,
|
||||
{
|
||||
prefixIdentifiers: true
|
||||
}
|
||||
)
|
||||
expect(root.hoists).toMatchObject([
|
||||
{
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
callee: CREATE_VNODE,
|
||||
arguments: [
|
||||
`"span"`,
|
||||
`null`,
|
||||
[
|
||||
{
|
||||
type: NodeTypes.TEXT,
|
||||
content: `foo `
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `1`,
|
||||
isStatic: false,
|
||||
isConstant: true
|
||||
}
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `2`,
|
||||
isStatic: false,
|
||||
isConstant: true
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
])
|
||||
expect(args).toMatchObject([
|
||||
`"div"`,
|
||||
`null`,
|
||||
[
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
codegenNode: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `_hoisted_1`
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('hoist nested static tree with static prop value', () => {
|
||||
const { root, args } = transformWithHoist(
|
||||
`<div><span :foo="0">{{ 1 }}</span></div>`,
|
||||
{
|
||||
prefixIdentifiers: true
|
||||
}
|
||||
)
|
||||
|
||||
expect(root.hoists).toMatchObject([
|
||||
{
|
||||
type: NodeTypes.JS_CALL_EXPRESSION,
|
||||
callee: CREATE_VNODE,
|
||||
arguments: [
|
||||
`"span"`,
|
||||
createObjectMatcher({ foo: `[0]` }),
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `1`,
|
||||
isStatic: false,
|
||||
isConstant: true
|
||||
}
|
||||
},
|
||||
'1 /* TEXT */'
|
||||
]
|
||||
}
|
||||
])
|
||||
expect(args).toMatchObject([
|
||||
`"div"`,
|
||||
`null`,
|
||||
[
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
codegenNode: {
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `_hoisted_1`
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('hoist class with static object value', () => {
|
||||
const { root, args } = transformWithHoist(
|
||||
`<div><span :class="{ foo: true }">{{ bar }}</span></div>`,
|
||||
{
|
||||
prefixIdentifiers: true
|
||||
}
|
||||
)
|
||||
|
||||
expect(root.hoists).toMatchObject([
|
||||
{
|
||||
type: NodeTypes.JS_OBJECT_EXPRESSION,
|
||||
properties: [
|
||||
{
|
||||
key: {
|
||||
content: `class`,
|
||||
isConstant: true,
|
||||
isStatic: true
|
||||
},
|
||||
value: {
|
||||
content: `{ foo: true }`,
|
||||
isConstant: true,
|
||||
isStatic: false
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
expect(args).toMatchObject([
|
||||
`"div"`,
|
||||
`null`,
|
||||
[
|
||||
{
|
||||
type: NodeTypes.ELEMENT,
|
||||
codegenNode: {
|
||||
callee: CREATE_VNODE,
|
||||
arguments: [
|
||||
`"span"`,
|
||||
{
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
content: `_hoisted_1`
|
||||
},
|
||||
{
|
||||
type: NodeTypes.INTERPOLATION,
|
||||
content: {
|
||||
content: `_ctx.bar`,
|
||||
isConstant: false,
|
||||
isStatic: false
|
||||
}
|
||||
},
|
||||
`1 /* TEXT */`
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
])
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should NOT hoist expressions that with scope variable', () => {
|
||||
const { root } = transformWithHoist(
|
||||
`<div><p v-for="o in list"><span>{{ o }}</span></p></div>`,
|
||||
{
|
||||
prefixIdentifiers: true
|
||||
}
|
||||
)
|
||||
|
||||
expect(root.hoists.length).toBe(0)
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
|
||||
test('should NOT hoist expressions that with scope variable (2)', () => {
|
||||
const { root } = transformWithHoist(
|
||||
`<div><p v-for="o in list"><span>{{ o + 'foo' }}</span></p></div>`,
|
||||
{
|
||||
prefixIdentifiers: true
|
||||
}
|
||||
)
|
||||
|
||||
expect(root.hoists.length).toBe(0)
|
||||
expect(generate(root).code).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -79,6 +79,7 @@ describe('isEmptyExpression', () => {
|
||||
content: '',
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
loc: null as any
|
||||
})
|
||||
).toBe(true)
|
||||
@@ -90,6 +91,7 @@ describe('isEmptyExpression', () => {
|
||||
content: ' \t ',
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
loc: null as any
|
||||
})
|
||||
).toBe(true)
|
||||
@@ -101,6 +103,7 @@ describe('isEmptyExpression', () => {
|
||||
content: 'foo',
|
||||
type: NodeTypes.SIMPLE_EXPRESSION,
|
||||
isStatic: true,
|
||||
isConstant: true,
|
||||
loc: null as any
|
||||
})
|
||||
).toBe(false)
|
||||
|
||||
Reference in New Issue
Block a user