方式一:props、emit
props:作为数据传递的参数方式,作为子组件的属性,通过属性绑定接收来自父组件的数据
$emit:子组件向父组件传值(通过事件形式),子组件通过 $emit 事件向父组件发送消息,将自己的数据传递给父组件
//代码如下
<template>
<div>
<children-component :name="value" @click="handleClick"></children-component>
</div>
</template>
<script>
//子组件
const childrenComponent = {
props: {
name: {
type: String,
default: '',
require: true, //声明该属性,则props是必传参数,但一般不和default同时使用,一旦有默认值,这个参数设置的意义不大
}
},
template: `
<div style="padding: 30px" @click="handleClick">
{{name}}
</div>`,
data() {
return {
count: 0
}
},
methods: {
handleClick() {
this.count += 1;
this.$emit('click', this.count)
}
}
}
export default {
name: "sendData",
components: {
childrenComponent
},
data() {
return {
value: '我是父组件要传递给子组件的数据'
}
},
methods: {
handleClick(e) {
console.log(`打印子组件传递过来的数据:${e}`);
}
}
}
</script>
<style scoped>
</style>
方式二:on、emit
总线数据传递: 通过创建一个Vue实例,通过它来触发以及监听事件,实现任意组件之间的通信,包括父子组件、兄弟组件、隔代组件等
//方法1.通过props实现总线对象的传递
//方法2. 通过prototype进行总线对象的挂载,绑定到每个对象上(也可以直接访问$bus数据)
<template>
<div>
<children-one :bus-event="busEvent"></children-one>
<children-two :bus-event="busEvent"></children-two>
<children-third :bus-event="busEvent"></children-third>
</div>
</template>
<script>
//总线机制数据传递
import Vue from 'vue'
const busEvent = new Vue();
// Vue.prototype.$busEvent = busEvent;
//子组件
const childrenOne = {
props: {
busEvent: Object
},
template: `
<div style="padding: 30px">
我的名字是: {{name}}
<button @click="handleSend">点击我向组件三告知我的名字</button>
</div>`,
data() {
return {
name: '组件1'
}
},
methods: {
handleSend() {
this.$busEvent.$emit('child-one-msg', this.name);
}
}
}
//子组件2
const childrenTwo = {
props: {
busEvent: Object
},
template: `
<div style="padding: 30px">
我是子组件2,我的年龄是{{age}}岁
<button @click="handleSend">点击我向组件三告知我的年龄</button>
</div>`,
data() {
return {
age: 0
}
},
methods: {
handleSend() {
this.age +=1;
this.$busEvent.$emit('child-two-msg', this.age);
}
}
}
const childrenThird = {
props: {
busEvent: Object
},
template: `
<div style="padding: 30px">
我是组件三: 老大是{{childOneName}}、老二{{childTwoAge}}岁
</div>`,
data() {
return {
childOneName: '',
childTwoAge: ''
}
},
mounted() {
this.$busEvent.$on('child-one-msg', (name) => {
this.childOneName = name;
})
this.$busEvent.$on('child-two-msg', (age) => {
this.childTwoAge = age;
})
}
}
export default {
name: "sendData",
components: {
childrenOne,
childrenTwo,
childrenThird
},
data() {
return {
busEvent: busEvent
}
},
methods: {
handleClick(e) {
console.log(`打印子组件传递过来的数据:${e}`);
}
}
}
</script>
方式三: Vuex(全局仓库)
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
以一个单向数据流的方式,通过mutations来修改state的数据。最有根据state的数据变化,渲染页面。
//main.js
import Vue from 'vue'
import App from './App.vue'
import store from './store';
new Vue({
store,
render: h => h(App),
}).$mount('#app');
//store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex({
state: {
count: 1
},
getters: {
doubleCount: state => state.count * 2
},
mutations: { //同步数据修改
addCount(state) {
state.count++;
},
reduceCount(state) {
state.count--;
}
},
actions: {
async_Add(state) { //异步修改
this.commit('addCount')
},
async_Reduce(state) {
this.commit('reduceCount')
}
}
})
//子组件中
<template>
<div class="container">
<h3>测试Vuex数据传递</h3>
<p>我的count:{{count}}</p>
<p>doubleCount:{{doubleCount}}</p>
<button @click="add()">增加</button>
<button @click="reduce()">减少</button>
</div>
</template>
<script>
import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'
export default {
name: "vuexData",
computed: {
...mapState(['count']),
...mapGetters(['doubleCount'])
},
methods: {
...mapMutations(['addCount', 'reduceCount']),
...mapActions({
'add': 'async_Add',
'reduce': 'async_Reduce'
})
}
}
</script>
方式四:attrs、listeners (v2.4)
$attrs: 包含了父作用域中不作为Prop被识别(且获取)的特性绑定(class和style)除外,简而言之,pros的属性不存在$attrs中。
当一个组件没有声明任何 Prop 时,这里会包含所有父作用域的绑定 (Class 和 Style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用
$listeners: 包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
<template>
<div class="container">
<button style="background-color: lightgray" @click="reduce">减少</button>
<child-one :aa="aa" :bb="bb" :cc="cc" :dd="dd" @click="reduce" v-on:reduce="reduce"></child-one>
</div>
</template>
<script>
const childThird = {
props: ['dd'],
template: `
<div style="padding: 10px;background-color: orchid">
<p>childThird:{{$attrs}}</p>
</div>`,
}
const childTwo = {
props: ['bb'],
components: {childThird},
template: `
<div style="padding: 10px;background-color: orange">
<div>
<p>组件二bb:{{bb}}</p>
<p>childTwo的$attrs:{{$attrs}}</p>
<button @click="reduce2">组件2减dd数据</button>
</div>
<child-third v-bind="$attrs"></child-third>
</div>`,
methods: {
reduce2() {
this.$emit('reduce');
}
}
}
const childOne = {
props: ['aa'],
components: {
childTwo
},
template: `
<div style="padding: 10px;background-color: red">
<div>
<p>组件一aa:{{aa}}</p>
<p>childOne的$attrs:{{$attrs}}</p>
<button @click="reduce1">组件1减dd数据</button>
</div>
<child-two v-bind="$attrs" v-on="$listeners"></child-two>
</div>`,
methods: {
reduce1() {
this.$emit('reduce');
}
}
}
export default {
name: "listenData",
components: {
childOne
},
data() {
return {
aa: 1,
bb: 2,
cc: 3,
dd: 100
}
},
methods: {
reduce() {
this.dd--;
}
}
}
</script>
<style scoped>
</style>
方式五 Provide、inject (v2.2)
Provide和inject需要同时使用,以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在其上下游关系成立的时间里始终生效。
简单来说,就是父组件通过 Provider 传入变量,任意子孙组件通过 Inject 来拿到变量
provide需要是一个函数,因为provide初始化的时候,对象data还没有初始化成功,所以需要是一个函数,本身不支持响应式数据,所以如果使用到data里面的数据的时候,需要用 Object.defineProperties关联绑定
<template>
<div>
<input type="text" v-model="value">
<child-one></child-one>
</div>
</template>
<script>
const childOne = {
template: `<div>this is childcomponent: {{data.value}}</div>`,
inject: ['data'],
mounted() {
console.log(this.data.value);
}
}
export default {
name: "provideData",
components: {
childOne
},
data() {
return {
value: '我是父组件数据'
}
},
provide() { //provide需要是一个函数,因为provide初始化的时候,对象data还没有初始化成功,所以需要是一个函数,
// 本身不支持响应式数据,所以如果使用到data里面的数据的时候,需要用 Object.defineProperties关联绑定
const data = {};
Object.defineProperties(data, {
value: {
get: () => this.value,
set: (v) => {
console.log(v)
},
enumerable: true
}
})
return {
data
}
}
}
</script>
<style scoped>
</style>
方式六:parent、children、ref
1. $parent / $children:指定已创建的实例之父实例,在两者之间建立父子关系。子实例可以用 this.$parent 访问父实例,子实例被推入父实例的 $children 数组中。
2. $refs 一个对象,持有注册过 ref 特性 的所有 DOM 元素和组件实例。ref 被用来给元素或子组件注册引用信息。引用信息将会注册在父组件的 $refs 对象上。如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素;如果用在子组件上,引用就指向组件。
<template>
<div class="container">
<p>我的title:{{title}}</p>
<p>我的name:{{name}}</p>
<child-one ref="comp1"></child-one>
<child-two ref="comp2"></child-two>
</div>
</template>
<script>
const childOne = {
template: `
<div>我的父组件是谁: {{content}}</div>`,
data() {
return {
title: '我是子组件1',
content: ''
}
},
mounted() {
this.content = this.$parent.content;
},
methods: {
sayHello() {
window.alert('hello')
}
}
}
const childTwo = {
template: `
<div>我是子组件2</div>`,
data() {
return {
title:'我是子组件2'
}
}
}
export default {
name: "parentData",
components: {
childOne,
childTwo
},
data() {
return {
title: '',
name: '',
content: '我就是父组件'
}
},
mounted() {
const comp1 = this.$refs.comp1;
this.title = comp1.title;
comp1.sayHello();
this.name = this.$children[1].title;
}
}
</script>
总结
组件间不同的使用场景可以分为3类,对应的通信方式如下:
1. 父子通信: Props / $emit,$emit / $on,Vuex,$attrs / $listeners,provide/inject,$parent / $children&$refs
2. 兄弟通信:$emit / $on,Vuex
3. 隔代通信:$emit / $on,Vuex,provide / inject,$attrs / $listeners