Как мы все знаем, одной из основных идей Vue является разработка компонентов. Поэтому при реальной разработке проекта она обязательно будет вестись в режиме разработки компонентов. Точно так же, как страницы должны взаимодействовать друг с другом, компоненты и компоненты Vue также должны взаимодействовать друг с другом и обмениваться состоянием. Далее мы покажем вам все методы связи между всеми компонентами Vue.
взаимосвязь компонентов
Изображение, показанное выше, может импортировать реляционную форму всех компонентов Vue:
- Компонент и компонент B, компонент B и компонент C, компонент B и компонент D образуют отношение родитель-потомок.
- Компонент C и компонент D образуют родственные отношения.
- Компонент и компонент С, компонент А и компонент D образуют отношения поколений (иерархия может быть многоуровневой, то есть несколько поколений)
компонент связи
Каковы способы связи между компонентами при таком количестве взаимосвязей между компонентами? В чем разница между различными способами? Каковы применимые сценарии? Продолжайте читать с вопросами!
1,propsа также$emit
Разработчики, которые использовали стек технологий Vue для разработки проектов, определенно знакомы с такой комбинацией.Этот способ взаимодействия компонентов — тот, который мы часто используем. Реквизиты вполне могут дополнить связь между родительскими и дочерними компонентами в виде одностороннего потока данных.
Так называемый односторонний поток данных: данные могут передаваться от родительского компонента к дочернему компоненту только через пропсы, а дочерний компонент не может модифицировать соответствующее состояние родительского компонента, модифицируя данные, переданные пропсами. Что касается того, почему это делается, официальный сайт Vue объясняет:
Все реквизиты формируют отношения между их родительскими и дочерними реквизитами.Односторонняя привязка нисходящего канала: обновления родительских реквизитов передаются дочерним компонентам, но не наоборот. Это предотвращает случайные изменения родительского состояния из дочерних компонентов, которые могут сделать поток данных вашего приложения непонятным.
Кроме того, каждый раз, когда родительский компонент обновляется, все реквизиты в дочернем компоненте будут обновляться до последнего значения. Это означает, что вы не должны изменять свойства внутри дочернего компонента. Если вы это сделаете, Vue выдаст предупреждение в консоли браузера.
——Официальный сайт Вью
Из-за этой особенности существует соответствующий$emit.$emitИспользуется для запуска событий в текущем экземпляре. В связи с этим мы можем определить логику в родительском компоненте, которая обрабатывает принятое состояние изменения, а затем запускать событие логической обработки родительского компонента при изменении соответствующего состояния в дочернем компоненте.
// 父组件
Vue.component('parent', {
template:`
<div>
<p>this is parent component!</p>
<child :message="message" v-on:getChildData="getChildData"></child>
</div>
`,
data() {
return {
message: 'hello'
}
},
methods:{
// 执行子组件触发的事件
getChildData(val) {
console.log(val);
}
}
});
// 子组件
Vue.component('child', {
template:`
<div>
<input type="text" v-model="myMessage" @input="passData(myMessage)">
</div>
`,
/**
* 得到父组件传递过来的数据
* 这里的定义最好是写成数据校验的形式,免得得到的数据是我们意料之外的
*
* props: {
* message: {
* type: String,
* default: ''
* }
* }
*
*/
props:['message'],
data() {
return {
// 这里是必要的,因为你不能直接修改 props 的值
myMessage: this.message
}
},
methods:{
passData(val) {
// 数据状态变化时触发父组件中的事件
this.$emit('getChildData', val);
}
}
});
var app=new Vue({
el: '#app',
template: `
<div>
<parent />
</div>
`
});
В приведенном выше примере есть родительский компонент-родитель и дочерний компонент-дочерний.
- 1) родительский компонент передает данные сообщения дочернему компоненту и привязывает событие getChildData через v-on для отслеживания триггерного события дочернего компонента;
- 2) Дочерний компонент получает соответствующие данные сообщения через реквизиты, а затем кэширует данные в данных. Наконец, когда значение данных свойства изменяется, событие getChildData, зарегистрированное родительским компонентом, запускается для обработки логики данных через это.$ испускают.
2,$attrsа также$listeners
Вышеупомянутый метод связи компонентов подходит только для прямых компонентов родитель-потомок, то есть, если родительский компонент A имеет дочерний компонент B, а компонент B имеет компонент C под компонентом B, тогда, если компонент A напрямую хочет передать данные компоненту C, это не будет работать! Может быть только то, что компонент A передает данные компоненту B через реквизиты, а затем компонент B получает данные, переданные компонентом A, и затем передает данные компоненту C через реквизиты. Конечно, этот метод очень сложен, в несвязанных компонентах больше логических дел, и обслуживание кода не становится сложным, кроме того, чем больше вложенных уровней, тем логика усложняется, и тем больше несвязанного кода!
Для такой проблемы,Vue 2.4при условии$attrsа также$listenersЧтобы получить возможность напрямую разрешить компоненту A передавать сообщения компоненту C.
// 组件A
Vue.component('A', {
template: `
<div>
<p>this is parent component!</p>
<B :messagec="messagec" :message="message" v-on:getCData="getCData" v-on:getChildData="getChildData(message)"></B>
</div>
`,
data() {
return {
message: 'hello',
messagec: 'hello c' //传递给c组件的数据
}
},
methods: {
// 执行B子组件触发的事件
getChildData(val) {
console.log(`这是来自B组件的数据:${val}`);
},
// 执行C子组件触发的事件
getCData(val) {
console.log(`这是来自C组件的数据:${val}`);
}
}
});
// 组件B
Vue.component('B', {
template: `
<div>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
<!-- C组件中能直接触发 getCData 的原因在于:B组件调用 C组件时,使用 v-on 绑定了 $listeners 属性 -->
<!-- 通过v-bind 绑定 $attrs 属性,C组件可以直接获取到 A组件中传递下来的 props(除了 B组件中 props声明的) -->
<C v-bind="$attrs" v-on="$listeners"></C>
</div>
`,
/**
* 得到父组件传递过来的数据
* 这里的定义最好是写成数据校验的形式,免得得到的数据是我们意料之外的
*
* props: {
* message: {
* type: String,
* default: ''
* }
* }
*
*/
props: ['message'],
data(){
return {
mymessage: this.message
}
},
methods: {
passData(val){
//触发父组件中的事件
this.$emit('getChildData', val)
}
}
});
// 组件C
Vue.component('C', {
template: `
<div>
<input type="text" v-model="$attrs.messagec" @input="passCData($attrs.messagec)">
</div>
`,
methods: {
passCData(val) {
// 触发父组件A中的事件
this.$emit('getCData',val)
}
}
});
var app=new Vue({
el:'#app',
template: `
<div>
<A />
</div>
`
});
В приведенном выше примере мы определили три компонента A, B и C, где компонент B является подкомпонентом компонента A, а компонент C является подкомпонентом компонента B.
- 1) В компоненте A значение свойства (message, messagec) и событие слушателя (getChildData, getCData) определяются для компонента B и компонента C соответственно, и они передаются непосредственному дочернему компоненту B компонента A через реквизиты;
- 2).В компоненте Б через пропсы получаются только свойства (сообщение), непосредственно связанные с ним самим, а значение свойства кэшируется в данных для последующей обработки мониторинга изменений, а затем при изменении значения свойства родительский компонент А срабатывает для определения. Логика обработки данных обрабатывает событие (getChildData). На прямой дочерний компонент C компонента B передается свойство
$attrsи привязать событие$listeners; - 3) Привязать непосредственно к v-модели в компоненте C
$attrsсвойство, связанное через v-on$listeners;
наконец$attrsа также$listenersВозьми и поговори об этом!
-
$attrs: содержит привязки свойств, которые не распознаются (и приобретаются) реквизитами в родительской области (classа такжеstyleКроме). Когда компонент не объявляет никаких свойств, это будет включать все связанные свойства родительской области (класс иstyleкроме), и может пройтиv-bind="$attrs"Передайте внутренний компонент. -
$listeners: содержит родительскую область (исключая.nativeмодификатор)v-onпрослушиватель событий. это может пройтиv-on="$listeners"Передайте внутренний компонент.
3, центральная шина событий EventBus
Для связи между родительско-дочерними компонентами два вышеупомянутых метода вполне достижимы, но как реализовать связь для двух компонентов, которые не находятся в отношениях родитель-потомок? В случае небольшого проекта вполне возможно использовать центральную шину событийEventBusПуть. Если размер вашего проекта средний или большой, вы можете использоватьУправление состоянием Vuex.
EventBusпутем создания новогоVueмероприятиеbusобъект, а затем передатьbus.$emitтриггерное событие,bus.$onСлушайте триггерные события.
// 组件 A
Vue.component('A', {
template: `
<div>
<p>this is A component!</p>
<input type="text" v-model="mymessage" @input="passData(mymessage)">
</div>
`,
data() {
return {
mymessage: 'hello brother1'
}
},
methods: {
passData(val) {
//触发全局事件globalEvent
this.$EventBus.$emit('globalEvent', val)
}
}
});
// 组件 B
Vue.component('B', {
template:`
<div>
<p>this is B component!</p>
<p>组件A 传递过来的数据:{{brothermessage}}</p>
</div>
`,
data() {
return {
mymessage: 'hello brother2',
brothermessage: ''
}
},
mounted() {
//绑定全局事件globalEvent
this.$EventBus.$on('globalEvent', (val) => {
this.brothermessage = val;
});
}
});
//定义中央事件总线
const EventBus = new Vue();
// 将中央事件总线赋值到 Vue.prototype 上,这样所有组件都能访问到了
Vue.prototype.$EventBus = EventBus;
const app = new Vue({
el: '#app',
template: `
<div>
<A />
<B />
</div>
`
});
В приведенном выше примере мы определили компонент A и компонент B, но между компонентом A и компонентом B нет никакой связи.
- 1), сначала проходим
new Vue()Создайте экземпляр Vue, который мы здесь называем центральной шиной событий EventBus, и назначьте егоVue.prototype.$EventBus, делая доступными все компоненты бизнес-логики; - 2), а затем определить компонент A и определить метод обработки passData в компоненте A, который в основном определяет запуск глобального
globalEventсобытие и передать параметр; - 3) Наконец, компонент B определен, в компоненте B
mountedЖизненный цикл прослушивает глобальную переменную, определенную в компоненте A.globalEventсобытие и выполнить некоторую логическую обработку в функции обратного вызова.
центральный автобус событийEventBusЭто очень просто, то есть любой компонент имеет дело с компонентами, нет избыточной бизнес-логики, нужно только вызвать событие в компоненте изменения состояния, а затем прослушать событие в компоненте логики обработки. Этот метод отлично подходит для небольших проектов!
4.provideа такжеinject
Студенты, знакомые с разработкой ReactContext APIКонечно не незнакомый! Подобный API также предоставляется в Vue для связи между компонентами.
передать родительский компонентproviderчтобы предоставить свойства, затем вставьте переменные в дочерние компоненты. Независимо от того, насколько глубоким является дочерний компонент, до тех пор, пока вызовinjectЗатем вы можете вводить данные, предоставленные в провайдере, вместо того, чтобы ограничиваться получением данных только из свойства prop текущего родительского компонента, если дочерний компонент может вызывать его в течение жизненного цикла родительского компонента. Это то же самое, что и ReactContext APIЕсть ли сходства!
// 定义 parent 组件
Vue.component('parent', {
template: `
<div>
<p>this is parent component!</p>
<child></child>
</div>
`,
provide: {
for:'test'
},
data() {
return {
message: 'hello'
}
}
});
// 定义 child 组件
Vue.component('child', {
template: `
<div>
<input type="tet" v-model="mymessage">
</div>
`,
inject: ['for'], // 得到父组件传递过来的数据
data(){
return {
mymessage: this.for
}
},
});
const app = new Vue({
el: '#app',
template: `
<div>
<parent />
</div>
`
});
В приведенном выше примере мы определили компонентparentи компонентыchild, компонентparentи компонентыchildЭто отношения отца и сына.
- 1), в
parentкомпонент, черезprovideСвойства, которые предоставляют некоторые свойства дочерним компонентам в виде объектов. - 2), в
childкомпонент, черезinjectвведенное свойствоparentДанные, предоставленные компонентом, фактическиеinjectАтрибут вводится в экземпляр Vue Mount, это может быть доступно внутренними компонентами.
⚠️ Примечание. В официальной документации веб-сайта упоминается, что предоставление и внедрение в основном предоставляют варианты использования для высокоуровневых плагинов/библиотек компонентов, и их не рекомендуется использовать непосредственно в коде приложения.
оprovideа такжеinjectБолее конкретное использование этой пары свойств см.Документация на официальном сайте.
Немного утомительно писать здесь. Я примерно представил четыре способа общения с компонентами Vue. Как вы думаете, этого достаточно? Нет, нет, нет, после первых четырех путей нас ждут еще четыре! Возьмите это, чтобы добавить разделительную линию, и вы будете шокированы! 😭 Кстати, не говорите, что вы больше не можете учиться, пока у вас есть дыхание, вы должны продолжать учиться!
5.v-model
Этот метод чем-то похож на упомянутый выше реквизит, но раз он упоминается отдельно, то должен иметь свою уникальность! Неважно, давайте начнем с кода!
// 定义 parent 组件
Vue.component('parent', {
template: `
<div>
<p>this is parent component!</p>
<p>{{message}}</p>
<child v-model="message"></child>
</div>
`,
data() {
return {
message: 'hello'
}
}
});
// 定义 child 组件
Vue.component('child', {
template: `
<div>
<input type="text" v-model="mymessage" @change="changeValue">
</div>
`,
props: {
value: String, // v-model 会自动传递一个字段为 value 的 props 属性
},
data() {
return {
mymessage: this.value
}
},
methods: {
changeValue() {
this.$emit('input', this.mymessage); //通过如此调用可以改变父组件上 v-model 绑定的值
}
},
});
const app = new Vue({
el: '#app',
template: `
<div>
<parent />
</div>
`
});
Когда дело доходит до обозначения v-модели, вы обязательно подумаете о двусторонней привязке данных, таких как входное значение ввода.Следующий дисплей предназначен для отображения соответствующего контента в режиме реального времени в соответствии с вводом. Было ли это потрясающе, когда вы впервые начали изучать Vue, независимо от того, есть ли он у вас или нет, я все равно чувствовал это!
Для получения подробной информации об использовании v-модели и реализации пользовательской v-модели компонента вы можете перейти кздесьПроверить! Здесь мы в основном объясняем, как v-model реализует связь между родительским и дочерним компонентами.
В приведенном выше примере кода мы определили два компонента, родительский и дочерний, которые представляют собой отношения родитель-потомок, а v-model может взаимодействовать только между компонентами родитель-потомок.
- 1) В родительском компоненте мы реализуем v-модель и привязываем свойство сообщения к пользовательскому дочернему компоненту. На данный момент это эквивалентно передаче атрибута value дочернему компоненту и привязке входного события.
- 2) Логично, что в определяемом дочернем компоненте атрибут value можно получить через пропсы, по принципу одностороннего потока данных пропсов, значение кэшируется на mymessage в data, а потом передается на вход.
v-modelграницаmymessageатрибуты иchangeмероприятие. Когда входное значение изменяется, запускается событие изменения, и родительский компонент обрабатывается черезv-modelПривязка к дочернему компонентуinputсобытие, триггерparentкомпонентmessageизменение значения свойства, сделаноchildДочерний компонент изменяет значение свойства родительского компонента.
В основном здесьv-modelПринцип реализации должен сосредоточиться на понимании! Полезность этого подхода подходит для отделения компонентов представления от компонентов бизнес-логики.
6.$parentа также$children
Этот метод, который следует здесь упомянуть, является более интуитивным и напрямую работает с экземпляром родительско-дочернего компонента.$parentявляется экземпляром объекта родительского компонента, и$childrenЭто экземпляр прямого дочернего компонента текущего экземпляра, но это значение свойства имеет тип массива, порядок не гарантируется и не отвечает.
// 定义 parent 组件
Vue.component('parent', {
template: `
<div>
<p>this is parent component!</p>
<button @click="changeChildValue">test</button>
<child />
</div>
`,
data() {
return {
message: 'hello'
}
},
methods: {
changeChildValue(){
this.$children[0].mymessage = 'hello';
}
},
});
// 定义 child 组件
Vue.component('child', {
template:`
<div>
<input type="text" v-model="mymessage" @change="changeValue" />
</div>
`,
data() {
return {
mymessage: this.$parent.message
}
},
methods: {
changeValue(){
this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值
}
},
});
const app = new Vue({
el: '#app',
template: `
<div>
<parent />
</div>
`
});
В приведенном выше примере кода родительский и дочерний компоненты определены соответственно, и эти два компонента имеют прямую связь родитель-потомок. Оба компонента определяют свои собственные свойства внутри. В родительском компоненте напрямую черезthis.$children[0].mymessage = 'hello';Датьchildвнутри компонентаmymessageНазначение атрибутов, а в дочерних подкомпонентах также напрямую черезthis.$parent.messageДатьparentв компонентеmessageНазначение, формирующее коммуникацию родитель-потомок.
о$parentа также$childrenПодробное описание этой пары атрибутов можно найти в официальной документации сайта!
7.$boradcastа также$dispatch
Этот способ тоже пары появляются парами, но только вVue1.0предоставлено вVue2.0Заброшен, но все еще есть много программного обеспечения с открытым исходным кодом, которое инкапсулирует этот способ взаимодействия компонентов, напримерMint UI,Element UIа такжеiViewЖдать.
// broadcast 方法的主逻辑处理方法
function broadcast(componentName, eventName, params) {
this.$children.forEach(child => {
const name = child.$options.componentName;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.apply(child, [componentName, eventName].concat(params));
}
});
}
export default {
methods: {
// 定义 dispatch 方法
dispatch(componentName, eventName, params) {
let parent = this.$parent;
let name = parent.$options.componentName;
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
// 定义 broadcast 方法
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
Показанный выше код обычно используется какmixinsсмешать,broadcastзаключается в том, чтобы инициировать событие для определенного родительского компонента,dispatchИнициировать ли событие для определенного дочернего компонента, по сути, этот способ по-прежнемуonа такжеemitИнкапсуляция очень практична в некоторых основных компонентах.
Потому чтоVue 2.0Этот API был заброшен, поэтому мы упомянем его здесь, если вы хотите узнать о нем больше.Vue 1.0Для реализации этого API с другими платформами пользовательского интерфейса на основе Vue вы можете щелкнуть, чтобы просмотретьэта статья!
8. Управление состоянием Vuex
Vuex — это инструмент управления состоянием, реализующий централизованное управление состоянием проекта. Реализация инструмента опирается наFlux,Redux,а также The Elm Architectureшаблоны и концепции. Конечно, в отличие от других режимов, Vuex — это библиотека управления состоянием, специально разработанная для Vue.js, чтобы использовать детальный механизм ответа на данные Vue.js для эффективного обновления состояния. Для подробного ознакомления с Vuex вы можете проверить егоОфициальная документация сайта, вы также можете ознакомиться с этой колонкой, чтобы узнать о Vuex.
Суммировать
К этому моменту все способы взаимодействия компонентов в Vue были представлены, он все еще кажется достаточно богатым? На самом деле, есть два других способа добиться взаимодействия компонентов, один из них — черезVue RouterКоммуникация, а вторая — реализовать компонентную коммуникацию через локальное хранилище браузера. Об этих двух методах я не буду рассказывать здесь, о них я, конечно, объясню отдельно в этой колонке, надеюсь, всем будет интересно сходить и посмотреть!
Если быть точным, в этой статье подробно объясняются шесть способов реализации связи Vue, каждый из которых имеет свои особенности. В реальных проектах каждый может использовать его по своему усмотрению.