From: Evan You Date: Wed, 11 Dec 2024 17:48:40 +0000 (+0800) Subject: wip: cache setProp prev value on element, simplify codegen X-Git-Tag: v3.6.0-alpha.1~16^2~157 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=dfe06f8f4e9254b2df32bb9026aab3a0371c5931;p=thirdparty%2Fvuejs%2Fcore.git wip: cache setProp prev value on element, simplify codegen Also separate `setClass`/`setClassIncremental` and `setStyle`/ `setStyleIncremental` --- 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 5710d104e5..f536750ea2 100644 --- a/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap +++ b/packages/compiler-vapor/__tests__/transforms/__snapshots__/vBind.spec.ts.snap @@ -330,6 +330,23 @@ export function render(_ctx) { }" `; +exports[`compiler v-bind > MathML global attributes should set as attribute 1`] = ` +"import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("") + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + _setAttr(n0, "autofucus", _ctx.autofucus) + _setAttr(n0, "dir", _ctx.dir) + _setAttr(n0, "displaystyle", _ctx.displaystyle) + _setAttr(n0, "mathcolor", _ctx.mathcolor) + _setAttr(n0, "tabindex", _ctx.tabindex) + }) + return n0 +}" +`; + exports[`compiler v-bind > MathML global attributes should set as dom prop 1`] = ` "import { setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("") @@ -348,6 +365,21 @@ export function render(_ctx) { }" `; +exports[`compiler v-bind > SVG global attributes should set as attribute 1`] = ` +"import { setAttr as _setAttr, renderEffect as _renderEffect, template as _template } from 'vue'; +const t0 = _template("") + +export function render(_ctx) { + const n0 = t0() + _renderEffect(() => { + _setAttr(n0, "id", _ctx.id) + _setAttr(n0, "lang", _ctx.lang) + _setAttr(n0, "tabindex", _ctx.tabindex) + }) + return n0 +}" +`; + exports[`compiler v-bind > SVG global attributes should set as dom prop 1`] = ` "import { setDOMProp as _setDOMProp, renderEffect as _renderEffect, template as _template } from 'vue'; const t0 = _template("") diff --git a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts index 87c7752aa3..261b26a741 100644 --- a/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vBind.spec.ts @@ -686,69 +686,6 @@ describe('compiler v-bind', () => { expect(code).contains(' _setAttr(n6, "width", _ctx.width)') }) - test('HTML global attributes should set as dom prop', () => { - const { code } = compileWithVBind(` -
- `) - - expect(code).matchSnapshot() - expect(code).contains( - '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))', - ) - expect(code).contains( - '_title !== _ctx.title && _setDOMProp(n0, "title", (_title = _ctx.title))', - ) - expect(code).contains( - '_lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))', - ) - expect(code).contains( - '_dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))', - ) - expect(code).contains( - '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))', - ) - }) - - test('SVG global attributes should set as dom prop', () => { - const { code } = compileWithVBind(` - - `) - - expect(code).matchSnapshot() - expect(code).contains( - '_id !== _ctx.id && _setDOMProp(n0, "id", (_id = _ctx.id))', - ) - expect(code).contains( - '_lang !== _ctx.lang && _setDOMProp(n0, "lang", (_lang = _ctx.lang))', - ) - expect(code).contains( - '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))', - ) - }) - - test('MathML global attributes should set as dom prop', () => { - const { code } = compileWithVBind(` - - `) - - expect(code).matchSnapshot() - expect(code).contains( - '_autofucus !== _ctx.autofucus && _setDOMProp(n0, "autofucus", (_autofucus = _ctx.autofucus))', - ) - expect(code).contains( - '_dir !== _ctx.dir && _setDOMProp(n0, "dir", (_dir = _ctx.dir))', - ) - expect(code).contains( - '_displaystyle !== _ctx.displaystyle && _setDOMProp(n0, "displaystyle", (_displaystyle = _ctx.displaystyle))', - ) - expect(code).contains( - '_mathcolor !== _ctx.mathcolor && _setDOMProp(n0, "mathcolor", (_mathcolor = _ctx.mathcolor))', - ) - expect(code).contains( - '_tabindex !== _ctx.tabindex && _setDOMProp(n0, "tabindex", (_tabindex = _ctx.tabindex))', - ) - }) - test(':innerHTML', () => { const { code } = compileWithVBind(`
diff --git a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts index 0aac082d65..3b744d0a7d 100644 --- a/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts +++ b/packages/compiler-vapor/__tests__/transforms/vOn.spec.ts @@ -106,6 +106,8 @@ describe('v-on', () => { }, }) + console.log(code) + expect(code).matchSnapshot() }) diff --git a/packages/compiler-vapor/src/generate.ts b/packages/compiler-vapor/src/generate.ts index bbcff0728c..a134882351 100644 --- a/packages/compiler-vapor/src/generate.ts +++ b/packages/compiler-vapor/src/generate.ts @@ -2,13 +2,7 @@ import type { CodegenOptions as BaseCodegenOptions, BaseCodegenResult, } from '@vue/compiler-dom' -import type { - BlockIRNode, - CoreHelper, - IREffect, - RootIRNode, - VaporHelper, -} from './ir' +import type { BlockIRNode, CoreHelper, RootIRNode, VaporHelper } from './ir' import { extend, remove } from '@vue/shared' import { genBlockContent } from './generators/block' import { genTemplates } from './generators/template' @@ -37,15 +31,6 @@ export class CodegenContext { delegates: Set = new Set() - processingRenderEffect: IREffect | undefined = undefined - allRenderEffectSeenNames: Record = Object.create(null) - shouldCacheRenderEffectDeps = (): boolean => { - // only need to generate effect deps when it's not nested in v-for - return !!( - this.processingRenderEffect && !this.processingRenderEffect.inVFor - ) - } - identifiers: Record = Object.create(null) block: BlockIRNode diff --git a/packages/compiler-vapor/src/generators/expression.ts b/packages/compiler-vapor/src/generators/expression.ts index c0f1c82124..69274aab56 100644 --- a/packages/compiler-vapor/src/generators/expression.ts +++ b/packages/compiler-vapor/src/generators/expression.ts @@ -1,4 +1,4 @@ -import { isArray, isGloballyAllowed } from '@vue/shared' +import { isGloballyAllowed } from '@vue/shared' import { BindingTypes, NewlineType, @@ -95,14 +95,7 @@ export function genExpression( ) if (i === ids.length - 1 && end < content.length) { - const rest = content.slice(end) - const last = frag[frag.length - 1] - if (hasMemberExpression && isArray(last)) { - // merge rest content into the last identifier's generated name - last[0] += rest - } else { - push([rest, NewlineType.Unknown]) - } + push([content.slice(end), NewlineType.Unknown]) } }) diff --git a/packages/compiler-vapor/src/generators/html.ts b/packages/compiler-vapor/src/generators/html.ts index 4eea9faa0b..72af699dd0 100644 --- a/packages/compiler-vapor/src/generators/html.ts +++ b/packages/compiler-vapor/src/generators/html.ts @@ -2,17 +2,15 @@ import type { CodegenContext } from '../generate' import type { SetHtmlIRNode } from '../ir' import { genExpression } from './expression' import { type CodeFragment, NEWLINE, genCall } from './utils' -import { processValues } from './prop' export function genSetHtml( oper: SetHtmlIRNode, context: CodegenContext, ): CodeFragment[] { - const { helper, shouldCacheRenderEffectDeps } = context + const { helper } = context const { value, element } = oper - let html = genExpression(value, context) - if (shouldCacheRenderEffectDeps()) { - processValues(context, [html]) - } - return [NEWLINE, ...genCall(helper('setHtml'), `n${element}`, html)] + return [ + NEWLINE, + ...genCall(helper('setHtml'), `n${element}`, genExpression(value, context)), + ] } diff --git a/packages/compiler-vapor/src/generators/operation.ts b/packages/compiler-vapor/src/generators/operation.ts index 6554b5e777..a82fc6e4d1 100644 --- a/packages/compiler-vapor/src/generators/operation.ts +++ b/packages/compiler-vapor/src/generators/operation.ts @@ -78,15 +78,16 @@ export function genEffects( ): CodeFragment[] { const { helper } = context const [frag, push, unshift] = buildCodeFragment() - const declareNames = new Set() let operationsCount = 0 for (let i = 0; i < effects.length; i++) { - const effect = (context.processingRenderEffect = effects[i]) + const effect = effects[i] operationsCount += effect.operations.length - const frags = genEffect(effect, context, declareNames) - const needSemi = frag[frag.length - 1] === ')' && frags[0] === '(' + const frags = genEffect(effect, context) i > 0 && push(NEWLINE) - push(needSemi ? ';' : undefined, ...frags) + if (frag[frag.length - 1] === ')' && frags[0] === '(') { + push(';') + } + push(...frags) } const newLineCount = frag.filter(frag => frag === NEWLINE).length @@ -100,60 +101,21 @@ export function genEffects( push(`)`) } - // declare variables: let _foo, _bar - if (declareNames.size) { - frag.splice(1, 0, `let ${[...declareNames].join(', ')}`, NEWLINE) - } return frag } export function genEffect( { operations }: IREffect, context: CodegenContext, - allDeclareNames: Set, ): CodeFragment[] { - const { processingRenderEffect } = context const [frag, push] = buildCodeFragment() - const { declareNames, earlyCheckExps } = processingRenderEffect! const operationsExps = genOperations(operations, context) - - if (declareNames.size) { - allDeclareNames.add([...declareNames].join(', ')) - } - const newlineCount = operationsExps.filter(frag => frag === NEWLINE).length + if (newlineCount > 1) { - // multiline check expression: if (_foo !== _ctx.foo || _bar !== _ctx.bar) { - const checkExpsStart: CodeFragment[] = - earlyCheckExps.length > 0 - ? [`if(`, ...earlyCheckExps.join(' || '), `) {`, INDENT_START] - : [] - const checkExpsEnd: CodeFragment[] = - earlyCheckExps.length > 0 ? [INDENT_END, NEWLINE, '}'] : [] - // assignment: _foo = _ctx.foo; _bar = _ctx.bar - const assignmentExps: CodeFragment[] = - earlyCheckExps.length > 0 - ? [NEWLINE, ...earlyCheckExps.map(c => c.replace('!==', '=')).join(';')] - : [] - push( - ...checkExpsStart, - ...operationsExps, - ...assignmentExps, - ...checkExpsEnd, - ) + push(...operationsExps) } else { - // single line check expression: (_foo !== _ctx.foo || _bar !== _ctx.bar) && - const multiple = earlyCheckExps.length > 1 - const checkExps: CodeFragment[] = - earlyCheckExps.length > 0 - ? [ - multiple ? `(` : undefined, - ...earlyCheckExps.join(' || '), - multiple ? `)` : undefined, - ' && ', - ] - : [] - push(...checkExps, ...operationsExps.filter(frag => frag !== NEWLINE)) + push(...operationsExps.filter(frag => frag !== NEWLINE)) } return frag diff --git a/packages/compiler-vapor/src/generators/prop.ts b/packages/compiler-vapor/src/generators/prop.ts index fef5e09875..b0137f97ab 100644 --- a/packages/compiler-vapor/src/generators/prop.ts +++ b/packages/compiler-vapor/src/generators/prop.ts @@ -21,19 +21,31 @@ import { genMulti, } from './utils' import { - attributeCache, canSetValueDirectly, - isArray, isHTMLGlobalAttr, - isHTMLTag, - isMathMLGlobalAttr, - isMathMLTag, - isSVGTag, - isSvgGlobalAttr, - shouldSetAsAttr, toHandlerKey, } from '@vue/shared' +export type HelperConfig = { + name: VaporHelper + needKey?: boolean + acceptRoot?: boolean +} + +// this should be kept in sync with runtime-vapor/src/dom/prop.ts +const helpers = { + setText: { name: 'setText' }, + setHtml: { name: 'setHtml' }, + setClass: { name: 'setClass' }, + setClassIncremental: { name: 'setClassIncremental' }, + setStyle: { name: 'setStyle' }, + setStyleIncremental: { name: 'setStyleIncremental' }, + setValue: { name: 'setValue' }, + setAttr: { name: 'setAttr', needKey: true }, + setDOMProp: { name: 'setDOMProp', needKey: true }, + setDynamicProps: { name: 'setDynamicProps', acceptRoot: true }, +} as const satisfies Partial> + // only the static key prop will reach here export function genSetProp( oper: SetPropIRNode, @@ -43,31 +55,19 @@ export function genSetProp( const { prop: { key, values, modifier }, tag, + root, } = oper - const { helperName, omitKey } = getRuntimeHelper(tag, key.content, modifier) + const resolvedHelper = getRuntimeHelper(tag, key.content, modifier, root) const propValue = genPropValue(values, context) - const { prevValueName, shouldWrapInParentheses } = processPropValues( - context, - helperName, - [propValue], - ) return [ NEWLINE, - ...(prevValueName - ? [shouldWrapInParentheses ? `(` : undefined, `${prevValueName} = `] - : []), ...genCall( - [helper(helperName), null], + [helper(resolvedHelper.name), null], `n${oper.element}`, - omitKey ? false : genExpression(key, context), - ...(prevValueName ? [`${prevValueName}`] : []), + resolvedHelper.needKey ? genExpression(key, context) : false, propValue, - // only `setClass` and `setStyle` need merge inherit attr - oper.root && (helperName === 'setClass' || helperName === 'setStyle') - ? 'true' - : undefined, + root && resolvedHelper.acceptRoot ? 'true' : undefined, ), - ...(prevValueName && shouldWrapInParentheses ? [`)`] : []), ] } @@ -84,24 +84,14 @@ export function genDynamicProps( ? genLiteralObjectProps([props], context) // dynamic arg props : genExpression(props.value, context), ) // v-bind="" - const { prevValueName, shouldWrapInParentheses } = processPropValues( - context, - 'setDynamicProps', - values, - ) return [ NEWLINE, - ...(prevValueName - ? [shouldWrapInParentheses ? `(` : undefined, `${prevValueName} = `] - : []), ...genCall( helper('setDynamicProps'), `n${oper.element}`, - ...(prevValueName ? [`${prevValueName}`] : []), genMulti(DELIMITERS_ARRAY, ...values), oper.root && 'true', ), - ...(prevValueName && shouldWrapInParentheses ? [`)`] : []), ] } @@ -161,161 +151,75 @@ export function genPropValue( ) } +// TODO +// - 1. textContent + innerHTML Known base dom properties in https://developer.mozilla.org/en-US/docs/Web/API/Element +// - 2. special handling (class / style) +// - 3. SVG: always attribute +// - 4. Custom Elements +// - always properties unless known global attr or has hyphen (aria- / data-) +// - 5. Normal Elements +// - 1. Known shared dom properties: +// - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement +// - 2. Each element's known dom properties +// - 3. Fallback to attribute + function getRuntimeHelper( tag: string, keyName: string, modifier: '.' | '^' | undefined, -) { + root: boolean, +): HelperConfig { const tagName = tag.toUpperCase() - let helperName: VaporHelper - let omitKey = false - if (modifier) { if (modifier === '.') { - const helper = getSpecialHelper(keyName, tagName) - if (helper) { - helperName = helper.name - omitKey = helper.omitKey - } else { - helperName = 'setDOMProp' - omitKey = false - } + return getSpecialHelper(keyName, tagName, root) || helpers.setDOMProp } else { - helperName = 'setAttr' + return helpers.setAttr } } else { - const attrCacheKey = `${tagName}_${keyName}` - const helper = getSpecialHelper(keyName, tagName) + const helper = getSpecialHelper(keyName, tagName, root) if (helper) { - helperName = helper.name - omitKey = helper.omitKey - } else if ( - attributeCache[attrCacheKey] === undefined - ? (attributeCache[attrCacheKey] = shouldSetAsAttr(tagName, keyName)) - : attributeCache[attrCacheKey] - ) { - helperName = 'setAttr' - } else if ( - (isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) || - (isSVGTag(tag) && isSvgGlobalAttr(keyName)) || - (isMathMLTag(tag) && isMathMLGlobalAttr(keyName)) - ) { - helperName = 'setDOMProp' + return helper + } else if (tagName.includes('-')) { + // custom element + if (isHTMLGlobalAttr(keyName) || keyName.includes('-')) { + return helpers.setAttr + } else { + return helpers.setDOMProp + } + } else if (/[A-Z]/.test(keyName)) { + return helpers.setDOMProp } else { - helperName = 'setDynamicProp' + return helpers.setAttr } } - return { helperName, omitKey } } -const specialHelpers: Record = - { - class: { name: 'setClass', omitKey: true }, - style: { name: 'setStyle', omitKey: true }, - innerHTML: { name: 'setHtml', omitKey: true }, - textContent: { name: 'setText', omitKey: true }, - } - const getSpecialHelper = ( keyName: string, tagName: string, -): { name: VaporHelper; omitKey: boolean } | null => { + root: boolean, +): HelperConfig | undefined => { // special case for 'value' property if (keyName === 'value' && canSetValueDirectly(tagName)) { - return { name: 'setValue', omitKey: true } - } - - return specialHelpers[keyName] || null -} - -// those runtime helpers will return the prevValue -const helpersNeedCachedReturnValue = [ - 'setStyle', - 'setDynamicProp', - 'setDynamicProps', -] - -function processPropValues( - context: CodegenContext, - helperName: string, - values: CodeFragment[][], -): { prevValueName: string | undefined; shouldWrapInParentheses: boolean } { - const { shouldCacheRenderEffectDeps, processingRenderEffect } = context - // single-line render effect and the operation needs cache return a value, - // the expression needs to be wrapped in parentheses. - // e.g. _foo === _ctx.foo && (_foo = _setStyle(...)) - let shouldWrapInParentheses: boolean = false - let prevValueName - if (shouldCacheRenderEffectDeps()) { - const needReturnValue = helpersNeedCachedReturnValue.includes(helperName) - processValues(context, values, !needReturnValue) - const { declareNames } = processingRenderEffect! - // if the operation needs to cache the return value and has multiple declareNames, - // combine them into a single name as the return value name. - if (declareNames.size > 0 && needReturnValue) { - prevValueName = [...declareNames].join('') - declareNames.add(prevValueName) - } - shouldWrapInParentheses = processingRenderEffect!.operations.length === 1 + return helpers.setValue } - return { prevValueName, shouldWrapInParentheses } -} - -export function processValues( - context: CodegenContext, - values: CodeFragment[][], - needRewrite: boolean = true, -): string[] { - const allCheckExps: string[] = [] - values.forEach(value => { - const checkExps = processValue(context, value, needRewrite) - if (checkExps) allCheckExps.push(...checkExps, ' && ') - }) - - return allCheckExps.length > 0 - ? (context.processingRenderEffect!.earlyCheckExps = [ - ...new Set(allCheckExps), - ]) - : [] -} - -function processValue( - context: CodegenContext, - values: CodeFragment[], - needRewrite: boolean = true, -): string[] | undefined { - const { processingRenderEffect, allRenderEffectSeenNames } = context - const { declareNames, rewrittenNames, earlyCheckExps, operations } = - processingRenderEffect! - - const isSingleLine = operations.length === 1 - for (const frag of values) { - if (!isArray(frag)) continue - // [code, newlineIndex, loc, name] -> [(_name = code), newlineIndex, loc, name] - const [newName, , , rawName] = frag - if (rawName) { - let name = rawName.replace(/[^\w]/g, '_') - if (rewrittenNames.has(name)) continue - rewrittenNames.add(name) - name = `_${name}` - if (declareNames.has(name)) continue - - if (allRenderEffectSeenNames[name] === undefined) - allRenderEffectSeenNames[name] = 0 - else name += ++allRenderEffectSeenNames[name] - - declareNames.add(name) - earlyCheckExps.push(`${name} !== ${newName}`) - - if (needRewrite && isSingleLine) { - // replace the original code fragment with the assignment expression - frag[0] = `(${name} = ${newName})` - } + if (root) { + if (keyName === 'class') { + return helpers.setClassIncremental + } else if (keyName === 'style') { + return helpers.setStyleIncremental } } - if (earlyCheckExps.length > 0) { - return [[...new Set(earlyCheckExps)].join(' && ')] + if (keyName === 'class') { + return helpers.setClass + } else if (keyName === 'style') { + return helpers.setStyle + } else if (keyName === 'innerHTML') { + return helpers.setHtml + } else if (keyName === 'textContent') { + return helpers.setText } } diff --git a/packages/compiler-vapor/src/generators/text.ts b/packages/compiler-vapor/src/generators/text.ts index 30ace31a7b..8d8b849c80 100644 --- a/packages/compiler-vapor/src/generators/text.ts +++ b/packages/compiler-vapor/src/generators/text.ts @@ -8,18 +8,14 @@ import { genCall, genMulti, } from './utils' -import { processValues } from './prop' export function genSetText( oper: SetTextIRNode, context: CodegenContext, ): CodeFragment[] { - const { helper, shouldCacheRenderEffectDeps } = context + const { helper } = context const { element, values } = oper const texts = values.map(value => genExpression(value, context)) - if (shouldCacheRenderEffectDeps()) { - processValues(context, texts) - } return [NEWLINE, ...genCall(helper('setText'), `n${element}`, ...texts)] } diff --git a/packages/compiler-vapor/src/ir/index.ts b/packages/compiler-vapor/src/ir/index.ts index 7dabd0dc2e..f1ca98325e 100644 --- a/packages/compiler-vapor/src/ir/index.ts +++ b/packages/compiler-vapor/src/ir/index.ts @@ -260,12 +260,7 @@ export interface IRDynamicInfo { export interface IREffect { expressions: SimpleExpressionNode[] - identifiers: string[] operations: OperationNode[] - declareNames: Set - rewrittenNames: Set - earlyCheckExps: string[] - inVFor: boolean } type Overwrite = Pick> & diff --git a/packages/compiler-vapor/src/transform.ts b/packages/compiler-vapor/src/transform.ts index 0b4f71f8d7..69e681745d 100644 --- a/packages/compiler-vapor/src/transform.ts +++ b/packages/compiler-vapor/src/transform.ts @@ -5,7 +5,6 @@ import { type CompilerCompatOptions, type ElementNode, ElementTypes, - type ExpressionNode, NodeTypes, type RootNode, type SimpleExpressionNode, @@ -13,16 +12,8 @@ import { defaultOnError, defaultOnWarn, isVSlot, - walkIdentifiers, } from '@vue/compiler-dom' -import { - EMPTY_OBJ, - NOOP, - extend, - isArray, - isString, - looseEqual, -} from '@vue/shared' +import { EMPTY_OBJ, NOOP, extend, isArray, isString } from '@vue/shared' import { type BlockIRNode, DynamicFlag, @@ -151,10 +142,8 @@ export class TransformContext { if (this.inVOnce || expressions.length === 0) { return this.registerOperation(...operations) } - const ids = new Set() - expressions.forEach(exp => extractIdentifiers(ids, exp)) const existing = this.block.effect.find(e => - looseEqual(e.identifiers, Array.from(ids)), + isSameExpression(e.expressions, expressions), ) if (existing) { existing.operations.push(...operations) @@ -162,14 +151,18 @@ export class TransformContext { this.block.effect.push({ expressions, operations, - earlyCheckExps: [], - declareNames: new Set(), - rewrittenNames: new Set(), - inVFor: this.inVFor > 0, - identifiers: Array.from(ids), }) } + + function isSameExpression( + a: SimpleExpressionNode[], + b: SimpleExpressionNode[], + ) { + if (a.length !== b.length) return false + return a.every((exp, i) => exp.content === b[i].content) + } } + registerOperation(...node: OperationNode[]): void { this.block.operation.push(...node) } @@ -304,11 +297,3 @@ export function createStructuralDirectiveTransform( } } } - -function extractIdentifiers(ids: Set, node: ExpressionNode) { - if (node.ast) { - walkIdentifiers(node.ast, n => ids.add(n.name), true) - } else if (node.ast === null) { - ids.add((node as SimpleExpressionNode).content) - } -} diff --git a/packages/runtime-dom/src/index.ts b/packages/runtime-dom/src/index.ts index 4f59f1f16a..60c32ce5d3 100644 --- a/packages/runtime-dom/src/index.ts +++ b/packages/runtime-dom/src/index.ts @@ -311,3 +311,14 @@ export const initDirectivesForSSR: () => void = __SSR__ export * from '@vue/runtime-core' export * from './jsx' + +// VAPOR ----------------------------------------------------------------------- + +/** + * @internal + */ +export { patchStyle } from './modules/style' +/** + * @internal + */ +export { shouldSetAsProp } from './patchProp' diff --git a/packages/runtime-dom/src/modules/props.ts b/packages/runtime-dom/src/modules/props.ts index 98608831a9..a28290acf4 100644 --- a/packages/runtime-dom/src/modules/props.ts +++ b/packages/runtime-dom/src/modules/props.ts @@ -1,5 +1,5 @@ import { DeprecationTypes, compatUtils, warn } from '@vue/runtime-core' -import { includeBooleanAttr } from '@vue/shared' +import { canSetValueDirectly, includeBooleanAttr } from '@vue/shared' import { unsafeToTrustedHTML } from '../nodeOps' // functions. The user is responsible for using them with only trusted content. @@ -24,12 +24,7 @@ export function patchDOMProp( const tag = el.tagName - if ( - key === 'value' && - tag !== 'PROGRESS' && - // custom elements may use _value internally - !tag.includes('-') - ) { + if (key === 'value' && canSetValueDirectly(tag)) { // #4956: