Когда вам нужно что-то ввести в Vue, вы, естественно, подумаете об использовании<input v-model="xxx" />способ достижения двусторонней привязки. Ниже приведен самый простой пример
<div id="app">
<h2>What's your name:</h2>
<input v-model="name" />
<div>Hello {{ name }}</div>
</div>
new Vue({
el: "#app",
data: {
name: ""
}
});
Демонстрация JsFiddle
Затем будет отображено то, что вводится в поле ввода этого примера. Это нативная пара Vue<input>Это также типичный пример двусторонней передачи данных между родительским и дочерним компонентами. ноv-modelЭто новая функция, добавленная в Vue 2.2.0, до этого Vue поддерживал только односторонний поток данных.
Односторонний поток данных в Vue
Односторонний поток данных Vue подобен React.Родительский компонент может передавать данные дочернему компоненту, устанавливая свойства (Props) дочернего компонента.Если родительский компонент хочет получить данные дочернего компонента, он должен зарегистрируйте событие с дочерним компонентом, и дочерний компонент счастлив Когда это событие запускается, данные передаются. Подводя итог в одном предложении, свойства передают данные вниз, а события передают данные вверх.
В приведенном выше примере, если вы не используетеv-modelэто должно выглядеть так
<input :value="name" @input="name = $event.target.value" />
Поскольку обработка событий написана во встроенном режиме, часть сценария не нужно изменять. Но в большинстве случаев событие вообще определяется как метод, код будет намного сложнее
<input :value="name" @input="updateName" />
new Vue({
// ....
methods: {
updateName(e) {
this.name = e.target.value;
}
}
})
Из примера вышеv-modelЭто экономит много кода, и самое главное, что можно определить на один обработчик событий меньше. такv-modelФактические засушливые события включают
- использовать
v-bind(который:) односторонняя привязка к свойству (пример::value="name") - связывать
inputсобытие (т.@input) в реализованный по умолчанию обработчик событий (пример:@input=updateName - Этот обработчик событий по умолчанию изменяет связанные данные на основе значения, переданного объектом события (пример:
this.name = e.target.value)
пользовательские компонентыv-model
Vue инкапсулирует нативные компоненты, поэтому<input>Срабатывает при вводеinputмероприятие. Но как насчет пользовательских компонентов? Вот пример списка задач с помощью шаблона JsFiddle Vue.
Шаблон Vue для JsFiddle
Нажмите на логотип JsFilddle и выберите шаблон Vue на всплывающей панели выше.
Стандартный код состоит из двух частей, HTML и Vue(js), и выглядит следующим образом:
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<label>
<input type="checkbox"
v-on:change="toggle(todo)"
v-bind:checked="todo.done">
<del v-if="todo.done">
{{ todo.text }}
</del>
<span v-else>
{{ todo.text }}
</span>
</label>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
methods: {
toggle: function(todo){
todo.done = !todo.done
}
}
})
Определить компоненты Todo
Шаблон Vue JsFiddle реализует отображение списка задач по умолчанию, данные фиксируются, а весь контент заполняется в одном шаблоне. Первое, что нам нужно сделать, это превратить отдельный Todo в подкомпонент. Поскольку его нельзя записать в виде нескольких файлов в JsFiddle, компонент используетVue.component()Определено в скрипте, в основном, чтобы поставить<li>Выньте эту часть содержимого:
Vue.component("todo", {
template: `
<label>
<input type="checkbox" @change="toggle" :checked="isDone">
<del v-if="isDone">
{{ text }}
</del>
<span v-else>
{{ text }}
</span>
</label>
`,
props: ["text", "done"],
data() {
return {
isDone: this.done
};
},
methods: {
toggle() {
this.isDone = !this.isDone;
}
}
});
Первоначально определено в приложенииtoggle()Метод также был немного изменен и определен в компоненте.toggle()При вызове он изменит, завершен он или нетdoneценность . Но из-заdoneопределяется вpropsАтрибуты не могут быть назначены напрямую, поэтому для определения данных используется первый метод, рекомендованный должностным лицом.isDone, инициализируетсяthis.doneи использовать внутри компонентаisDoneчтобы контролировать, завершено ли это состояние.
Шаблон и код соответствующего раздела App значительно уменьшены:
<div id="app">
<h2>Todos:</h2>
<ol>
<li v-for="todo in todos">
<todo :text="todo.text" :done="todo.done"></todo>
</li>
</ol>
</div>
new Vue({
el: "#app",
data: {
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
}
});
Демонстрация JsFiddle
Но пока данные все еще односторонние. С точки зрения эффекта, установка флажка может привести к эффекту зачеркивания, но все эти динамические изменения находятся вtodoКомпонент делается внутри, и нет проблем с привязкой данных.
Добавить счетчик в список задач
чтобыtodoИзменения состояния внутри компонента могут отображаться в списке задач.Мы добавляем счетчик в список задач, чтобы показать количество задач, которые были выполнены. Потому что это числоtodoВлияние внутреннего состояния (данных) компонента, требующегоtodoИзменения внутренних данных отражаются в его родительском компоненте, так чтоv-modelиспользования.
Номер в названии, который мы должныn/mформа, например2/4Обозначает в общей сложности 4 задачи, 2 из которых выполнены. Это требует модификации шаблона и части кода Todo List, добавленияcountDoneа такжеcountДва вычисляемых свойства:
<div id="app">
<h2>Todos ({{ countDone }}/{{ count }}):</h2>
<!-- ... -->
</div>
new Vue({
// ...
computed: {
count() {
return this.todos.length;
},
countDone() {
return this.todos.filter(todo => todo.done).length;
}
}
});
Теперь количество отображается, но теперь изменение состояния задачи не влияет на количество. Мы хотим, чтобы изменения дочернего компонента влияли на данные родительского компонента.v-modelО нем я расскажу позже, начнем с самого распространенного метода, событий:
- Подсборка
todoсуществуетtoggle()средний триггерtoggleсобытие будетisDoneкак параметр события - родительский компонент является дочерним компонентом
toggleобработчик событий определения события
Vue.component("todo", {
//...
methods: {
toggle(e) {
this.isDone = !this.isDone;
this.$emit("toggle", this.isDone);
}
}
});
<!-- #app 中其它代码略 -->
<todo :text="todo.text" :done="todo.done" @toggle="todo.done = $event"></todo>
Здесь@toggleСвязывание — это выражение. потому что здесьtodoявляется временной переменной, если вmethodsТрудно связать эту временную переменную, определив специальную функцию обработки событий в прошлом (конечно, можно определить общий метод, вызвав его).
Функции обработчика событий, которые обычно непосредственно соответствуют обрабатываемым вещам, например определение
onToggle(e), связанный как@toggle="onToggle". В этом случае его нельзя передатьtodoкак параметр.Обычный метод, который можно определить как
toggle(todo, e), который вызывается как выражение вызова функции в определении события:@toggle="toggle(todo, $event)"。它和todo.done = $event` Общее выражение.Обратите внимание на разницу между ними: первая — связанная функция-обработчик (ссылка), а вторая — связанное выражение (вызов).
Теперь ожидаемый эффект был достигнут через метод события
** Демонстрация скрипта Js **
изменено вv-model
Ранее мы говорили об использованииv-modelПонял, теперь его преобразовывать. Обратите внимание на реализациюv-modelнесколько элементов
- дочерний компонент через
valueОпора принимает ввод - Дочерний компонент запускается
inputВыход события с параметрами массива - используется в родительском компоненте
v-modelсвязывать
Vue.component("todo", {
// ...
props: ["text", "value"], // <-- 注意 done 改成了 value
data() {
return {
isDone: this.value // <-- 注意 this.done 改成了 this.value
};
},
methods: {
toggle(e) {
this.isDone = !this.isDone;
this.$emit("input", this.isDone); // <-- 注意事件名称变了
}
}
});
<!-- #app 中其它代码略 -->
<todo :text="todo.text" v-model="todo.done"></todo>
.syncРеализовать другие привязки данных
Введение Vue 2.2.0 упоминалось ранее.v-modelхарактеристика. По какой-то причине его входное свойствоvalue, но выходное событие вызываетсяinput.v-model,value,inputМежду этими тремя именами нет буквально никакой связи. Хотя это может показаться немного странным, дело не в этом, дело в том, что элемент управления может привязываться только к одному свойству в обоих направлениях?
Vue 2.3.0 представлен.syncмодификаторы используются для измененияv-bind(который:), чтобы сделать его двусторонним. Это тоже синтаксический сахар, добавляющий.syncПривязка декорированных данных будет выглядеть какv-modelОдин и тот же обработчик событий автоматически зарегистрирован для назначения значений в связанные данные. Этот подход также требует дочерних компонентов, чтобы вызвать определенные события. Однако имя этого события имеет отношение к имени свойства связывания. Он добавляется до имени свойства связывания.update:приставка.
Например<sub :some.sync="any" />подкомпонентыsomeсвойства и родительский компонентanyПривязка данных, дочерний компонент должен пройти$emit("update:some", value)чтобы вызвать изменение.
В приведенном выше примере используйтеv-modelСвязывание всегда кажется немного неловким, потому чтоv-modelБуквальное значение состоит в том, чтобы связать значение в обоих направлениях и указать, является ли оно неполным.doneНа самом деле это состояние, а не значение. Поэтому мы снова модифицируем его, все еще используяdoneэто имя свойства (вместоvalue),пройти через.syncдля достижения двусторонней привязки.
Vue.component("todo", {
// ...
props: ["text", "done"], // <-- 恢复成 done
data() {
return {
isDone: this.done // <-- 恢复成 done
};
},
methods: {
toggle(e) {
this.isDone = !this.isDone;
this.$emit("update:done", this.isDone); // <-- 事件名称:update:done
}
}
});
<!-- #app 中其它代码略 -->
<!-- 注意 v-model 变成了 :done.sync,别忘了冒号哟 -->
<todo :text="todo.text" :done.sync="todo.done"></todo>
** Демонстрация скрипта Js **
Демистификация двусторонней привязки Vue
Из приведенного выше описания, я думаю, все должны были понять, что двусторонняя привязка Vue на самом деле завершается обычной односторонней привязкой и комбинацией событий, но черезv-modelа также.syncФункция обработчика по умолчанию зарегистрирована для обновления данных. В исходном коде Vue есть такой абзац
// @file: src/compiler/parser/index.js
if (modifiers.sync) {
addHandler(
el,
`update:${camelize(name)}`,
genAssignmentCode(value, `$event`)
)
}
Как видно из этого кода,.syncПри двусторонней привязке компилятор добавитupdate:${camelize(name)}Функция обработчика события для назначения данных (genAssignmentCodeбуквально означает код, который генерирует присваивание).
Перспектива
В настоящее время двусторонняя привязка Vue также должна обеспечивать возврат данных путем запуска событий. Между этим и многими ожидаемыми доходами от назначений все еще существует определенный разрыв. Есть две основные причины этого разрыва
- Необходимо вернуть данные через события
- Свойства (реквизит) не могут быть назначены
В текущей версии Vue упрощение может быть достигнуто за счет определения вычисляемых свойств, таких как
computed: {
isDone: {
get() {
return this.done;
},
set(value) {
this.$emit("update:done", value);
}
}
}
По правде говоря, довольно утомительно определять больше имен переменных с одинаковым значением и разными именами. Я надеюсь, что Vue сможет сократить этот процесс с помощью определенных технических средств в будущих версиях, таких как добавление объявлений свойств (Prop).syncвариант, просто объявитеsync: trueмогут быть напрямую назначены и автоматически запущеныupdate:xxxмероприятие.
Конечно, как фреймворк, при решении проблемы нам также нужно учитывать влияние на другие функции и расширяемость фреймворка, так что во что в итоге превратится двухсторонняя привязка, мы подождем и увидим для Vue 3.0.