feat: generate specific function when the prop key is static (#97)

Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
ygj6 2024-01-22 23:03:39 +08:00 committed by GitHub
parent 51098cff94
commit 5028880e5b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 94 additions and 56 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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