v-model является синтаксическим сахаром для двусторонней
привязки данных между компонентом и его состоянием. Начиная с Vue 3,
появилась возможность использовать множественные
v-model для разных свойств компонента, что
позволяет создавать более гибкие и переиспользуемые компоненты.
Тестирование таких компонентов требует особого внимания к правильной
настройке и проверке обновлений состояния.
v-modelВ Vue 3 компонент может иметь несколько v-model через
определение отдельных пропов и событий. Например:
export default {
props: {
modelValue: String,
title: String
},
emits: ['update:modelValue', 'update:title']
};
Использование в шаблоне:
v-model="text" связывает modelValue с
переменной text и ожидает событие
update:modelValue.v-model:title="header" связывает title с
переменной header и ожидает событие
update:title.Это разделение позволяет каждому v-model управлять своей
частью состояния независимо.
v-modelПри тестировании с помощью Vue Test Utils необходимо корректно передавать начальные значения пропов:
import { mount } from '@vue/test-utils';
import MyComponent from '@/components/MyComponent.vue';
const wrapper = mount(MyComponent, {
props: {
modelValue: 'initial text',
title: 'initial title'
}
});
props задают начальное состояние для каждого
v-model.v-model инициирует отдельное событие обновления.Для тестирования множественных v-model используется
метод emitted():
wrapper.vm.$emit('update:modelValue', 'new text');
wrapper.vm.$emit('update:title', 'new title');
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['new text']);
expect(wrapper.emitted('update:title')[0]).toEqual(['new title']);
emitted() возвращает объект с ключами по имени
событий.v-model не конфликтуют.Если компонент имеет форму ввода для каждого v-model,
можно симулировать взаимодействие пользователя:
const inputModel = wrapper.find('input[name="modelValue"]');
const inputTitle = wrapper.find('input[name="title"]');
await inputModel.setValue('updated text');
await inputTitle.setValue('updated title');
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['updated text']);
expect(wrapper.emitted('update:title')[0]).toEqual(['updated title']);
setValue() автоматически триггерит событие
input.v-model
поля, чтобы убедиться в независимости их работы.Когда v-model связан с объектом, важно помнить о
реактивности:
const wrapper = mount(MyComponent, {
props: {
modelValue: { text: 'initial' },
title: { header: 'initial' }
}
});
wrapper.vm.$emit('update:modelValue', { text: 'changed' });
expect(wrapper.emitted('update:modelValue')[0][0].text).toBe('changed');
v-model создается новая ссылка при каждом
обновлении.reactive или
toRefs.v-model в составе родителяИногда компонент используется внутри родителя, где
v-model привязан к локальному состоянию:
const Parent = {
template: ` `,
components: { MyComponent },
data() {
return { text: 'parent text', header: 'parent header' };
}
};
const wrapper = mount(Parent);
const child = wrapper.findComponent(MyComponent);
child.vm.$emit('update:modelValue', 'child text');
child.vm.$emit('update:title', 'child header');
expect(wrapper.vm.text).toBe('child text');
expect(wrapper.vm.header).toBe('child header');
v-model ограничивается локальным
состоянием дочернего компонента.v-model правильно получает prop.setValue, trigger для проверки
реактивности.update: проверять
аргументы и порядок эмитов.v-model.v-model, если правильно настраивать props и события.