fix(v-model): handle mutations of v-model bound array/sets

fix #4096
This commit is contained in:
Evan You 2021-07-15 12:14:19 -04:00
parent c23153d82e
commit 2937530bef
3 changed files with 15 additions and 6 deletions

View File

@ -390,12 +390,12 @@ export function createPathGetter(ctx: any, path: string) {
}
}
function traverse(value: unknown, seen: Set<unknown> = new Set()) {
if (
!isObject(value) ||
seen.has(value) ||
(value as any)[ReactiveFlags.SKIP]
) {
export function traverse(value: unknown, seen: Set<unknown> = new Set()) {
if (!isObject(value) || (value as any)[ReactiveFlags.SKIP]) {
return value
}
seen = seen || new Set()
if (seen.has(value)) {
return value
}
seen.add(value)

View File

@ -20,6 +20,7 @@ import { callWithAsyncErrorHandling, ErrorCodes } from './errorHandling'
import { ComponentPublicInstance } from './componentPublicInstance'
import { mapCompatDirectiveHook } from './compat/customDirective'
import { pauseTracking, resetTracking } from '@vue/reactivity'
import { traverse } from './apiWatch'
export interface DirectiveBinding<V = any> {
instance: ComponentPublicInstance | null
@ -51,6 +52,7 @@ export interface ObjectDirective<T = any, V = any> {
beforeUnmount?: DirectiveHook<T, null, V>
unmounted?: DirectiveHook<T, null, V>
getSSRProps?: SSRDirectiveHook
deep?: boolean
}
export type FunctionDirective<T = any, V = any> = DirectiveHook<T, any, V>
@ -101,6 +103,9 @@ export function withDirectives<T extends VNode>(
updated: dir
} as ObjectDirective
}
if (dir.deep) {
traverse(value)
}
bindings.push({
dir,
instance,

View File

@ -99,6 +99,8 @@ export const vModelText: ModelDirective<
}
export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
// #4096 array checkboxes need to be deep traversed
deep: true,
created(el, _, vnode) {
el._assign = getModelAssigner(vnode)
addEventListener(el, 'change', () => {
@ -171,6 +173,8 @@ export const vModelRadio: ModelDirective<HTMLInputElement> = {
}
export const vModelSelect: ModelDirective<HTMLSelectElement> = {
// <select multiple> value need to be deep traversed
deep: true,
created(el, { value, modifiers: { number } }, vnode) {
const isSetModel = isSet(value)
addEventListener(el, 'change', () => {