1、父获取子组件数据事件能够经过下面几种方式:
css
一、经过给子组件绑定ref属性,获取子组件实例
二、经过this.$children获取子组件实例
三、经过vuex完成父子组件数据事件通讯
复制代码
2、子获取父组件数据事件能够经过下面几种方式:
html
一、经过props传递父组件数据、事件, 或者经过$emit和$on实现事件传递
二、经过ref属性,调用子组件方法,传递数据;经过props传递父组件数据、事件, 或者经过$emit和$on实现事件传递
三、经过this.$parent.$data或者this.$parevent._data获取父组件数据;经过this.$parent执行父组件方法
四、经过vuex完成父子组件数据事件通讯
复制代码
<div class="demo-block">
我是父组件内容,我有一个子组件名字叫{{childName1}},这是经过ref获取的内容; <br>
我是父组件内容,我有一个子组件名字叫{{childName2}},这是经过children获取的内容; <br>
<component-a ref="childA" :message1="msg1" :message2="msg2" :fn="setFn"></component-a><br>
<button @click="getName1()">refs获取子组件名称</button>
<button @click="getName2()">children获取子组件名称</button>
<button @click="setChild">触发子组件事件写入数据</button>
</div>
复制代码
<script>
var componentA = {
name: 'child1',
template: `<div><div>我是子组件child:{{message1}}-{{message2}},写入数据{{con}}</div><button @click="fn1">经过props调用父组件方法</button> <button @click="fn2">经过$parent调用父组件方法</button></div>`,
data() {
return {
child: true,
name: 'child1',
con:''
}
},
props: ['message1', 'message2', 'fn'],
methods: {
fn1() {
typeof this.fn === 'function' && this.fn();
},
fn2() {
this.$parent && this.$parent.setFn();
},
init(params) {
this.con = params.msg1 + params.msg2;
alert('我是子组件')
}
}
};
export default {
components: {
componentA
},
data() {
return {
msg1: 'hello',
msg2: 'world',
prevent: true,
childName1: '',
childName2: ''
};
},
methods: {
getName1() {
this.childName1 = this.$refs.childA.name;
},
getName2() {
this.childName2 = this.$children[0].name;
},
setChild() {
this.$children[0].init({
msg1: 'world',
msg2: 'hello',
});
},
setFn() {
alert('我是父组件')
}
}
}
</script>
复制代码
一、利用Vuex存储数据, 配合父组件watch进行事件监听
二、利用bus中央开关总线,挂载全局事件
三、利用$parent进去数据传递, $parent.$children调用兄弟组件事件
复制代码
<style> .tab-list { border-bottom: 1px solid #ccc; padding-bottom: 10px; } .tab-item { display: inline-block; width: 100px; text-align: center; border-right: 1px solid #ccc; } .tab-item.active { color: red; } </style>
<div class="demo-block">
<component-b ref="childB" :listType.sync='transitType'></component-b><br>
<component-c ref="childC"></component-c>
</div>
复制代码
<script>
var componentB = {
template: `<div>
<div class="tab-list">
<span class="tab-item" v-for="(item, index) in list" @click="changeType(item.type)" :class="{'active': item.type === type}">{{item.value}}</span>
</div>
</div>`,
data() {
return {
type: 0,
list: [{value: '新闻', type: 0}, {value: '汽车', type: 1}, {value: '娱乐', type: 2}]
}
},
props: ['listType'],
methods: {
changeType(type) {
this.type = type;
if(type === 0) { // 使用watch开关通知
this.$emit('update:listType', type)
} else if(type === 1) { // 使用中央总线;
/*挂载全局触发事件*/
window.Vue.$emit('switchType', type);
this.$emit('update:listType', type)
} else { // 使用$parent.$children
// this.$parent.$children[1].$options.name 能够获取组件名称
// this.$parent.$children[1].$options._componentTag 能够获取局部注册的组件标签名
this.$parent.$children[2].switchType && this.$parent.$children[2].switchType(type)
this.$emit('update:listType', type)
}
}
}
};
var componentC = {
name: 'childC',
template: `<div>
<div v-for="(item, index) in listInfo[listType]">{{item}}</div>
</div>`,
data() {
return {
listType: 0,
listInfo: {
0: ['新闻1', '新闻2', '新闻3'],
1: ['汽车1', '汽车2', '汽车3'],
2: ['娱乐1', '娱乐2', '娱乐3']
}
}
},
props: [],
methods: {
switchType(type) {
this.listType = type;
}
},
mounted() {
/*挂载全局触发事件*/
window.Vue.$on('switchType', (type) => {
console.log(type, 'type');
this.switchType(type);
});
}
};
export default {
components: {
componentB,
componentC
},
data() {
return {
transitType: 0, // 这是一个中转控制属性
};
},
methods: {
},
mounted() {
//
this.transitType = this.$refs.childB.type;
console.log(this.transitType)
},
watch: {
transitType(newType, oldType) {
if(newType === 0) {
this.$refs.childC.switchType && this.$refs.childC.switchType(newType);
}
}
}
}
</script>
复制代码
一、利用$attrs实现祖孙组件间的数据传递,$listeners实现祖孙组件间的事件监听
$attrs包含了父做用域中不做为 prop 被识别 (且获取) 的特性绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含全部父做用域的绑定 (class 和 style 除外),而且能够经过 v-bind="$attrs" 传入内部组件——在建立高级别的组件时很是有用。
$listeners包含了父做用域中的 (不含 .native 修饰器的) v-on 事件监听器。它能够经过 v-on="$listeners" 传入内部组件——在建立更高层次的组件时很是有用。
二、利用provide/inject 实现祖孙组件间的数据传递和事件监听
provide 和 inject 主要为高阶插件/组件库提供用例,容许一个祖先组件向其全部子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。
三、利用vuex传递存储数据,利用$parent、$children封装一套可向下广播事件,向上派发事件的方法;
复制代码
<style> .mask-bg { position: fixed; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, .3); } .alert { position: fixed; left: 50%; top: 50%; width: 300px; height: 200px; border-radius: 10px; text-align: center; background-color: #fff; transform: translate(-50%, -50%); } </style>
<div class="demo-block">
<div>
<div @click="isShow = true" style="margin-bottom: 20px">显示弹层(第一种方式)</div>
<my-mask :show="isShow" :title="title" :con="con" @on-submit="submit"></my-mask><br>
</div>
</div>
复制代码
// 弹层组件(子组件的子组件)
var alert = {
template: ` <div class="alert"> <h2>{{title}}</h2> <div class="alert-con"> {{con}} </div> <button class="submit" @click="submit">肯定</button> </div>`,
data() {
return {
}
},
props: ['title', 'con'],
methods: {
submit() {
this.$emit('on-submit')
}
}
};
// mask组件(子组件)
var myMask = {
template: `<div class="mask" v-if="show"> <div class="mask-bg"></div> <alert v-bind="$attrs" v-on="$listeners"></alert> </div>`,
components: {
alert
},
inheritAttrs: false,
data() {
return {
}
},
props: ['show'],
methods: {
},
watch: {
}
};
export default {
name: 'my-page',
components: {
myMask,
},
data() {
return {}
},
isShow: false,
title: '我是标题',
con: '我是内容',
};
},
methods: {
submit() {
window.alert('关闭弹层');
this.isShow = false;
},
}
}
复制代码
祖组件在传递数据给子组件的时候,若是子组件没有经过props接收祖组件传递的参数,那么这个数据会表如今子组件的根标签的属性上;经过设置 inheritAttrs 到 false,这些默认行为将会被去掉; 而后咱们能够在子组件绑定v-bind="
listeners实现祖孙之间的事件监听器传递vue
<style> .tab-list { border-bottom: 1px solid #ccc; padding-bottom: 10px; } .tab-item { display: inline-block; width: 100px; text-align: center; border-right: 1px solid #ccc; } .tab-item.active { color: red; } </style>
<div class="demo-block">
<div>
<div @click="changeCon()" style="margin-bottom: 20px">切换显示内容(第二种方式)</div>
<my-con></my-con><br>
</div>
</div>
复制代码
var myTable = {
template: `<div> <div class="tab-list"> <span class="tab-item" v-for="(item, index) in list" @click="switchType(item.type)" :class="{'active': item.type === store.state.type}">{{item.value}}</span> </div> </div>`,
data() {
return {
tableType: 0,
list: [{value: '新闻', type: 0}, {value: '汽车', type: 1}, {value: '娱乐', type: 2}]
}
},
inject: ['store'],
methods: {
switchType(value) {
this.store.commit('type', value)
}
}
};
var myList = {
template: `<div> <div v-for="(item, index) in listInfo[store.state.type]">{{item}}</div> </div>`,
data() {
return {
listType: 0,
listInfo: {
0: ['新闻1', '新闻2', '新闻3'],
1: ['汽车1', '汽车2', '汽车3'],
2: ['娱乐1', '娱乐2', '娱乐3']
}
}
},
inject: ['store'],
props: [],
methods: {
},
mounted() {
}
};
var myCon = {
template: `<div> <my-table></my-table> <my-list></my-list> </div>`,
data() {
return {
}
},
components: {
myTable,
myList,
mySearch
},
props: [],
methods: {
},
mounted() {
}
};
export default {
name: 'my-page',
components: {
myCon,
},
provide() {
return {
store: this.store
}
},
data() {
return {
store: {
state: {
type: 0
},
commit (type, value) {
this.state[type] = value
}
},
};
},
methods: {
changeCon() {
this.store.state.type ++;
if(this.store.state.type > 2) this.store.state.type = 0;
},
},
mounted() {
}
}
复制代码
<div class="demo-block">
<div>
<input type="text" placeholder="搜索(第三种方式)" class="input" @input="changInput"><span v-if="!hasSearch" style="display:inline-block; margin-bottom: 20px">请输入内容</span>
<my-con></my-con><br>
</div>
</div>
复制代码
var myCon = {
template: `<div> <my-table></my-table> <my-list></my-list> <my-search></my-search> </div>`,
data() {
return {
}
},
components: {
myTable,
myList,
mySearch
},
props: [],
methods: {
},
mounted() {
}
};
var mySearch = {
name: 'mySearch',
template: `<div> <p style="margin: 20px auto">{{content}}</p> </div>`,
data() {
return {
content: '没有搜索内容'
}
},
props: [],
methods: {
/*对多级父组件进行事件派发*/
dispatch(componentName, eventName, params) {
/*获取父组件,若是以及是根组件,则是$root*/
var parent = this.$parent || this.$root;
/*获取父节点的组件名*/
var name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
/*当父组件不是所需组件时继续向上寻找*/
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
/*找到所需组件后调用$emit触发当前事件*/
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
},
mounted() {
this.$on('on-input', (value) => {
this.content = value;
if(value == '') {
this.content = '没有搜索内容';
this.dispatch('my-page', 'no-data', false);
}
})
}
};
function broadcast(componentName, eventName, params) {
/*遍历当前节点下的全部子组件*/
this.$children.forEach(child => {
/*获取子组件名称*/
var name = child.$options.name;
if (name === componentName) {
/*若是是咱们须要广播到的子组件的时候调用$emit触发所需事件,在子组件中用$on监听*/
child.$emit.apply(child, [eventName].concat(params));
} else {
/*非所需子组件则递归遍历深层次子组件*/
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
name: 'my-page',
components: {
myMask,
myCon,
},
provide() {
return {
store: this.store
}
},
data() {
return {
store: {
state: {
type: 0
},
commit (type, value) {
this.state[type] = value
}
},
isShow: false,
title: '我是标题',
con: '我是内容',
hasSearch: false
};
},
methods: {
submit() {
window.alert('关闭弹层');
this.isShow = false;
},
changeCon() {
this.store.state.type ++;
if(this.store.state.type > 2) this.store.state.type = 0;
},
changInput(e) {
if(e.target.value) {
this.hasSearch = true;
}
this.broadcast('mySearch', 'on-input', e.target.value)
},
/*对多级父组件进行事件派发*/
dispatch(componentName, eventName, params) {
/*获取父组件,若是以及是根组件,则是$root*/
var parent = this.$parent || this.$root;
/*获取父节点的组件名*/
var name = parent.$options.name;
while (parent && (!name || name !== componentName)) {
/*当父组件不是所需组件时继续向上寻找*/
parent = parent.$parent;
if (parent) {
name = parent.$options.name;
}
}
/*找到所需组件后调用$emit触发当前事件*/
if (parent) {
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
},
mounted() {
this.$on('no-data', (value) => {
this.hasSearch = value;
})
}
}
复制代码
broadcast经过递归遍历子组件找到所需组件广播事件,而dispatch则逐级向上查找对应父组件派发事件。 broadcast(componentName:(组件名),eventName:(事件名称)以及params:(数据))。根据componentName深度遍历子组件找到对应组件emit事件eventName。 dispatch(componentName:(组件名),eventName:(事件名称)以及params:(数据))。根据componentName向上级一直寻找对应父组件,找到之后emit事件eventName。
经过这种方式进行数据、事件传递,必须给组件命名name;vuex