前言:
本片文章着重将组件通信的简单实现代码给大家呈现以下,希望对大家有所帮助
组件通信:
父组件 => 子组件:
属性props
//父页面
<template>
<div id="app">
<input type="text" v-model="text" @change="dataChange">
<Children :msg="text"></Children>
</div>
</template>
<script>
import Children from './components/组件传值/son'
export default {
name: "app",
components: {
Children
},
data() {
return {
text: "父组件的值",
};
},
methods: {
dataChange(data) {
this.msg = data
}
}
};
</script>
//子组件
<template>
<div id="container"> {{msg}} </div>
</template>
<script>
export default {
data() {
return {};
},
props: {
msg: String
}
};
</script>
引用refs
//父页面
<template>
<div id="app">
<input type="text" v-model="text" @change="dataChange">
<Children ref="msg"></Children>
</div>
</template>
<script>
import Children from './components/组件传值/son'
export default {
name: "app",
components: {
Children
},
data() {
return {
text: "父组件的值",
};
},
methods: {
dataChange(data) {
this.$refs.msg.msg = data.target.value;
}
},
mounted() {
this.$refs.msg.msg = this.text;
}
};
</script>
//子组件
<template>
<div id="container"> {{msg}} </div>
</template>
<script>
export default {
data() {
return {};
},
props: {
msg: String
}
};
</script>
子组件 => 父组件:自定义事件
//父组件
<template>
<div id="app">
<p>我是父组件</p>
<ChildrenEmit :isShow="show" @hidechild="hidechild"></ChildrenEmit>
<button @click="show=true">显示子组件</button>
</div>
</template>
<script>
import ChildrenEmit from './components/组件传值/父子组件通信 emit/son'
export default {
name: "app",
components: {
ChildrenEmit
},
data() {
return {
show: false
};
},
methods: {
hidechild: function (data) {
this.show = data;
}
}
};
</script>
//子组件
<template>
<div>
<h2 v-show="isShow">我是子组件</h2>
<button @click="hideMyself()">隐藏子组件</button>
</div>
</template>
<script>
export default {
name: "child",
props: {
isShow: Boolean
},
methods: {
hideMyself: function () {
this.$emit('hidechild', false);
}
}
}
</script>
兄弟组件:通过共同祖辈组件
通过共同的祖辈组件搭桥,$parent或$root。
//父组件
<template>
<div id="app">
<son :message="sisterText"></son>
<sister :message="sonText"></sister>
</div>
</template>
<script>
import son from './components/组件传值/兄弟组件通信/son'
import sister from './components/组件传值/兄弟组件通信/sister'
export default {
name: "app",
components: {
son,
sister
},
data() {
return {
sonText: '',
sisterText: ''
};
},
methods: {
sendMessage: function (data, who) {
if (who == '姐姐') {
this.sisterText = data;
} else {
this.sonText = data;
}
}
}
};
</script>
//子组件 姐姐
<template>
<div>
<h2>我是姐姐组件</h2>
<h3>{{message}}</h3>
<button @click="sendMessage()">给弟弟发消息</button>
</div>
</template>
<script>
export default {
name: "sister",
props: {
message: String
},
methods: {
sendMessage: function () {
this.$parent.sendMessage('弟弟该吃饭了', '姐姐');
}
}
}
</script>
//子组件 弟弟
<template>
<div>
<h2>我是弟弟组件</h2>
<h3>{{message}}</h3>
<button @click="sendMessage()">给姐姐发消息</button>
</div>
</template>
<script>
export default {
name: "son",
props: {
message: String
},
methods: {
sendMessage: function () {
this.$parent.sendMessage('姐姐该吃饭了', '弟弟');
}
}
}
</script>
祖先和后代之间
provide/inject:能够实现祖先给后代传值
// ancestor 祖辈
<template>
<div id="app">
<grandSon></grandSon>
</div>
</template>
<script>
import grandSon from './components/组件传值/祖先后代传值/grandSon'
export default {
name: "app",
components: {
grandSon
},
data() {
return {
message: '我是爷爷组件传过来的数据'
};
},
provide() {
return {
message: this.message
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
//我是孙子组件
<template>
<div>
<h2>我是孙子组件</h2>
<h3>{{message}}</h3>
</div>
</template>
<script>
export default {
name: "son",
inject: ['message']
}
</script>
注: provide 和 inject 主要为高级插件/组价库提供,不推荐直接在应用程序编写,我们更多会在开源组件库中见到
dispatch:后代给祖先传值
// 祖先
<template>
<div id="app">
<grandSon></grandSon>
</div>
</template>
<script>
import grandSon from './components/组件传值/后代祖先传值/grandSon'
export default {
name: "app",
components: {
grandSon
},
mounted() {
this.$on('hello', (data) => {
console.log(data);
});
},
};
</script>
<template>
<div>
<h2 @click="dispatch('hello', 'hello,world')">我是孙子组件</h2>
</div>
</template>
<script>
export default {
name: "son",
methods: {
// 定义一个dispatch方法,指定要派发事件名称和数据
dispatch(eventName, data) {
let parent = this.$parent
// 只要还存在父元素就继续往上查找
while (parent) {
// 父元素用$emit触发
parent.$emit(eventName, data)
// 递归查找父元素
parent = parent.$parent
}
}
},
}
</script>
任意两个组件之间:事件总线 或 vuex
事件总线:创建一个Bus类负责事件派发、监听和回调管理
// Bus:事件派发、监听和回调管理
class Bus{
constructor(){
// {
// eventName1:[fn1,fn2],
// eventName2:[fn3,fn4],
// }
this.callbacks = {}
}
$on(name, fn){
this.callbacks[name] = this.callbacks[name] || []
this.callbacks[name].push(fn)
}
$emit(name, args){
if(this.callbacks[name]){
this.callbacks[name].forEach(cb => cb(args))
}
}
//父页面
<template>
<div id="app">
<son></son>
<sister></sister>
</div>
</template>
<script>
import son from './components/组件传值/任意两个组件传值/son'
import sister from './components/组件传值/任意两个组件传值/sister'
export default {
name: "app",
components: {
son,
sister,
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
//子页面
<template>
<div>
<h2>我是姐姐组件</h2>
<h3>{{message}}</h3>
<button @click="sendToSonMessage()">给弟弟发消息</button>
</div>
</template>
<script>
import Bus from './bus'
export default {
name: "sister",
methods: {
sendToSonMessage: function () {
Bus.$emit('sendToSonMessage', '弟弟该吃饭了');
}
},
mounted() {
Bus.$on('sendToSisterMessage', target => {
this.message = target
//注意:发送和监听的事件名称必须一致,target就是获取的数据,可以不写target。只要你喜欢叫什么都可以(当然了,这一定要符合形参变量的命名规范)
})
},
beforeDestroy() {
//清除事件总线eventBus
Bus.$off('sendToSisterMessage')
}
}
</script>
//子页面
<template>
<div>
<h2>我是弟弟组件</h2>
<h3>{{message}}</h3>
<button @click="sendToSisterMessage()">给姐姐发消息</button>
</div>
</template>
<script>
import Bus from './bus'
export default {
name: "son",
methods: {
sendToSisterMessage: function () {
Bus.$emit('sendToSisterMessage', '姐姐该吃饭了')
}
},
mounted() {
Bus.$on('sendToSonMessage', target => {
this.message = target
//注意:发送和监听的事件名称必须一致,target就是获取的数据,可以不写target。只要你喜欢叫什么都可以(当然了,这一定要符合形参变量的命名规范)
})
},
beforeDestroy() {
//清除事件总线eventBus
Bus.$off('sendToSonMessage')
}
}
</script>
注意:在实践中我们可以使用vue替代bus,因为它实现了相应功能
vuex: 创建唯一的全局数据管理者store,通过它管理数据并通知组件状态更改
插槽
Vue 2.6.0之后采用全新v-slot语法取代之前的slot、slot-scope
匿名插槽
//匿名插槽组件
<template>
<div>
<h2>我是匿名插槽组件</h2>
<slot></slot>
</div>
</template>
//主页面
<template>
<div id="app">
<slot>
<h4>哈哈哈,我是匿名插槽</h4>
</slot>
<!-- <grandSon></grandSon> -->
</div>
</template>
具名插槽
// comp2
<div>
<slot></slot>
<slot name="content"></slot>
</div>
// parent
<Comp2>
<!-- 默认插槽用default做参数 -->
<template v-slot:default>具名插槽</template>
<!-- 具名插槽用插槽名做参数 -->
<template v-slot:content>内容...</template>
</Comp2>
//父页面
<template>
<div id="app">
<example>
<!-- 默认插槽用default做参数 -->
<template v-slot:default></template>
<!-- 具名插槽用插槽名做参数 -->
<template v-slot:content>
<h3>我是名为 content的插槽的值 </h3>
</template>
</example>
</div>
</template>
//子页面
<template>
<div>
<slot>具名插槽的默认值</slot>
<slot name="content">
</slot>
</div>
</template>
<script>
export default {
name: "slot",
}
</script>
作用域插槽
// 子组件
<template>
<div>
<slot :foo='foo'>{{foo[0]}}</slot>
</div>
</template>
<script>
export default {
name: "slot",
data() {
return {
foo: [' 刘甜', 'hpp']
}
},
}
</script>
// parent
<template>
<div id="app">
<example>
<template v-slot="ctx">
{{ctx.foo[1]}}
</template>
</example>
</div>
</template>
递归数组
//父组件
<template>
<div>
<ul>
<item class="item" :model="treeData"></item>
</ul>
</div>
</template>
//循环数据:
treeData: {
title: "Web全栈架构师",
children: [
{
title: "Java架构师"
},
{
title: "JS高级",
children: [
{
title: "ES6"
},
{
title: "动效"
}
]
}
]
}
//子组件
<template>
<li>
<div @click="toggle">
{{model.title}}
<span v-if="isFolder">[{{open ? '-' : '+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item"
v-for="childmodel in model.children"
:model="childmodel"
:key="childmodel.title">
</item>
</ul>
</li>
</template>
<script>
export default {
name: "Item",
props: {
model: {
type: Object,
required: true
}
},
data() {
return {
open: false
};
},
computed: {
isFolder() {
// return this.model.children && this.model.children.length;
return this.model.children;
}
},
methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open;
}
}
}
};
</script>