vue组件通信分为横向和纵向。
**纵向**
1. props 和 $emit
props:接收来自父组件的数据
$emit:触发事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
childToParentData: 'I am the data of Parent!',
};
},
template: `<div>
<p> I am the parent component!</p>
<Child :childData="msg" @childHandler="handerFn"/>
<p>childToParentData: {{childToParentData}}</p>
</div>`,
methods: {
handerFn(val){
console.log(val);
this.childToParentData = val;
},
}
})
Vue.component('Child', {
data(){
return {
msg: 'I am the data of Child!',
inputVal: this.childData,
};
},
props: ['childData'],
template: `<div>
<p> I am the child component!</p>
<p>{{msg}}</p>
<input v-model="inputVal" @input="changeVal(inputVal)" />
</div>`,
methods: {
changeVal(val){
// 通过 $emit 触发,参数为 事件名,参数
this.$emit('childHandler', val);
}
}
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
<pre>
子组件向父组件传值
1、自定义事件
2、子组件原生事件
3、原生事件的处理函数中通过$emit触发自定义事件
注:子组件不能修改props中的值,否则报错,可通过子组件自己的数据接收props中的值来解决
</pre>
</body>
</html>
2. $parent 和 $children
后代组件可以通过$parent.$parent.$parent这种形式跨级通信
父组件可以通过$children[0].$children[0]这种形式跨级通信,如果有多个子组件,索引不好控制
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
grandToParent: '',
};
},
template: `<div>
<p> I am the parent component!</p>
<input v-model="msg" @input="consoleFn"/>
<p style="color: red;">{{grandToParent}}</p>
<hr />
<Child/>
</div>`,
methods: {
consoleFn(){
this.$children[0].parentMsg = this.msg;
},
},
})
Vue.component('Child', {
data(){
return {
msg: 'I am the data of Child!',
parentMsg: '',
grandToChild: '',
};
},
template: `<div>
<p> I am the child component!</p>
<p>{{msg}}</p>
<button @click="consoleFn">attr</button>
<p style="color: red;">{{parentMsg}}</p>
<p style="color: red;">{{grandToChild}}</p>
<hr />
<GrandChild />
</div>`,
methods: {
consoleFn(){
this.$children[0].childMsg = this.msg;
},
},
})
Vue.component('GrandChild', {
data(){
return {
msg: 'I am the data of GrandChild!',
childMsg: '',
};
},
template: `<div>
<p> I am the GrandChild component!</p>
<p>{{msg}}</p>
<button @click="consoleFn" >attr</button>
<p style="color: red;">{{childMsg}}</p>
<hr />
</div>`,
methods: {
consoleFn(){
this.$parent.grandToChild = this.msg;
this.$parent.$parent.grandToParent = this.msg;
},
},
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
<pre>
$parent, $children, $root, $parent.$parent
非响应式,如果有多个直接子组件
</pre>
</body>
</html>
3. $attrs 和 $listeners
后代组件从$attrs获取父组件传给后代组件的数据
后代组件通过$emit触发$listeners的事件将数据传给父组件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
childToParentData: 'I am the data of Parent!',
};
},
template: `<div>
<p> I am the parent component!</p>
<p>childToParentData: {{childToParentData}}</p>
<button @click="consoleFn">attr</button>
<br />
<Child :parentMsg="msg" @childClick="parentHandler" v-bind="$attrs" v-on="$listeners"/>
</div>`,
// inheritAttr: false,
methods: {
consoleFn(){
console.log(this.$attrs); // {}
console.log(this.$listeners); // {}
},
parentHandler(data){
console.log(data);
},
},
})
Vue.component('Child', {
data(){
return {
msg: 'I am the data of Child!',
};
},
template: `<div>
<p> I am the child component!</p>
<p>{{msg}}</p>
<button @click="consoleFn">attr</button>
<br />
<GrandChild :childMsg="msg" @grandChildClick="childHandler" v-bind="$attrs" v-on="$listeners"/>
</div>`,
// inheritAttr: false,
methods: {
consoleFn(){
console.log(this.$attrs); // {parentMsg: }
console.log(this.$listeners); //{childClick: }
},
childHandler(data){
console.log(data);
},
},
})
Vue.component('GrandChild', {
data(){
return {
msg: 'I am the data of GrandChild!',
};
},
template: `<div>
<p> I am the GrandChild component!</p>
<p>{{msg}}</p>
<button @click="consoleFn" >attr</button>
<br />
</div>`,
// inheritAttr: false,
methods: {
consoleFn(){
console.log(this.$attrs); // {parentMsg: , childMsg: }
console.log(this.$listeners); //{childClick: , grandChildClick: }
this.$emit('childClick', this.msg); // 如果不收集,$emit只能触发其父级的事件
this.$emit('grandChildClick', this.msg);
},
},
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
<pre>
1、$attrs收集属性
2、$listeners收集事件
</pre>
</body>
</html>
4. provide 和 inject
父组件向后代组件单向传递数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
};
},
provide(){
return {
parentMsg: this.msg,
};
},
template: `<div>
<p> I am the parent component!</p>
<input v-model="msg"/> <!-- provide和inject绑定并不是可响应的, 所以msg的变化不会影响后代组件中已经接收到的msg的值 -->
<button @click="consoleFn">attr</button>
<hr />
<Child/>
</div>`,
methods: {
consoleFn(){
console.log(this);
},
},
})
Vue.component('Child', {
data(){
return {
msg: 'I am the data of Child!',
};
},
provide: {
childMsg: 'I am the data of Child!',
},
inject: ['parentMsg'],
template: `<div>
<p> I am the child component!</p>
<p>{{msg}}</p>
<button @click="consoleFn">attr</button>
<p style="color: red;">{{parentMsg}}</p>
<hr />
<GrandChild />
</div>`,
methods: {
consoleFn(){
console.log(this);
},
},
})
Vue.component('GrandChild', {
data(){
return {
msg: 'I am the data of GrandChild!',
};
},
inject: ['parentMsg', 'childMsg'],
template: `<div>
<p> I am the GrandChild component!</p>
<p>{{msg}}</p>
<button @click="consoleFn" >attr</button>
<p style="color: red;">{{parentMsg}}</p>
<p style="color: red;">{{childMsg}}</p>
<hr />
</div>`,
methods: {
consoleFn(){
console.log(this);
},
},
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
<pre>
单向
</pre>
</body>
</html>
**横向**
1. 数据总线
用一个中间变量保存数据
var bus = new Vue()
$on绑定事件
$emit触发事件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
var bus = new Vue();
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
};
},
template: `<div>
<p> I am the parent component!</p>
<button @click="consoleFn">button</button>
<br />
<BrotherOne />
<BrotherTwo />
</div>`,
methods: {
consoleFn(){
console.log(bus);
},
},
})
Vue.component('BrotherOne', {
data(){
return {
msg: 'I am the data of BrotherOne!',
fromBrother: '',
};
},
template: `<div>
<p> I am the BrotherOne component!</p>
<p>{{msg}}</p>
<input v-model="fromBrother" @input="transformData"/>
<br />
</div>`,
methods: {
transformData(){
bus.$emit('globalBus', this.fromBrother);
},
},
})
Vue.component('BrotherTwo', {
data(){
return {
msg: 'I am the data of BrotherTwo!',
fromBrother: '',
};
},
template: `<div>
<p> I am the BrotherTwo component!</p>
<p>{{msg}}</p>
<p>fromBrother: {{fromBrother}}</p>
<button @click="consoleFn" >button</button>
<br />
</div>`,
methods: {
consoleFn(){
console.log(bus);
},
},
mounted(){
bus.$on('globalBus', val => {
this.fromBrother = val;
})
}
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
</body>
</html>
var bus = {}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div id="app">
</div>
<script src="./node_modules/vue/dist/vue.js"></script>
<script>
var bus = {};
// 全局组件
Vue.component('Parent', {
data(){
return {
msg: 'I am the data of Parent!',
};
},
template: `<div>
<p> I am the parent component!</p>
<button @click="consoleFn">button</button>
<br />
<BrotherOne />
<BrotherTwo />
</div>`,
methods: {
consoleFn(){
console.log(bus);
},
},
})
Vue.component('BrotherOne', {
data(){
return {
msg: 'I am the data of BrotherOne!',
fromBrother: '',
};
},
template: `<div>
<p> I am the BrotherOne component!</p>
<p>{{msg}}</p>
<input v-model="fromBrother" @input="transformData"/>
<br />
</div>`,
methods: {
transformData(){
bus['brotherOne'] = {
'fromBrother': this.fromBrother,
};
},
},
})
Vue.component('BrotherTwo', {
data(){
return {
msg: 'I am the data of BrotherTwo!',
fromBrother: '',
};
},
template: `<div>
<p> I am the BrotherTwo component!</p>
<p>{{msg}}</p>
<p>fromBrother: {{fromBrother}}</p>
<button @click="consoleFn" >button</button>
<br />
</div>`,
methods: {
consoleFn(){
this.fromBrother = bus['brotherOne']['fromBrother'];
},
},
})
var App = {
template: `<div>
<Parent />
</div>`,
};
var vm = new Vue({
el: '#app',
data(){
return {
};
},
components: {
App
},
methods: {
},
template: `
<App />
`
})
</script>
</body>
</html>