mirror of https://github.com/vuejs/core.git
fix(custom-elements): use strict number casting
close #4946 close #2598 close #2604 This commit also refactors internal usage of previous loose implementation of `toNumber` to the stricter version where applicable. Use of `looseToNumber` is preserved for `v-model.number` modifier to ensure backwards compatibility and consistency with Vue 2 behavior.
This commit is contained in:
parent
efa2ac54d9
commit
7d0c63ff43
|
@ -2,9 +2,9 @@ import {
|
|||
extend,
|
||||
looseEqual,
|
||||
looseIndexOf,
|
||||
looseToNumber,
|
||||
NOOP,
|
||||
toDisplayString,
|
||||
toNumber
|
||||
toDisplayString
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
ComponentPublicInstance,
|
||||
|
@ -148,7 +148,7 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
|
|||
$createElement: () => compatH,
|
||||
_c: () => compatH,
|
||||
_o: () => legacyMarkOnce,
|
||||
_n: () => toNumber,
|
||||
_n: () => looseToNumber,
|
||||
_s: () => toDisplayString,
|
||||
_l: () => renderList,
|
||||
_t: i => legacyRenderSlot.bind(null, i),
|
||||
|
|
|
@ -10,8 +10,8 @@ import {
|
|||
isObject,
|
||||
isString,
|
||||
isOn,
|
||||
toNumber,
|
||||
UnionToIntersection
|
||||
UnionToIntersection,
|
||||
looseToNumber
|
||||
} from '@vue/shared'
|
||||
import {
|
||||
ComponentInternalInstance,
|
||||
|
@ -126,7 +126,7 @@ export function emit(
|
|||
args = rawArgs.map(a => (isString(a) ? a.trim() : a))
|
||||
}
|
||||
if (number) {
|
||||
args = rawArgs.map(toNumber)
|
||||
args = rawArgs.map(looseToNumber)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,12 @@ import {
|
|||
} from '../renderer'
|
||||
import { queuePostFlushCb } from '../scheduler'
|
||||
import { filterSingleRoot, updateHOCHostEl } from '../componentRenderUtils'
|
||||
import { pushWarningContext, popWarningContext, warn } from '../warning'
|
||||
import {
|
||||
pushWarningContext,
|
||||
popWarningContext,
|
||||
warn,
|
||||
assertNumber
|
||||
} from '../warning'
|
||||
import { handleError, ErrorCodes } from '../errorHandling'
|
||||
|
||||
export interface SuspenseProps {
|
||||
|
@ -419,6 +424,10 @@ function createSuspenseBoundary(
|
|||
} = rendererInternals
|
||||
|
||||
const timeout = toNumber(vnode.props && vnode.props.timeout)
|
||||
if (__DEV__) {
|
||||
assertNumber(timeout, `Suspense timeout`)
|
||||
}
|
||||
|
||||
const suspense: SuspenseBoundary = {
|
||||
vnode,
|
||||
parent,
|
||||
|
|
|
@ -104,7 +104,7 @@ export { useSSRContext, ssrContextKey } from './helpers/useSsrContext'
|
|||
|
||||
export { createRenderer, createHydrationRenderer } from './renderer'
|
||||
export { queuePostFlushCb } from './scheduler'
|
||||
export { warn } from './warning'
|
||||
export { warn, assertNumber } from './warning'
|
||||
export {
|
||||
handleError,
|
||||
callWithErrorHandling,
|
||||
|
|
|
@ -162,3 +162,15 @@ function formatProp(key: string, value: unknown, raw?: boolean): any {
|
|||
return raw ? value : [`${key}=`, value]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function assertNumber(val: unknown, type: string) {
|
||||
if (!__DEV__) return
|
||||
if (typeof val !== 'number') {
|
||||
warn(`${type} is not a valid number - ` + `got ${JSON.stringify(val)}.`)
|
||||
} else if (isNaN(val)) {
|
||||
warn(`${type} is NaN - ` + 'the duration expression might be incorrect.')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import {
|
|||
BaseTransition,
|
||||
BaseTransitionProps,
|
||||
h,
|
||||
warn,
|
||||
assertNumber,
|
||||
FunctionalComponent,
|
||||
compatUtils,
|
||||
DeprecationTypes
|
||||
|
@ -283,24 +283,10 @@ function normalizeDuration(
|
|||
|
||||
function NumberOf(val: unknown): number {
|
||||
const res = toNumber(val)
|
||||
if (__DEV__) validateDuration(res)
|
||||
if (__DEV__) assertNumber(res, '<transition> explicit duration')
|
||||
return res
|
||||
}
|
||||
|
||||
function validateDuration(val: unknown) {
|
||||
if (typeof val !== 'number') {
|
||||
warn(
|
||||
`<transition> explicit duration is not a valid number - ` +
|
||||
`got ${JSON.stringify(val)}.`
|
||||
)
|
||||
} else if (isNaN(val)) {
|
||||
warn(
|
||||
`<transition> explicit duration is NaN - ` +
|
||||
'the duration expression might be incorrect.'
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function addTransitionClass(el: Element, cls: string) {
|
||||
cls.split(/\s+/).forEach(c => c && el.classList.add(c))
|
||||
;(
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
looseEqual,
|
||||
looseIndexOf,
|
||||
invokeArrayFns,
|
||||
toNumber,
|
||||
looseToNumber,
|
||||
isSet
|
||||
} from '@vue/shared'
|
||||
|
||||
|
@ -54,7 +54,7 @@ export const vModelText: ModelDirective<
|
|||
domValue = domValue.trim()
|
||||
}
|
||||
if (castToNumber) {
|
||||
domValue = toNumber(domValue)
|
||||
domValue = looseToNumber(domValue)
|
||||
}
|
||||
el._assign(domValue)
|
||||
})
|
||||
|
@ -88,7 +88,10 @@ export const vModelText: ModelDirective<
|
|||
if (trim && el.value.trim() === value) {
|
||||
return
|
||||
}
|
||||
if ((number || el.type === 'number') && toNumber(el.value) === value) {
|
||||
if (
|
||||
(number || el.type === 'number') &&
|
||||
looseToNumber(el.value) === value
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -182,7 +185,7 @@ export const vModelSelect: ModelDirective<HTMLSelectElement> = {
|
|||
const selectedVal = Array.prototype.filter
|
||||
.call(el.options, (o: HTMLOptionElement) => o.selected)
|
||||
.map((o: HTMLOptionElement) =>
|
||||
number ? toNumber(getValue(o)) : getValue(o)
|
||||
number ? looseToNumber(getValue(o)) : getValue(o)
|
||||
)
|
||||
el._assign(
|
||||
el.multiple
|
||||
|
|
|
@ -153,11 +153,23 @@ export const def = (obj: object, key: string | symbol, value: any) => {
|
|||
})
|
||||
}
|
||||
|
||||
export const toNumber = (val: any): any => {
|
||||
/**
|
||||
* "123-foo" will be parsed to 123
|
||||
* This is used for the .number modifier in v-model
|
||||
*/
|
||||
export const looseToNumber = (val: any): any => {
|
||||
const n = parseFloat(val)
|
||||
return isNaN(n) ? val : n
|
||||
}
|
||||
|
||||
/**
|
||||
* "123-foo" will be returned as-is
|
||||
*/
|
||||
export const toNumber = (val: any): any => {
|
||||
const n = Number(val)
|
||||
return isNaN(n) ? val : n
|
||||
}
|
||||
|
||||
let _globalThis: any
|
||||
export const getGlobalThis = (): any => {
|
||||
return (
|
||||
|
|
Loading…
Reference in New Issue