From 9806d8a794651ca4138dfbe530acbbd36426a0e1 Mon Sep 17 00:00:00 2001 From: ygj6 <7699524+ygj6@users.noreply.github.com> Date: Sat, 23 Dec 2023 22:05:24 +0800 Subject: [PATCH] feat: prop and attr modifiers for v-bind --- .../__snapshots__/compile.spec.ts.snap | 14 +- .../__snapshots__/vBind.spec.ts.snap | 124 +++++++++++-- .../__snapshots__/vOnce.spec.ts.snap | 12 +- .../__tests__/transforms/vBind.spec.ts | 164 ++++++++++++++++-- packages/compiler-vapor/src/generate.ts | 8 +- packages/compiler-vapor/src/ir.ts | 1 + .../compiler-vapor/src/transforms/vBind.ts | 17 ++ packages/runtime-vapor/src/dom.ts | 30 +++- 8 files changed, 322 insertions(+), 48 deletions(-) diff --git a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap index 02c09554ee1..211377595c5 100644 --- a/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/__snapshots__/compile.spec.ts.snap @@ -121,7 +121,7 @@ export function render(_ctx) { `; exports[`compile > directives > v-pre > self-closing v-pre 1`] = ` -"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") @@ -133,14 +133,14 @@ export function render(_ctx) { _setText(n1, undefined, _ctx.bar) }) _effect(() => { - _setAttr(n2, "id", undefined, _ctx.foo) + _setDynamicProp(n2, "id", undefined, _ctx.foo) }) return n0 }" `; exports[`compile > directives > v-pre > should not affect siblings after it 1`] = ` -"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, createTextNode as _createTextNode, append as _append, effect as _effect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
{{ bar }}
") @@ -152,7 +152,7 @@ export function render(_ctx) { _setText(n1, undefined, _ctx.bar) }) _effect(() => { - _setAttr(n2, "id", undefined, _ctx.foo) + _setDynamicProp(n2, "id", undefined, _ctx.foo) }) return n0 }" @@ -179,7 +179,7 @@ export function render(_ctx) { `; exports[`compile > dynamic root nodes and interpolation 1`] = ` -"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, on as _on, effect as _effect, setText as _setText, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, createTextNode as _createTextNode, prepend as _prepend, insert as _insert, append as _append, on as _on, effect as _effect, setText as _setText, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("") @@ -196,7 +196,7 @@ export function render(_ctx) { _setText(n1, undefined, _ctx.count) _setText(n2, undefined, _ctx.count) _setText(n3, undefined, _ctx.count) - _setAttr(n4, "id", undefined, _ctx.count) + _setDynamicProp(n4, "id", undefined, _ctx.count) }) return n0 }" @@ -220,7 +220,7 @@ exports[`compile > expression parsing > v-bind 1`] = ` const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, key.value+1, undefined, _unref(foo)[key.value+1]()) + _setDynamicProp(n1, key.value+1, undefined, _unref(foo)[key.value+1]()) }) return n0 })()" diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap index 910d019cd66..67c818148c9 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -1,14 +1,42 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html +exports[`compiler v-bind > .attr modifier 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(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, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar) + }) + return n0 +}" +`; + exports[`compiler v-bind > .camel modifier 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, "fooBar", undefined, _ctx.id) + _setDynamicProp(n1, "fooBar", undefined, _ctx.id) }) return n0 }" @@ -22,77 +50,147 @@ export function render(_ctx) { const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id) + _setDynamicProp(n1, _camelize(_ctx.foo), undefined, _ctx.id) }) return n0 }" `; exports[`compiler v-bind > .camel modifier w/ no expression 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, "fooBar", undefined, _ctx.fooBar) + }) + return n0 +}" +`; + +exports[`compiler v-bind > .prop modifier (shortband) w/ no expression 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar) + }) + return n0 +}" +`; + +exports[`compiler v-bind > .prop modifier (shorthand) 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, ".fooBar", undefined, _ctx.id) + }) + return n0 +}" +`; + +exports[`compiler v-bind > .prop modifier 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, ".fooBar", undefined, _ctx.id) + }) + return n0 +}" +`; + +exports[`compiler v-bind > .prop modifier w/ dynamic arg 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; + +export function render(_ctx) { + const t0 = _template("
") + const n0 = t0() + const { 0: [n1],} = _children(n0) + _effect(() => { + _setDynamicProp(n1, \`.\${_ctx.fooBar}\`, undefined, _ctx.id) + }) + return n0 +}" +`; + +exports[`compiler v-bind > .prop modifier w/ no expression 1`] = ` +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, "fooBar", undefined, _ctx.fooBar) + _setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar) }) return n0 }" `; exports[`compiler v-bind > basic 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, "id", undefined, _ctx.id) + _setDynamicProp(n1, "id", undefined, _ctx.id) }) return n0 }" `; exports[`compiler v-bind > dynamic arg 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, _ctx.id, undefined, _ctx.id) + _setDynamicProp(n1, _ctx.id, undefined, _ctx.id) }) return n0 }" `; exports[`compiler v-bind > no expression (shorthand) 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, "camel-case", undefined, _ctx.camelCase) + _setDynamicProp(n1, "camel-case", undefined, _ctx.camelCase) }) return n0 }" `; exports[`compiler v-bind > no expression 1`] = ` -"import { template as _template, children as _children, effect as _effect, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, effect as _effect, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) _effect(() => { - _setAttr(n1, "id", undefined, _ctx.id) + _setDynamicProp(n1, "id", undefined, _ctx.id) }) return n0 }" diff --git a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap index 4b8e89d6d6f..7a761ed3c66 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vOnce.spec.ts.snap @@ -1,19 +1,19 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`compiler: v-once > as root node 1`] = ` -"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [n1],} = _children(n0) - _setAttr(n1, "id", undefined, _ctx.foo) + _setDynamicProp(n1, "id", undefined, _ctx.foo) return n0 }" `; exports[`compiler: v-once > basic 1`] = ` -"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setAttr as _setAttr, prepend as _prepend } from 'vue/vapor'; +"import { template as _template, children as _children, createTextNode as _createTextNode, setText as _setText, setDynamicProp as _setDynamicProp, prepend as _prepend } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") @@ -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) - _setAttr(n2, "class", undefined, _ctx.clz) + _setDynamicProp(n2, "class", undefined, _ctx.clz) _prepend(n3, n1) return n0 }" @@ -38,13 +38,13 @@ export function render(_ctx) { `; exports[`compiler: v-once > on nested plain element 1`] = ` -"import { template as _template, children as _children, setAttr as _setAttr } from 'vue/vapor'; +"import { template as _template, children as _children, setDynamicProp as _setDynamicProp } from 'vue/vapor'; export function render(_ctx) { const t0 = _template("
") const n0 = t0() const { 0: [, { 0: [n1],}],} = _children(n0) - _setAttr(n1, "id", undefined, _ctx.foo) + _setDynamicProp(n1, "id", undefined, _ctx.foo) return n0 }" `; diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index c81f2e51a48..0f97fdb5252 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -83,7 +83,7 @@ describe('compiler v-bind', () => { }) expect(code).matchSnapshot() - expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)') + expect(code).contains('_setDynamicProp(n1, "id", undefined, _ctx.id)') }) test('no expression', () => { @@ -110,7 +110,7 @@ describe('compiler v-bind', () => { }) expect(code).matchSnapshot() - expect(code).contains('_setAttr(n1, "id", undefined, _ctx.id)') + expect(code).contains('_setDynamicProp(n1, "id", undefined, _ctx.id)') }) test('no expression (shorthand)', () => { @@ -130,7 +130,7 @@ describe('compiler v-bind', () => { expect(code).matchSnapshot() expect(code).contains( - '_setAttr(n1, "camel-case", undefined, _ctx.camelCase)', + '_setDynamicProp(n1, "camel-case", undefined, _ctx.camelCase)', ) }) @@ -152,7 +152,7 @@ describe('compiler v-bind', () => { }) expect(code).matchSnapshot() - expect(code).contains('_setAttr(n1, _ctx.id, undefined, _ctx.id)') + expect(code).contains('_setDynamicProp(n1, _ctx.id, undefined, _ctx.id)') }) test('should error if empty expression', () => { @@ -192,7 +192,7 @@ describe('compiler v-bind', () => { }) expect(code).matchSnapshot() - expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.id)') + expect(code).contains('_setDynamicProp(n1, "fooBar", undefined, _ctx.id)') }) test('.camel modifier w/ no expression', () => { @@ -211,7 +211,9 @@ describe('compiler v-bind', () => { expect(code).matchSnapshot() expect(code).contains('effect') - expect(code).contains('_setAttr(n1, "fooBar", undefined, _ctx.fooBar)') + expect(code).contains( + '_setDynamicProp(n1, "fooBar", undefined, _ctx.fooBar)', + ) }) test('.camel modifier w/ dynamic arg', () => { @@ -232,18 +234,152 @@ describe('compiler v-bind', () => { expect(code).matchSnapshot() expect(code).contains('effect') expect(code).contains( - `_setAttr(n1, _camelize(_ctx.foo), undefined, _ctx.id)`, + `_setDynamicProp(n1, _camelize(_ctx.foo), undefined, _ctx.id)`, ) }) test.todo('.camel modifier w/ dynamic arg + prefixIdentifiers') - test.todo('.prop modifier') - test.todo('.prop modifier w/ no expression') - test.todo('.prop modifier w/ dynamic arg') + test('.prop modifier', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true, + }, + value: { + content: `id`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)') + }) + + test('.prop modifier w/ no expression', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true, + }, + value: { + content: `fooBar`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains( + '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)', + ) + }) + + test('.prop modifier w/ dynamic arg', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `fooBar`, + isStatic: false, + }, + value: { + content: `id`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains( + '_setDynamicProp(n1, `.${_ctx.fooBar}`, undefined, _ctx.id)', + ) + }) + test.todo('.prop modifier w/ dynamic arg + prefixIdentifiers') - test.todo('.prop modifier (shorthand)') - test.todo('.prop modifier (shortband) w/ no expression') - test.todo('.attr modifier') - test.todo('.attr modifier w/ no expression') + + test('.prop modifier (shorthand)', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true, + }, + value: { + content: `id`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains('_setDynamicProp(n1, ".fooBar", undefined, _ctx.id)') + }) + + test('.prop modifier (shortband) w/ no expression', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `.fooBar`, + isStatic: true, + }, + value: { + content: `fooBar`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains( + '_setDynamicProp(n1, ".fooBar", undefined, _ctx.fooBar)', + ) + }) + + test('.attr modifier', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `^foo-bar`, + isStatic: true, + }, + value: { + content: `id`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains('_setDynamicProp(n1, "^foo-bar", undefined, _ctx.id)') + }) + + test('.attr modifier w/ no expression', () => { + const { ir, code } = compileWithVBind(`
`) + + expect(ir.effect[0].operations[0]).toMatchObject({ + key: { + content: `^foo-bar`, + isStatic: true, + }, + value: { + content: `fooBar`, + isStatic: false, + }, + }) + + expect(code).matchSnapshot() + expect(code).contains('effect') + expect(code).contains( + '_setDynamicProp(n1, "^foo-bar", undefined, _ctx.fooBar)', + ) + }) }) diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index b79ac290192..fc18a3add39 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -391,16 +391,20 @@ function genOperation(oper: OperationNode, context: CodegenContext) { } function genSetProp(oper: SetPropIRNode, context: CodegenContext) { - const { pushFnCall, newline, vaporHelper, helper } = context + const { pushFnCall, pushMulti, newline, vaporHelper, helper } = context newline() pushFnCall( - vaporHelper('setAttr'), + vaporHelper('setDynamicProp'), `n${oper.element}`, // 2. key name () => { if (oper.runtimeCamelize) { pushFnCall(helper('camelize'), () => genExpression(oper.key, context)) + } else if (oper.runtimePrefix) { + pushMulti([`\`${oper.runtimePrefix}\${`, `}\``], () => + genExpression(oper.key, context), + ) } else { genExpression(oper.key, context) } diff --git a/packages/compiler-vapor/src/ir.ts b/packages/compiler-vapor/src/ir.ts index 2af5455d628..ae76aa92a03 100644 --- a/packages/compiler-vapor/src/ir.ts +++ b/packages/compiler-vapor/src/ir.ts @@ -61,6 +61,7 @@ export interface SetPropIRNode extends BaseIRNode { key: IRExpression value: IRExpression runtimeCamelize: boolean + runtimePrefix?: string } export interface SetTextIRNode extends BaseIRNode { diff --git a/packages/compiler-vapor/src/transforms/vBind.ts b/packages/compiler-vapor/src/transforms/vBind.ts index 84fd1ac4382..6a5fe6652ad 100644 --- a/packages/compiler-vapor/src/transforms/vBind.ts +++ b/packages/compiler-vapor/src/transforms/vBind.ts @@ -2,6 +2,7 @@ import { createCompilerError, createSimpleExpression, ErrorCodes, + SimpleExpressionNode, } from '@vue/compiler-core' import { camelize } from '@vue/shared' import { IRNodeTypes } from '../ir' @@ -30,6 +31,14 @@ 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), @@ -48,7 +57,15 @@ export const transformVBind: DirectiveTransform = (dir, node, context) => { key: arg, value: exp, runtimeCamelize: camel, + runtimePrefix: prefix, }, ], ) } + +const injectPrefix = (arg: SimpleExpressionNode, prefix: string) => { + if (!arg.isStatic) { + return prefix + } + arg.content = prefix + arg.content +} diff --git a/packages/runtime-vapor/src/dom.ts b/packages/runtime-vapor/src/dom.ts index d102433e3a1..23a1d854d2e 100644 --- a/packages/runtime-vapor/src/dom.ts +++ b/packages/runtime-vapor/src/dom.ts @@ -92,16 +92,34 @@ export function setAttr(el: Element, key: string, oldVal: any, newVal: any) { } } -export function setDynamicProp(el: Element, key: string, val: any) { +export function setDOMProp(el: any, key: string, oldVal: any, newVal: any) { + // TODO special checks + if (newVal !== oldVal) { + el[key] = newVal + } +} + +export function setDynamicProp( + el: Element, + key: string, + oldVal: any, + newVal: any, +) { if (key === 'class') { - setClass(el, void 0, val) + setClass(el, oldVal, newVal) } else if (key === 'style') { - setStyle(el as HTMLElement, void 0, val) - } else if (key in el) { - ;(el as any)[key] = val + setStyle(el as HTMLElement, oldVal, newVal) + } else if ( + key[0] === '.' + ? ((key = key.slice(1)), true) + : key[0] === '^' + ? ((key = key.slice(1)), false) + : key in el + ) { + setDOMProp(el, key, oldVal, newVal) } else { // TODO special checks - setAttr(el, key, void 0, val) + setAttr(el, key, oldVal, newVal) } }