|
|
@@ -118,7 +118,6 @@ export interface RendererOptions<
|
|
|
parentSuspense?: SuspenseBoundary | null,
|
|
|
unmountChildren?: UnmountChildrenFn
|
|
|
): void
|
|
|
- forcePatchProp?(el: HostElement, key: string): boolean
|
|
|
insert(el: HostNode, parent: HostElement, anchor?: HostNode | null): void
|
|
|
remove(el: HostNode): void
|
|
|
createElement(
|
|
|
@@ -288,99 +287,6 @@ export const queuePostRenderEffect = __FEATURE_SUSPENSE__
|
|
|
? queueEffectWithSuspense
|
|
|
: queuePostFlushCb
|
|
|
|
|
|
-export const setRef = (
|
|
|
- rawRef: VNodeNormalizedRef,
|
|
|
- oldRawRef: VNodeNormalizedRef | null,
|
|
|
- parentSuspense: SuspenseBoundary | null,
|
|
|
- vnode: VNode,
|
|
|
- isUnmount = false
|
|
|
-) => {
|
|
|
- if (isArray(rawRef)) {
|
|
|
- rawRef.forEach((r, i) =>
|
|
|
- setRef(
|
|
|
- r,
|
|
|
- oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
|
|
- parentSuspense,
|
|
|
- vnode,
|
|
|
- isUnmount
|
|
|
- )
|
|
|
- )
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- if (isAsyncWrapper(vnode) && !isUnmount) {
|
|
|
- // when mounting async components, nothing needs to be done,
|
|
|
- // because the template ref is forwarded to inner component
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- const refValue =
|
|
|
- vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
|
|
- ? getExposeProxy(vnode.component!) || vnode.component!.proxy
|
|
|
- : vnode.el
|
|
|
- const value = isUnmount ? null : refValue
|
|
|
-
|
|
|
- const { i: owner, r: ref } = rawRef
|
|
|
- if (__DEV__ && !owner) {
|
|
|
- warn(
|
|
|
- `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
|
|
|
- `A vnode with ref must be created inside the render function.`
|
|
|
- )
|
|
|
- return
|
|
|
- }
|
|
|
- const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
|
|
|
- const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
|
|
|
- const setupState = owner.setupState
|
|
|
-
|
|
|
- // dynamic ref changed. unset old ref
|
|
|
- if (oldRef != null && oldRef !== ref) {
|
|
|
- if (isString(oldRef)) {
|
|
|
- refs[oldRef] = null
|
|
|
- if (hasOwn(setupState, oldRef)) {
|
|
|
- setupState[oldRef] = null
|
|
|
- }
|
|
|
- } else if (isRef(oldRef)) {
|
|
|
- oldRef.value = null
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if (isString(ref)) {
|
|
|
- const doSet = () => {
|
|
|
- if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
|
|
|
- registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
|
|
|
- } else {
|
|
|
- refs[ref] = value
|
|
|
- }
|
|
|
- if (hasOwn(setupState, ref)) {
|
|
|
- setupState[ref] = value
|
|
|
- }
|
|
|
- }
|
|
|
- // #1789: for non-null values, set them after render
|
|
|
- // null values means this is unmount and it should not overwrite another
|
|
|
- // ref with the same key
|
|
|
- if (value) {
|
|
|
- ;(doSet as SchedulerJob).id = -1
|
|
|
- queuePostRenderEffect(doSet, parentSuspense)
|
|
|
- } else {
|
|
|
- doSet()
|
|
|
- }
|
|
|
- } else if (isRef(ref)) {
|
|
|
- const doSet = () => {
|
|
|
- ref.value = value
|
|
|
- }
|
|
|
- if (value) {
|
|
|
- ;(doSet as SchedulerJob).id = -1
|
|
|
- queuePostRenderEffect(doSet, parentSuspense)
|
|
|
- } else {
|
|
|
- doSet()
|
|
|
- }
|
|
|
- } else if (isFunction(ref)) {
|
|
|
- callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
|
|
|
- } else if (__DEV__) {
|
|
|
- warn('Invalid template ref type:', value, `(${typeof value})`)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* The createRenderer function accepts two generic arguments:
|
|
|
* HostNode and HostElement, corresponding to Node and Element types in the
|
|
|
@@ -444,7 +350,6 @@ function baseCreateRenderer(
|
|
|
insert: hostInsert,
|
|
|
remove: hostRemove,
|
|
|
patchProp: hostPatchProp,
|
|
|
- forcePatchProp: hostForcePatchProp,
|
|
|
createElement: hostCreateElement,
|
|
|
createText: hostCreateText,
|
|
|
createComment: hostCreateComment,
|
|
|
@@ -767,7 +672,7 @@ function baseCreateRenderer(
|
|
|
// props
|
|
|
if (props) {
|
|
|
for (const key in props) {
|
|
|
- if (!isReservedProp(key)) {
|
|
|
+ if (key !== 'value' && !isReservedProp(key)) {
|
|
|
hostPatchProp(
|
|
|
el,
|
|
|
key,
|
|
|
@@ -781,6 +686,18 @@ function baseCreateRenderer(
|
|
|
)
|
|
|
}
|
|
|
}
|
|
|
+ /**
|
|
|
+ * Special case for setting value on DOM elements:
|
|
|
+ * - it can be order-sensitive (e.g. should be set *after* min/max, #2325, #4024)
|
|
|
+ * - it needs to be forced (#1471)
|
|
|
+ * #2353 proposes adding another renderer option to configure this, but
|
|
|
+ * the properties affects are so finite it is worth special casing it
|
|
|
+ * here to reduce the complexity. (Special casing it also should not
|
|
|
+ * affect non-DOM renderers)
|
|
|
+ */
|
|
|
+ if ('value' in props) {
|
|
|
+ hostPatchProp(el, 'value', null, props.value)
|
|
|
+ }
|
|
|
if ((vnodeHook = props.onVnodeBeforeMount)) {
|
|
|
invokeVNodeHook(vnodeHook, parentComponent, vnode)
|
|
|
}
|
|
|
@@ -967,10 +884,8 @@ function baseCreateRenderer(
|
|
|
const key = propsToUpdate[i]
|
|
|
const prev = oldProps[key]
|
|
|
const next = newProps[key]
|
|
|
- if (
|
|
|
- next !== prev ||
|
|
|
- (hostForcePatchProp && hostForcePatchProp(el, key))
|
|
|
- ) {
|
|
|
+ // #1471 force patch value
|
|
|
+ if (next !== prev || key === 'value') {
|
|
|
hostPatchProp(
|
|
|
el,
|
|
|
key,
|
|
|
@@ -1104,10 +1019,8 @@ function baseCreateRenderer(
|
|
|
if (isReservedProp(key)) continue
|
|
|
const next = newProps[key]
|
|
|
const prev = oldProps[key]
|
|
|
- if (
|
|
|
- next !== prev ||
|
|
|
- (hostForcePatchProp && hostForcePatchProp(el, key))
|
|
|
- ) {
|
|
|
+ // defer patching value
|
|
|
+ if (next !== prev && key !== 'value') {
|
|
|
hostPatchProp(
|
|
|
el,
|
|
|
key,
|
|
|
@@ -1138,6 +1051,9 @@ function baseCreateRenderer(
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+ if ('value' in newProps) {
|
|
|
+ hostPatchProp(el, 'value', oldProps.value, newProps.value)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -2418,6 +2334,99 @@ function baseCreateRenderer(
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+export function setRef(
|
|
|
+ rawRef: VNodeNormalizedRef,
|
|
|
+ oldRawRef: VNodeNormalizedRef | null,
|
|
|
+ parentSuspense: SuspenseBoundary | null,
|
|
|
+ vnode: VNode,
|
|
|
+ isUnmount = false
|
|
|
+) {
|
|
|
+ if (isArray(rawRef)) {
|
|
|
+ rawRef.forEach((r, i) =>
|
|
|
+ setRef(
|
|
|
+ r,
|
|
|
+ oldRawRef && (isArray(oldRawRef) ? oldRawRef[i] : oldRawRef),
|
|
|
+ parentSuspense,
|
|
|
+ vnode,
|
|
|
+ isUnmount
|
|
|
+ )
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isAsyncWrapper(vnode) && !isUnmount) {
|
|
|
+ // when mounting async components, nothing needs to be done,
|
|
|
+ // because the template ref is forwarded to inner component
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const refValue =
|
|
|
+ vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT
|
|
|
+ ? getExposeProxy(vnode.component!) || vnode.component!.proxy
|
|
|
+ : vnode.el
|
|
|
+ const value = isUnmount ? null : refValue
|
|
|
+
|
|
|
+ const { i: owner, r: ref } = rawRef
|
|
|
+ if (__DEV__ && !owner) {
|
|
|
+ warn(
|
|
|
+ `Missing ref owner context. ref cannot be used on hoisted vnodes. ` +
|
|
|
+ `A vnode with ref must be created inside the render function.`
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const oldRef = oldRawRef && (oldRawRef as VNodeNormalizedRefAtom).r
|
|
|
+ const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
|
|
|
+ const setupState = owner.setupState
|
|
|
+
|
|
|
+ // dynamic ref changed. unset old ref
|
|
|
+ if (oldRef != null && oldRef !== ref) {
|
|
|
+ if (isString(oldRef)) {
|
|
|
+ refs[oldRef] = null
|
|
|
+ if (hasOwn(setupState, oldRef)) {
|
|
|
+ setupState[oldRef] = null
|
|
|
+ }
|
|
|
+ } else if (isRef(oldRef)) {
|
|
|
+ oldRef.value = null
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isString(ref)) {
|
|
|
+ const doSet = () => {
|
|
|
+ if (__COMPAT__ && isCompatEnabled(DeprecationTypes.V_FOR_REF, owner)) {
|
|
|
+ registerLegacyRef(refs, ref, refValue, owner, rawRef.f, isUnmount)
|
|
|
+ } else {
|
|
|
+ refs[ref] = value
|
|
|
+ }
|
|
|
+ if (hasOwn(setupState, ref)) {
|
|
|
+ setupState[ref] = value
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // #1789: for non-null values, set them after render
|
|
|
+ // null values means this is unmount and it should not overwrite another
|
|
|
+ // ref with the same key
|
|
|
+ if (value) {
|
|
|
+ ;(doSet as SchedulerJob).id = -1
|
|
|
+ queuePostRenderEffect(doSet, parentSuspense)
|
|
|
+ } else {
|
|
|
+ doSet()
|
|
|
+ }
|
|
|
+ } else if (isRef(ref)) {
|
|
|
+ const doSet = () => {
|
|
|
+ ref.value = value
|
|
|
+ }
|
|
|
+ if (value) {
|
|
|
+ ;(doSet as SchedulerJob).id = -1
|
|
|
+ queuePostRenderEffect(doSet, parentSuspense)
|
|
|
+ } else {
|
|
|
+ doSet()
|
|
|
+ }
|
|
|
+ } else if (isFunction(ref)) {
|
|
|
+ callWithErrorHandling(ref, owner, ErrorCodes.FUNCTION_REF, [value, refs])
|
|
|
+ } else if (__DEV__) {
|
|
|
+ warn('Invalid template ref type:', value, `(${typeof value})`)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
export function invokeVNodeHook(
|
|
|
hook: VNodeHook,
|
|
|
instance: ComponentInternalInstance | null,
|