Dieser Artikel vermittelt Ihnen relevantes Wissen über vue und stellt hauptsächlich die Kommunikationsmethoden zwischen Vue-Komponenten vor. Die Kommunikation zwischen Vue-Komponenten war schon immer ein wichtiges Thema, obwohl die offiziell eingeführte Vuex-Zustandsverwaltungslösung das Problem der Komponentenkommunikation sehr gut lösen kann Probleme zwischen ihnen, aber die Verwendung von Vuex innerhalb der Komponentenbibliothek ist oft schwieriger. Schauen wir uns das gemeinsam an. Ich hoffe, es wird für alle hilfreich sein.
[Verwandte Empfehlungen: Javascript-Video-Tutorial, vue.js-Tutorial]
Während des Entwicklungsprozesses der Vue-Komponentenbibliothek war die Kommunikation zwischen Vue-Komponenten schon immer ein wichtiges Thema, obwohl das offizielle Vuex The Eine Zustandsverwaltungslösung kann das Kommunikationsproblem zwischen Komponenten sehr gut lösen, aber die Verwendung von Vuex innerhalb der Komponentenbibliothek ist oft recht aufwändig. In diesem Artikel werden systematisch mehrere praktischere Kommunikationsmethoden zwischen Komponenten ohne Verwendung von Vuex aufgeführt.
Bevor wir heute in unser Thema einsteigen, fassen wir zunächst einige Kommunikationsszenarien zwischen Vue-Komponenten zusammen, die im Allgemeinen in die folgenden Szenarien unterteilt werden können:
Die Kommunikation zwischen übergeordneten und untergeordneten Komponenten sollte die einfachste Vue-Komponentenkommunikation sein, die in zwei Teile zusammengefasst werden kann: Die übergeordnete Komponente übergibt Daten über Requisiten an die untergeordnete Komponente, und die untergeordnete Komponente übergibt Daten über benutzerdefinierte Ereignisse an die übergeordnete Komponente.
Übergeordnete Komponente KomponenteA:
<template> <p> <component-b title="welcome"></component-b> </p> </template> <script> import ComponentB from './ComponentB' export default { name: 'ComponentA', components: { ComponentB } } </script>
Untergeordnete Komponente KomponenteB:
<template> <p> <p>{{title}}</p> </p> </template> <script> export default { name: 'ComponentB', props: { title: { type: String, } } } </script>
$emit</code übergeben > zur übergeordneten Komponente Ein Ereignis tritt in der Komponente auf und wird in der übergeordneten Komponente über <code>v-on
/@
überwacht. Unterkomponente ComponentA:
<template> <p> <component-b :title="title" @title-change="titleChange"></component-b> </p> </template> <script> import ComponentB from './ComponentB' export default { name: 'ComponentA', components: { ComponentB }, data: { title: 'Click me' }, methods: { titleChange(newTitle) { this.title = newTitle } } } </script>
Unterkomponente ComponentB:$emit
向父组件发生一个事件,在父组件中通过 v-on
/@
进行监听。
子组件 ComponentA:
<template> <p> <p @click="handleClick">{{title}}</p> </p> </template> <script> export default { name: 'ComponentB', props: { title: { type: String, } }, methods: { handleClick() { this.$emit('title-change', 'New title !') } } } </script>
子组件 ComponentB:
<template> <p> <p>{{title}}</p> <p @click="changeTitle">click me</p> </p> </template> <script> export default { name: 'ComponentA', props: { title: { type: String }, changeTitle: Function } } </script>
这个例子非常简单,在子组件 ComponentB 里面通过 $emit
派发一个事件 title-change
,在父组件 ComponentA 通过 @title-change
绑定的 titleChange
事件进行监听,ComponentB 向 ComponentA 传递的数据在 titleChange
函数的传参中可以获取到。
写过 React 的同学应该对组件的 状态提升
概念并不陌生,React 里面将组件按照职责的不同划分为两类:展示型组件(Presentational Component)
和 容器型组件(Container Component)
。
展示型组件不关心组件使用的数据是如何获取的,以及组件数据应该如何修改,它只需要知道有了这些数据后,组件UI是什么样子的即可。外部组件通过 props 传递给展示型组件所需的数据和修改这些数据的回调函数,展示型组件只是它们的使用者。
容器型组件的职责是获取数据以及这些数据的处理逻辑,并把数据和逻辑通过 props 提供给子组件使用。
因此,参考 React 组件中的 状态提升
<template> <p> <p>{{title}}</p> <p @click="changeTitle">click me</p> </p> </template> <script> export default { name: 'ComponentB', props: { title: { type: String }, changeTitle: Function } } </script>
Dieses Beispiel ist sehr einfach. Verwenden Sie in der Unterkomponente ComponentB $emit
, um ein Ereignis < auszulösen code>title-change, die übergeordnete Komponente ComponentA überwacht das durch @title-change
gebundene titleChange
-Ereignis und die von ComponentB an ComponentA übergebenen Daten befinden sich in < code>titleChange Es kann aus den Funktionsparametern abgerufen werden. Kommunikation zwischen Geschwisterkomponenten
StatusförderungStudenten, die React geschrieben haben, sollten mit dem Konzept der Statusförderung
für Komponenten vertraut sein. React unterteilt Komponenten nach unterschiedlichen Verantwortlichkeiten in zwei Kategorien Präsentationskomponente und Containerkomponente
.
Der Anzeigekomponente ist es egal, wie die von der Komponente verwendeten Daten abgerufen werden und wie die Komponentendaten geändert werden sollen. Sie muss lediglich wissen, wie die Benutzeroberfläche der Komponente mit diesen Daten aussieht. Externe Komponenten übergeben Requisiten an die von den Präsentationskomponenten benötigten Daten und die Rückruffunktionen, die diese Daten ändern. Die Präsentationskomponenten sind lediglich ihre Benutzer. Die Verantwortung der Containerkomponente besteht darin, Daten und die Verarbeitungslogik dieser Daten abzurufen und die Daten und Logik über Requisiten an Unterkomponenten bereitzustellen.
Daher stellen wir in Anlehnung an das Konzept der Statusförderung
in React-Komponenten eine übergeordnete Komponente über den beiden Geschwisterkomponenten bereit, die der Containerkomponente entspricht und für die Datenverarbeitung verantwortlich ist Geschwisterkomponenten erhalten Parameter über Requisiten und Die Rückruffunktion entspricht der Anzeigekomponente, um das Kommunikationsproblem zwischen Geschwisterkomponenten zu lösen.
<template>
<p>
<component-a :title="titleA" :change-title="titleAChange"></component-a>
<component-b :title="titleB" :change-title="titleBChange"></component-b>
</p>
</template>
<script>
import ComponentA from './ComponentA'
import ComponentB from './ComponentB'
export default {
name: 'ComponentC',
components: {
ComponentA,
ComponentB
},
data: {
titleA: 'this is title A',
titleB: 'this is title B'
},
methods: {
titleAChange() {
this.titleA = 'change title A'
},
titleBChange() {
this.titleB = 'change title B'
}
}
}
</script>
<template> <component-a name="Lin" age="24" sex="male"></component-a> </template> <script> import ComponentB from '@/components/ComponentB.vue' export default { name: 'App', components: { ComponentA } } </script>
<template> <p> I am component B <component-c v-bind="$attrs"></component-c> </p> </template> <script> import ComponentC from '@/components/ComponentC.vue' export default { name: 'ComponentB', inheritAttrs: false, components: { ComponentC } } </script>
$attrs
/$listeners
rovide
/inject
$parent
/$children
实现的 dispatch
和 broadcast
Vue 2.4.0 版本新增了 $attrs
和 $listeners
两个方法。先看下官方对 $attrs
的介绍:
包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定(
class
和style
除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class
和style
除外),并且可以通过v-bind="$attrs"
传入内部组件——在创建高级别的组件时非常有用。
看个例子:
组件A(ComponentA):
<template> <component-a name="Lin" age="24" sex="male"></component-a> </template> <script> import ComponentB from '@/components/ComponentB.vue' export default { name: 'App', components: { ComponentA } } </script>
组件B(ComponetB):
<template> <p> I am component B <component-c v-bind="$attrs"></component-c> </p> </template> <script> import ComponentC from '@/components/ComponentC.vue' export default { name: 'ComponentB', inheritAttrs: false, components: { ComponentC } } </script>
组件C(ComponetC):
<template> <p> I am component C </p> </template> <script> export default { name: 'ComponentC', props: { name: { type: String } }, mounted: function() { console.log('$attrs', this.$attrs) } } </script>
这里有三个组件,祖先组件(ComponentA)、父组件(ComponentB)和子组件(ComponentC)。这三个组件构成了一个典型的子孙组件之间的关系。
ComponetA 给 ComponetB 传递了三个属性 name、age 和 sex,ComponentB 通过 v-bind="$attrs"
将这三个属性再透传给 ComponentC, 最后在 ComponentC 中打印 $attrs
的值为:
{age: '24', sex: 'male'}
为什么我们一开始传递了三个属性,最后只打印了两个属性 age 和 sex 呢?因为在 ComponentC 的props 中声明了 name 属性,$attrs
会自动排除掉在 props 中声明的属性,并将其他属性以对象的形式输出。
说白了就是一句话,$attrs
可以获取父组件中绑定的非 Props 属性。
一般在使用的时候会同时和 inheritAttrs
属性配合使用。
如果你不希望组件的根元素继承 attribute,你可以在组件的选项中设置 inheritAttrs: false
。
在 ComponentB 添加了 inheritAttrs=false
属性后,ComponentB 的dom结构中可以看到是不会继承父组件传递过来的属性:
如果不加上 inheritAttrs=false
属性,就会自动继承父组件传递过来的属性:
再看下 $listeners
的定义:
包含了父作用域中的 (不含
.native
修饰器的)v-on
事件监听器。它可以通过v-on="$listeners"
传入内部组件——在创建更高层次的组件时非常有用。
$listeners
也能把父组件中对子组件的事件监听全部拿到,这样我们就能用一个v-on
把这些来自于父组件的事件监听传递到下一级组件。
继续改造 ComponentB 组件:
<template> <p> I am component B <component-c v-bind="$attrs" v-on="$listeners"></component-c> </p> </template> <script> import ComponentC from '@/components/ComponentC.vue' export default { name: 'ComponentB', inheritAttrs: false, components: { ComponentC } } </script>
这里利用 $attrs
和 $listeners
方法,可以将祖先组件(ComponentA) 中的属性和事件透传给孙组件(ComponentC),这样就可以实现隔代组件之间的通信。
provide/inject
是 Vue 2.2.0 版本后新增的方法。
这对选项需要一起使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。如果你熟悉 React,这与 React 的上下文特性很相似。
先看下简单的用法:
父级组件:
export default { provide: { name: 'Lin' } }
子组件:
export default { inject: ['name'], mounted () { console.log(this.name); // Lin } }
上面的例子可以看到,父组件通过 privide
返回的对象里面的值,在子组件中通过 inject
注入之后可以直接访问到。
但是需要注意的是,provide
和 inject
绑定并不是可响应的,按照官方的说法,这是刻意为之的。
也就是说父组件 provide 里面的name属性值变化了,子组件中 this.name 获取到的值不变。
如果想让 provide 和 inject 变成可响应的,有以下两种方式:
看一下第一种场景:
祖先组件组件(ComponentA):
export default { name: 'ComponentA', provide() { return { app: this } }, data() { return { appInfo: { title: '' } } }, methods: { fetchAppInfo() { this.appInfo = { title: 'Welcome to Vue world'} } } }
我们把整个 ComponentA.vue 的实例 this
对外提供,命名为 app
。接下来,任何组件只要通过 inject
注入 app 的话,都可以直接通过 this.app.xxx
来访问 ComponentA.vue 的 data
、computed
、methods
等内容。
子组件(ComponentB):
<template> <p> {{ title }} <button @click="fetchInfo">获取App信息</button> </p> </template> <script> export default { name: 'ComponentB', inject: ['app'], computed: { title() { return this.app.appInfo.title } }, methods: { fetchInfo() { this.app.fetchAppInfo() } } } </script>
这样,任何子组件,只要通过 inject
注入 app
后,就可以直接访问祖先组件中的数据了,同时也可以调用祖先组件提供的方法修改祖先组件的数据并反应到子组件上。
当点击子组件(ComponentB)的获取App信息按钮,会调用 this.app.fetchAppInfo
方法,也就是访问祖先组件(ComponentA)实例上的 fetchAppInfo 方法,fetchAppInfo 会修改fetchAppInfo的值。同时子组件(ComponentB)中会监听 this.app.appInfo 的变化,并将变化后的title值显示在组件上。
再看一下第二种场景,通过 Vue.observable
方法来实现 provide
和 inject
绑定并可响应。
基于上面的示例,改造祖先组件(ComponentA):
import Vue from 'vue' const state = Vue.observable({ title: '' }); export default { name: 'ComponentA', provide() { return { state } } }
使用 Vue.observable
定义一个可响应的对象 state,并在 provide 中返回这个对象。
改造子组件(ComponentB):
<template> <p> {{ title }} <button @click="fetchInfo">获取App信息</button> </p> </template> <script> export default { name: 'ComponentInject', inject: ['state'], computed: { title() { return this.state.title } }, methods: { fetchInfo() { this.state.title = 'Welcome to Vue world22' } } } </script>
与之前的例子不同的是,这里我们直接修改了 this.state.title 的值,因为 state 被定义成了一个可响应的数据,所以 state.title 的值被修改后,视图上的 title 也会立即响应并更新,从这里看,其实很像 Vuex
的处理方式。
以上两种方式对比可以发现,第二种借助于 Vue.observable
方法实现 provide
和 inject
的可响应更加简单高效,推荐大家使用这种方式。
先了解下 dispatch 和 broadcast 两个概念:
$on
进行监听$on
进行监听在实现 dispatch 和 broadcast 方法之前,先来看一下具体的使用方法。有 ComponentA.vue 和 ComponentB.vue 两个组件,其中 ComponentB 是 ComponentA 的子组件,中间可能跨多级,在 ComponentA 中向 ComponentB 通信:
组件ComponentA:
<template> <button @click="handleClick">派发事件</button> </template> <script> import Emitter from '../mixins/emitter.js'; export default { name: 'ComponentA', mixins: [Emitter], methods: { handleClick () { this.dispatch('ComponentB', 'on-message', 'Hello Vue.js') } } } </script>
组件ComponentB:
export default { name: 'ComponentB', created () { this.$on('on-message', this.showMessage) }, methods: { showMessage (text) { console.log(text) } } }
dispatch 的逻辑写在 emitter.js
中,使用的时候通过 mixins
混入到组件中,这样可以很好的将事件通信逻辑和组件进行解耦。
dispatch 的方法有三个传参,分别是:需要接受事件的组件的名字(全局唯一,用来精确查找组件)、事件名和事件传递的参数。
dispatch 的实现思路非常简单,通过 $parent
获取当前父组件对象,如果组件的name和接受事件的name一致(dispatch方法的第一个参数),在父组件上调用 $emit
发射一个事件,这样就会触发目标组件上 $on
定义的回调函数,如果当前组件的name和接受事件的name不一致,就递归地向上调用此逻辑。
dispath:
export default { methods: { dispatch(componentName, eventName, params) { let parent = this.$parent || this.$root; let name = parent.$options.name; while (parent && (!name || name !== componentName)) { parent = parent.$parent; if (parent) { name = parent.$options.name } } if (parent) { parent.$emit.apply(parent, [eventName].concat(params)); } } } }
broadcast逻辑和dispatch的逻辑差不多,只是一个是通过 $parent
向上查找,一个是通过 $children
向下查找,
export default { methods: { broadcast(componentName, eventName, params) { this.$children.forEach(child => { const name = child.$options.name if (name === componentName) { child.$emit.apply(child, [eventName].concat(params)) } else { broadcast.apply(child, [componentName, eventName].concat([params])) } }) } } }
【相关推荐:javascript视频教程、vue.js教程】
Das obige ist der detaillierte Inhalt vonDetaillierte Analyse der Kommunikationsmethoden zwischen Vue-Komponenten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!