mirror of https://github.com/vuejs/core.git
feat: generate specific function when the prop key is static (#97)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
51098cff94
commit
5028880e5b
|
@ -29,10 +29,10 @@ PR are welcome!
|
|||
- [x] `v-bind`
|
||||
- [x] simple expression
|
||||
- [x] compound expression
|
||||
- [ ] modifiers
|
||||
- [x] modifiers
|
||||
- [x] .camel
|
||||
- [ ] .prop
|
||||
- [ ] .attr
|
||||
- [x] .prop
|
||||
- [x] .attr
|
||||
- [x] `v-on`
|
||||
- [x] simple expression
|
||||
- [x] compound expression
|
||||
|
|
|
@ -1,28 +1,28 @@
|
|||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`compiler v-bind > .attr modifier 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)
|
||||
_setAttr(n1, "foo-bar", undefined, _ctx.id)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler v-bind > .attr modifier w/ no expression 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setAttr as _setAttr } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)
|
||||
_setAttr(n1, "foo-bar", undefined, _ctx.fooBar)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
|
@ -71,42 +71,42 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
|
||||
_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler v-bind > .prop modifier (shorthand) 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
|
||||
_setDOMProp(n1, "fooBar", undefined, _ctx.id)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`compiler v-bind > .prop modifier 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)
|
||||
_setDOMProp(n1, "fooBar", undefined, _ctx.id)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
|
@ -127,14 +127,14 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler v-bind > .prop modifier w/ no expression 1`] = `
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, renderEffect as _renderEffect, setDOMProp as _setDOMProp } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div></div>")
|
||||
const n0 = t0()
|
||||
const { 0: [n1],} = _children(n0)
|
||||
_renderEffect(() => {
|
||||
_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)
|
||||
_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)
|
||||
})
|
||||
return n0
|
||||
}"
|
||||
|
|
|
@ -13,7 +13,7 @@ export function render(_ctx) {
|
|||
`;
|
||||
|
||||
exports[`compiler: v-once > basic 1`] = `
|
||||
"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor';
|
||||
"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setClass as _setClass, prepend as _prepend } from 'vue/vapor';
|
||||
|
||||
export function render(_ctx) {
|
||||
const t0 = _template("<div> <span></span></div>")
|
||||
|
@ -21,7 +21,7 @@ export function render(_ctx) {
|
|||
const { 0: [n3, { 1: [n2],}],} = _children(n0)
|
||||
const n1 = _createTextNode(_ctx.msg)
|
||||
_setText(n1, undefined, _ctx.msg)
|
||||
_setDynamicProp(n2, "class", undefined, _ctx.clz)
|
||||
_setClass(n2, "class", undefined, _ctx.clz)
|
||||
_prepend(n3, n1)
|
||||
return n0
|
||||
}"
|
||||
|
|
|
@ -189,6 +189,8 @@ describe('compiler v-bind', () => {
|
|||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: undefined,
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
|
@ -207,6 +209,8 @@ describe('compiler v-bind', () => {
|
|||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: undefined,
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
|
@ -220,7 +224,6 @@ describe('compiler v-bind', () => {
|
|||
const { ir, code } = compileWithVBind(`<div v-bind:[foo].camel="id"/>`)
|
||||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
runtimeCamelize: true,
|
||||
key: {
|
||||
content: `foo`,
|
||||
isStatic: false,
|
||||
|
@ -229,6 +232,8 @@ describe('compiler v-bind', () => {
|
|||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: true,
|
||||
modifier: undefined,
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
|
@ -245,18 +250,20 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `.fooBar`,
|
||||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
|
||||
expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
|
||||
})
|
||||
|
||||
test('.prop modifier w/ no expression', () => {
|
||||
|
@ -264,20 +271,20 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `.fooBar`,
|
||||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains(
|
||||
'_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
|
||||
)
|
||||
expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
|
||||
})
|
||||
|
||||
test('.prop modifier w/ dynamic arg', () => {
|
||||
|
@ -292,6 +299,8 @@ describe('compiler v-bind', () => {
|
|||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
|
@ -308,18 +317,20 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `.fooBar`,
|
||||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)')
|
||||
expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.id)')
|
||||
})
|
||||
|
||||
test('.prop modifier (shortband) w/ no expression', () => {
|
||||
|
@ -327,20 +338,20 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `.fooBar`,
|
||||
content: `fooBar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '.',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains(
|
||||
'_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)',
|
||||
)
|
||||
expect(code).contains('_setDOMProp(n1, "fooBar", undefined, _ctx.fooBar)')
|
||||
})
|
||||
|
||||
test('.attr modifier', () => {
|
||||
|
@ -348,18 +359,20 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `^foo-bar`,
|
||||
content: `foo-bar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `id`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '^',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)')
|
||||
expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.id)')
|
||||
})
|
||||
|
||||
test('.attr modifier w/ no expression', () => {
|
||||
|
@ -367,19 +380,19 @@ describe('compiler v-bind', () => {
|
|||
|
||||
expect(ir.effect[0].operations[0]).toMatchObject({
|
||||
key: {
|
||||
content: `^foo-bar`,
|
||||
content: `foo-bar`,
|
||||
isStatic: true,
|
||||
},
|
||||
value: {
|
||||
content: `fooBar`,
|
||||
isStatic: false,
|
||||
},
|
||||
runtimeCamelize: false,
|
||||
modifier: '^',
|
||||
})
|
||||
|
||||
expect(code).matchSnapshot()
|
||||
expect(code).contains('renderEffect')
|
||||
expect(code).contains(
|
||||
'_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)',
|
||||
)
|
||||
expect(code).contains('_setAttr(n1, "foo-bar", undefined, _ctx.fooBar)')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,20 +1,56 @@
|
|||
import type { CodegenContext } from '../generate'
|
||||
import type { SetPropIRNode } from '../ir'
|
||||
import { genExpression } from './expression'
|
||||
import { isString } from '@vue/shared'
|
||||
|
||||
export function genSetProp(oper: SetPropIRNode, context: CodegenContext) {
|
||||
const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context
|
||||
|
||||
newline()
|
||||
|
||||
const element = `n${oper.element}`
|
||||
|
||||
// fast path for static props
|
||||
if (isString(oper.key) || oper.key.isStatic) {
|
||||
const keyName = isString(oper.key) ? oper.key : oper.key.content
|
||||
|
||||
let helperName: string | undefined
|
||||
if (keyName === 'class') {
|
||||
helperName = 'setClass'
|
||||
} else if (keyName === 'style') {
|
||||
helperName = 'setStyle'
|
||||
} else if (oper.modifier) {
|
||||
helperName = oper.modifier === '.' ? 'setDOMProp' : 'setAttr'
|
||||
}
|
||||
|
||||
if (helperName) {
|
||||
pushFnCall(
|
||||
vaporHelper(helperName),
|
||||
element,
|
||||
() => {
|
||||
const expr = () => genExpression(oper.key, context)
|
||||
if (oper.runtimeCamelize) {
|
||||
pushFnCall(helper('camelize'), expr)
|
||||
} else {
|
||||
expr()
|
||||
}
|
||||
},
|
||||
'undefined',
|
||||
() => genExpression(oper.value, context),
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pushFnCall(
|
||||
vaporHelper('setDynamicProp'),
|
||||
`n${oper.element}`,
|
||||
element,
|
||||
// 2. key name
|
||||
() => {
|
||||
if (oper.runtimeCamelize) {
|
||||
pushFnCall(helper('camelize'), () => genExpression(oper.key, context))
|
||||
} else if (oper.runtimePrefix) {
|
||||
pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () =>
|
||||
} else if (oper.modifier) {
|
||||
pushMulti([`\`${oper.modifier}\${`, `}\``], () =>
|
||||
genExpression(oper.key, context),
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -63,8 +63,8 @@ export interface SetPropIRNode extends BaseIRNode {
|
|||
element: number
|
||||
key: IRExpression
|
||||
value: IRExpression
|
||||
modifier?: '.' | '^'
|
||||
runtimeCamelize: boolean
|
||||
runtimePrefix?: string
|
||||
}
|
||||
|
||||
export interface SetTextIRNode extends BaseIRNode {
|
||||
|
|
|
@ -38,14 +38,6 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
|
|||
}
|
||||
}
|
||||
|
||||
let prefix: string | undefined
|
||||
if (modifiers.includes('prop')) {
|
||||
prefix = injectPrefix(arg, '.')
|
||||
}
|
||||
if (modifiers.includes('attr')) {
|
||||
prefix = injectPrefix(arg, '^')
|
||||
}
|
||||
|
||||
if (!exp.content.trim()) {
|
||||
context.options.onError(
|
||||
createCompilerError(ErrorCodes.X_V_BIND_NO_EXPRESSION, loc),
|
||||
|
@ -64,15 +56,12 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => {
|
|||
key: arg,
|
||||
value: exp,
|
||||
runtimeCamelize: camel,
|
||||
runtimePrefix: prefix,
|
||||
modifier: modifiers.includes('prop')
|
||||
? '.'
|
||||
: modifiers.includes('attr')
|
||||
? '^'
|
||||
: undefined,
|
||||
},
|
||||
],
|
||||
)
|
||||
}
|
||||
|
||||
const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => {
|
||||
if (!arg.isStatic) {
|
||||
return prefix
|
||||
}
|
||||
arg.content = prefix + arg.content
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue