предисловие
Компоненты — одна из самых мощных функций vue.js, а области действия экземпляров компонентов не зависят друг от друга, а это означает, что данные между разными компонентами не могут ссылаться друг на друга. В общем случае компоненты могут иметь следующие отношения:
Как показано на рисунке выше, A и B, B и C, B и D — все отношения между родителями и детьми, C и D — отношения между братьями и сестрами, а A и C — межпоколенческие (возможно, несколько поколений). Как выбрать эффективный способ коммуникации для разных сценариев использования?
Способ 1, реквизит /$emit
Родительский компонент A передается дочернему компоненту B через свойства.
1. Родительский компонент передает значение дочернему компоненту
Далее мы используем пример, чтобы проиллюстрировать, как родительский компонент передает значения дочернему компоненту: как получить данные в родительском компоненте App.vue в дочернем компоненте Users.vue users:["Henry","Bucky" ,"Эмили"]
//App.vue父组件
<template>
<div id="app">
<users v-bind:users="users"></users>//前者自定义名称便于子组件调用,后者要传递数据名
</div>
</template>
<script>
import Users from "./components/Users"
export default {
name: 'App',
data(){
return{
users:["Henry","Bucky","Emily"]
}
},
components:{
"users":Users
}
}
//users子组件
<template>
<div class="hello">
<ul>
<li v-for="user in users">{{user}}</li>//遍历传递过来的值,然后呈现到页面
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
props:{
users:{ //这个就是父组件中子标签自定义名字
type:Array,
required:true
}
}
}
</script>
Резюме: родительский компонент передает данные дочернему компоненту через свойства. Примечание. В компонентах есть три формы данных: данные, реквизиты, вычисляемые
2. Дочерний компонент передает значения родительскому компоненту (в виде событий)
Далее, давайте используем пример, чтобы проиллюстрировать, как дочерний компонент передает значение родительскому компоненту: когда мы нажимаем «Vue.js Demo», дочерний компонент передает значение родительскому компоненту, и текст меняется с «передача значения " to " Дочерний элемент передает значение родительскому компоненту", который реализует передачу значения от дочернего компонента к родительскому компоненту.
// 子组件
<template>
<header>
<h1 @click="changeTitle">{{title}}</h1>//绑定一个点击事件
</header>
</template>
<script>
export default {
name: 'app-header',
data() {
return {
title:"Vue.js Demo"
}
},
methods:{
changeTitle() {
this.$emit("titleChanged","子向父组件传值");//自定义事件 传递值“子向父组件传值”
}
}
}
</script>
// 父组件
<template>
<div id="app">
<app-header @titleChanged="updateTitle" ></app-header>//与子组件titleChanged自定义事件保持一致
// updateTitle($event)接受传递过来的文字
<h2>{{title}}</h2>
</div>
</template>
<script>
import Header from "./components/Header"
export default {
name: 'App',
data(){
return{
title:"传递的是一个值"
}
},
methods:{
updateTitle(e){ //声明这个函数
this.title = e;
}
},
components:{
"app-header":Header,
}
}
</script>
Резюме: дочерний компонент отправляет сообщения родительскому компоненту через события, которые на самом деле представляют собой дочерний компонент, отправляющий свои собственные данные родительскому компоненту.
Способ второй, центральная шина событий EventBus
Для связи между родительско-дочерними компонентами описанный выше метод вполне достижим, но как реализовать связь для двух компонентов, которые не являются родительско-дочерними отношениями? В случае небольшого проекта можно использовать центральную шину событий EventBus. Если размер вашего проекта средний или большой, вы можете использовать управление состоянием Vuex, которое мы представим позже. Введение в EventBus EventBus также известен как шина событий. В Vue EventBus можно использовать в качестве концепции коммуникационного моста, точно так же, как все компоненты используют один и тот же центр событий, вы можете зарегистрироваться для отправки или получения событий в центр, поэтому компоненты могут уведомлять другие компоненты параллельно, но это слишком. удобно, поэтому, если его использовать небрежно, это вызовет катастрофу, которую трудно поддерживать.Поэтому необходим более полный Vuex в качестве центра управления состоянием, а концепция уведомления поднята на уровень общего состояния.
Как использовать EventBus Как использовать EventBus в проекте Vue для реализации обмена данными между компонентами? В частности, это можно сделать с помощью следующих шагов.
инициализация Первое, что вам нужно сделать, это создать шину событий и экспортировать ее, чтобы другие модули могли ее использовать или прослушивать. Мы можем справиться с этим двумя способами. Давайте посмотрим на первый, создадим новый файл .js, такой как eventBus.js:
// eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
Все, что вам нужно сделать, это импортировать Vue и экспортировать его экземпляр (в этом случае я назову его EventBus). По сути, это компонент, у которого нет DOM, все, что у него есть, — это методы экземпляра, поэтому он очень легкий. Другой способ — инициализировать EventBus непосредственно в main.js вашего проекта:
// main.js
Vue.prototype.$EventBus = new Vue()
Обратите внимание, что инициализированная таким образом EventBus является глобальной шиной событий. Теперь, когда мы создали EventBus, все, что вам нужно сделать, это загрузить его в свой компонент и вызвать тот же метод, как если бы вы передавали сообщения друг другу в родительском и дочернем компонентах.
пример: Активировано в A.vue
import EventBus from 'eventBus.js';
// 必须引入同一个实例
methods: {
doSomething() {
EventBus.$emit("getTarget", 22);
console.log("向getTarget方法传参22");
}
}
Прослушивание события в B.vue
import EventBus from 'eventBus.js';
// 我们在create钩子中监听方法
create(){
console.log("this.getTarget是一个带参数的方法,但是这边只要将两者关联起来");
EventBus.$on('getTarget', this.getTarget);
},
beforeDestroy() {
console.log("组件销毁前需要解绑事件。否则会出现重复触发事件的问题");
EventBus.$off('getTarget', this.getTarget);
},
methods: {
getTarget(param) {
// todo
}
}
EventBus на самом деле очень удобен, с ним можно осуществлять любую связь между компонентами. Однако мы выберем более удобный способ в зависимости от ситуации. Поскольку для eventBus сложно найти конкретную реализацию соответствующего события мониторинга или запуска, общая связь компонентов должна учитывать описанный выше метод реализации. Логически понятно использовать eventBus для связи между модулями, а затем использовать связь vuex внутри модуля для поддержки данных.
Способ третий, vuex
Vuex реализует односторонний поток данных и имеет состояние для глобального хранения данных. Когда компонент хочет изменить данные в состоянии, это необходимо сделать с помощью мутации. Мутация также предоставляет режим подписки для внешних подключаемых модулей для вызова для получения обновленных данных о состоянии. Когда все асинхронные операции (обычные при вызове внутреннего интерфейса для асинхронного получения обновленных данных) или пакетные синхронные операции должны проходить через действие, но действие не может напрямую изменить состояние, все равно необходимо изменить данные состояния с помощью мутации. . Наконец, в соответствии с изменением состояния, оно отображается в представлении.
2. Кратко представить функции каждого модуля в процессе:
Компоненты Vue: компоненты Vue. На HTML-странице он отвечает за получение интерактивного поведения, такого как пользовательские операции, и за выполнение метода отправки для запуска соответствующего действия для ответа.
диспетчеризация: метод триггера действия, который является единственным методом, который может выполнять действие.
действия: модуль обработки действия действия, запускаемый $store.dispatch('имя действия', data1) в компоненте. Затем вызов мутации запускается функцией commit(), которая косвенно обновляет состояние. Отвечает за обработку всех взаимодействий, полученных Vue Components. Содержит синхронные/асинхронные операции, поддерживает несколько методов с одинаковыми именами и запускается в порядке регистрации. Операции, запрашиваемые в фоновом API, выполняются в этом модуле, включая инициирование других действий и отправку мутаций. Этот модуль предоставляет пакет Promise для поддержки последовательного запуска действий.
commit: метод операции фиксации изменения состояния. Выполнение мутации — единственный способ выполнить мутацию.
мутации: метод операции изменения состояния, запускаемый коммитом('имя мутации') в действиях. это единственный рекомендуемый способ изменения состояния Vuex. Этот метод может выполнять только синхронные операции, а имя метода может быть только глобально уникальным. Во время операции будут обнаружены некоторые крючки для мониторинга состояния и так далее.
state: объект-контейнер управления состоянием страницы. Централизованно храните разрозненные данные объекта данных в компонентах Vue, что является уникальным в глобальном масштабе для унифицированного управления состоянием. Данные, необходимые для отображения страницы, считываются из этого объекта, а детальный механизм ответа данных Vue используется для выполнения эффективных обновлений состояния.
геттеры: метод чтения объекта состояния. Этот модуль не указан отдельно на рисунке и должен быть включен в визуализацию.Компоненты Vue считывают объект глобального состояния с помощью этого метода.
3. Специальное использование:
store/index.js
import Vuex from 'vuex';
import Vue from 'vue';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
stateName: 'xxxx'
},
mutations: {
mutationsName(state, {params}) {
state.stateName = params;
console.log("只有在mutations中才能直接改变state中的值")
}
},
actions: {
actionName({ state,commit}, {params}) {
let actionParam = 'mmm';
commit('mutationsName', actionParam );
console.log(" 触发mutation 方法要用commit分发,以此改变state");
}
}
});
export default store;
main.js
import store from './store/index.js';
new Vue({
el: '#app',
store,
console.log("将store挂载到vue实例上")
render: h => h(App)
})
использовать в компоненте
раздел child.vue js
**import { mapActions, mapMutations, mapState } from 'vuex';
export default {
computed: {
...mapState({ stateName })
},
methods: {
...mapActions(['actionName']),
...mapMutations(['mutationName'])
console.log("使用辅助函数mapMutations直接将触发函数映射到methods上")
}
// 接下来在实例中就可以用this.stateName,this.actionName来调用
}
Когда есть много одноуровневых компонентов и данные для обработки огромны, можно использовать модули в vuex, чтобы сделать структуру более понятной.
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
Способ четвертый,$attrs/ $listeners
1. Введение
Когда для многоуровневой вложенности компонентов необходимо передать данные, обычно используется метод через vuex. Но если вы просто передаете данные без промежуточной обработки, использование обработки vuex немного излишне. Есть проблема с передачей данных между родительским и дочерним компонентами: если есть дочерний компонент B под родительским компонентом A, а компонент C под компонентом B, что, если компонент A захочет передать данные компоненту C? Если используется метод props, мы должны позволить компоненту A передавать сообщения компоненту B через реквизиты, а компоненту B передавать сообщения компоненту C через реквизиты; если между компонентом A и компонентом C больше компонентов, использовать это очень просто. метод сложный. Vue 2.4 начал предоставлять атрибуты и прослушиватели для решения этой проблемы, позволяя компоненту A передавать сообщения компоненту C.
Официальный сайт объясняет $attrs следующим образом:
$attrs:包含了父作用域中不被 prop 所识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件。通常配合 interitAttrs 选项一起使用。
Официальный сайт объясняет $listeners следующим образом:
$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
Выше приведено объяснение $attrs на официальном сайте. Я был сбит с толку, когда впервые увидел его. Вернуться назад и попробовать несложно, и он больше подходит для задачи передачи значений от компонентов-предков к подкомпоненты в сценарии глубоко вложенных компонентов.
Мое понимание:
意思就是父组件传向子组件传的,子组件不prop接受的数据都会放在$attrs中,子组件直接用this.$attrs获取就可以了。如过从父->孙传,就在子组件中添加v-bind='$attrs',就把父组件传来的子组件没props接收的数据全部传到孙组件;我觉得 $attrs 和 $listeners 属性像两个收纳箱,一个负责收纳属性,一个负责收纳事件,都是以对象的形式来保存数据;
Далее мы рассмотрим пример межуровневого общения:
// index.vue
<template>
<div>
<h2>科大讯飞</h2>
<child-com1
:foo="foo"
:boo="boo"
:coo="coo"
:doo="doo"
@one.native="triggerOne"
@two="triggerTwo"
title="Vue通信"
></child-com1>
</div>
</template>
<script>
const childCom1 = () => import("./childCom1.vue");
export default {
components: { childCom1 },
data() {
return {
foo: "Javascript",
boo: "Html",
coo: "CSS",
doo: "Vue"
};
},
methods:{
triggerOne(){
alert('one')
},
triggerTwo(){
alert('two')
}
}
};
</script>
// childCom1.vue
<template class="border">
<div>
<p>foo: {{ foo }}</p>
<p>childCom1的$attrs: {{ $attrs }}</p>
<child-com2 v-bind="$attrs" v-on="$listeners"></child-com2>
</div>
</template>
<script>
const childCom2 = () => import("./childCom2.vue");
export default {
components: {
childCom2
},
// 默认为true,如果传入的属性子组件没有prop接受,就会以字符串的形式出现为标签属性
// 设为false,在dom中就看不到这些属性,试一下就知道了
inheritAttrs: false, // 可以关闭自动挂载到组件根元素上的没有在props声明的属性
props: {
foo: String // foo作为props属性绑定
},
mounted() {
console.log(this.$attrs); // { "boo": "Html", "coo": "CSS", "doo": "Vue", "title": "Vue通信" }
console.log(this.$listeners);
}
};
</script>
// childCom2.vue
<template>
<div class="border">
<p>boo: {{ boo }}</p>
<p>childCom2的$attrs: {{ $attrs }}</p>
<child-com3 v-bind="$attrs" v-on="$listeners"></child-com3>
</div>
</template>
<script>
const childCom3 = () => import("./childCom3.vue");
export default {
components: {
childCom3
},
inheritAttrs: false,
props: {
boo: String
},
mounted() {
console.log(this.$attrs); // {"coo": "CSS", "doo": "Vue", "title": "Vue通信" }
console.log(this.$listeners);
}
};
</script>
// childCom3.vue
<template>
<div class="border">
<p>childCom3: {{ $attrs }}</p>
</div>
</template>
<script>
export default {
props: {
coo: String,
title: String
},
mounted() {
console.log(this.$listeners);
// this.$listeners.two();
}
};
</script>
$attrs表示没有继承数据的对象,格式为{属性名:属性值}。Vue2.4提供了 $attrs , $listeners 来传递数据与事件,跨级组件之间的通讯变得更简单。
简单来说: $attrs与 $listeners 是两个对象, $attrs 里存放的是父组件中绑定的非 Props 属性, $listeners里存放的是父组件中绑定的非原生事件。
Метод 5, предоставить/ввести (при использовании базовых общих компонентов частота использования будет очень высокой)
1. Введение
Vue2.2.0 добавил API, эту пару параметров необходимо использовать вместе, чтобы компонент-предок мог вводить зависимость всем своим потомкам, независимо от того, насколько глубок уровень компонента, и он всегда будет действовать, когда восходящие и нисходящие отношения установлены. Одним словом: компонент-предок предоставляет переменные через провайдера, а затем внедряет переменные через инжект в компонент-потомок. API предоставления/внедрения в основном решает проблему связи между межуровневыми компонентами, но его сценарии использования в основном предназначены для подкомпонентов для получения состояния родительского компонента, а между межуровневыми компонентами устанавливается связь между активным предоставлением и внедрением зависимостей. . Давайте взглянем на официальное описание Provide/inject: Предоставлять и внедрять в основном обеспечивают варианты использования для библиотек плагинов/компонентов более высокого порядка. Его не рекомендуется использовать непосредственно в коде приложения. И эту пару опций необходимо использовать вместе, чтобы позволить компоненту-предку внедрить зависимость во все его потомки, независимо от того, насколько глубока иерархия компонентов, и это всегда будет действовать, пока устанавливаются отношения восходящего и нисходящего потоков.
2. Приведите пример
Предположим, что есть два компонента: A.vue и B.vue, B является подкомпонентом A
// A.vue
export default {
provide: {
name: '科大讯飞'
}
}
// B.vue
export default {
inject: ['name'],
mounted () {
console.log(this.name); // 科大讯飞
}
}
Как вы можете видеть, в A.vue мы установили provider: name со значением iFLYTEK, и его функция — предоставить имя переменной всем его подкомпонентам. В B.vue переменная name, предоставленная компонентом A, вводится через inject, затем в компоненте B к этой переменной можно получить доступ напрямую через this.name, и ее значение также равно iFLYTEK. Это основное использование API предоставления/внедрения.
Одно предостережение: привязки Provide и Inject не являются реактивными. Это сделано намеренно. Однако, если вы передаете прослушиваемый объект, свойства объекта по-прежнему реагируют — официальная документация vue. Следовательно, если имя A.vue, указанное выше, будет изменено, this.name B.vue не изменится, и оно по-прежнему будет iFLYTEK.
3. Как обеспечить и внедрить реакцию на данные агрегата
Вообще говоря, есть два пути:
Предоставьте экземпляр компонента-предка, а затем внедрите зависимости в компонент-потомок, чтобы вы могли напрямую изменять свойства экземпляра компонента-предка в компоненте-потомке, но этот метод имеет недостаток, заключающийся в том, что монтируются многие ненужные вещи, такие как свойства. на этом экземпляре. , методы
Используйте последнюю версию API Vue.observable версии 2.6 для оптимизации адаптивного предоставления (рекомендуется) Давайте рассмотрим пример: компонент-внук E и получить значение цвета, переданное компонентом A, и может реализовать изменение, реагирующее на данные, то есть после изменения цвета компонента A соответственно изменятся компоненты E и F ( основной код выглядит следующим образом:)
// A 组件
<div>
<h1>A 组件</h1>
<button @click="() => changeColor()">改变color</button>
<ChildrenB />
<ChildrenC />
</div>
......
data() {
return {
color: "blue"
};
},
// provide() {
// return {
// theme: {
// color: this.color //这种方式绑定的数据并不是可响应的
// } // 即A组件的color变化后,组件E、F 不会跟着变
// };
// },
provide() {
return {
theme: this//方法一:提供祖先组件的实例
};
},
methods: {
changeColor(color) {
if (color) {
this.color = color;
} else {
this.color = this.color === "blue" ? "red" : "blue";
}
}
}
// 方法二:使用vue2.6最新API Vue.observable 优化响应式 provide
// provide() {
// this.theme = Vue.observable({
// color: "blue"
// });
// return {
// theme: this.theme
// };
// },
// methods: {
// changeColor(color) {
// if (color) {
// this.theme.color = color;
// } else {
// this.theme.color = this.theme.color === "blue" ? "red" : "blue";
// }
// }
// }
// F 组件
<template functional>
<div class="border2">
<h3 :style="{ color: injections.theme.color }">F 组件</h3>
</div>
</template>
<script>
export default {
inject: {
theme: {
//函数式组件取值不一样
default: () => ({})
}
}
};
</script>
Несмотря на то, что обеспечить и внедрить в основном предоставляют варианты использования для высокоуровневых библиотек подключаемых модулей/компонентов, если вы можете умело использовать их в своем бизнесе, вы можете добиться вдвое большего результата, затрачивая половину усилий!
Способ шестой,$parent / $children и ссылка
$parent / $children:访问父 / 子实例
ref:如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件实例
Следует отметить, что эти два метода напрямую получают экземпляр компонента, и вы можете напрямую вызвать метод компонента или получить доступ к данным после использования. Давайте сначала рассмотрим пример использования ref для доступа к компоненту:
// component-a 子组件
export default {
data () {
return {
title: 'Vue.js'
}
},
methods: {
sayHello () {
window.alert('Hello');
}
}
}
// 父组件
<template>
<component-a ref="comA"></component-a>
</template>
<script>
export default {
mounted () {
const comA = this.$refs.comA;
console.log(comA.title); // Vue.js
comA.sayHello(); // 弹窗
}
}
</script>
$parent 和 $children
Этот метод, который следует здесь упомянуть, является более интуитивным и напрямую работает с экземпляром родительско-дочернего компонента.$parentявляется экземпляром объекта родительского компонента, и$childrenЭто экземпляр прямого дочернего компонента текущего экземпляра, но это значение свойства имеет тип массива, порядок не гарантируется и не отвечает.
родительский компонент:
<template>
<div class="">
<p>this is parent component!</p>
<button @click="changeChildValue">test</button>
<child />
</div>
</template>
<script>
export default {
data() {
return {
message: 'hello'
}
},
methods: {
changeChildValue(){
this.$children[0].mymessage = 'hello';
}
},
}
</script>
<style lang="less" scoped>
</style>
Детский компонент:
<template>
<div class="">
<input type="text" v-model="mymessage" @change="changeValue" />
</div>
</template>
<script>
export default {
data() {
return {
mymessage: this.$parent.message
}
},
methods: {
changeValue(){
this.$parent.message = this.mymessage;//通过如此调用可以改变父组件的值
console.log(this.$parent.message)
}
}
}
</script>
<style lang="less" scoped>
</style>
Однако недостатком этих двух методов является то, что нет возможности общаться между уровнями или братьями и сестрами.
// parent.vue
<component-a></component-a>
<component-b></component-b>
<component-b></component-b>
Мы хотим получить доступ к двум компонентам компонента-b на странице, которая ссылается на него (здесь parent.vue) в компоненте-а. В этом случае нам нужно настроить дополнительные плагины или инструменты, такие как решение Vuex и Bus.
Суммировать
常见使用场景可以分为三类:
父子通信: 父向子传递数据是通过 props,子向父是通过 events( $emit);通过父链 / 子链也可以通信( $parent / $children);ref 也可以访问组件实例;provide / inject API; $attrs/$listeners
兄弟通信: Bus;Vuex
跨级通信: Bus;Vuex;provide / inject API、 $attrs/$listeners
Ссылаться на:
Всеобъемлющее резюме взаимодействия компонентов vue
1212 способов взаимодействия между родительскими и дочерними компонентами в Vue.js
Передача данных родительско-дочернего компонента Vue
Изучение предоставления/внедрения в vue
Всестороннее и подробное объяснение методов связи компонентов Vue.