Appearance
七种组件通信
- props/$emit
- $parent/$children
- provide/inject
- ref
- eventBus
- Vuex
- $attrs/$listeners
一、props/$emit
父组件通过v-bind把值传给子组件,子组件通过props接收。子组件通过$emit触发父组件的自定义事件,父组件通过v-on监听子组件的自定义事件。
父组件:
vue
<template>
<div>
<children :list='list' @onEmit='onEmit'></children>
<div>{{a}}</div>
</div>
</template>
<script>
import children from './children'
export default {
components:{
children,
},
data(){
return{
list:[{id:1,name:'斗破'},{id:2,name:'圣墟'},{id:3,name:'牧神记'}],
a:-1,
}
},
methods:{
onEmit(val){
console.log(val)
this.a = val;
}
}
}
</script>子组件:
vue
<template>
<div>
<div v-for="(item,index) in list" :key="index" @click="emitClick(index)">{{item}}</div>
</div>
</template>
<script>
export default {
props:['list'],
data(){
return{
}
},
methods:{
emitClick(index){
console.log(11,index)
this.$emit('onEmit',index)
}
}
}
</script>二、$parent/$children
父组件通过$parent获取子组件,子组件通过$children获取父组件。
父组件:
vue
<template>
<div style="margin-top:100px">
<children></children>
<div>{{ message }}</div>
<button @click="btn">点击获取子组件的值</button>
</div>
</template>
<script>
import children from './children'
export default {
components:{
children,
},
data(){
return{
message:'来至父组件的消息'
}
},
methods:{
btn(){
this.$children[0].message = this.message
}
}
}
</script>子组件:
vue
<template>
<div>
<div>{{message}}</div>
<div>获取父组件的值:{{parentM}}</div>
</div>
</template>
<script>
export default {
props:['list'],
data(){
return{
message:'来至子组件的消息',
}
},
computed:{
parentM(){
return this.$parent.message
}
},
methods:{
}
}
</script>总结:以上只能在父子组件中传值,不能隔代传值,不能同级传值。
三、provide/ inject
provide和inject是vue2.0新增的api。父组件通过provide来进行定义变量,子组件通过inject来注入。不论子组件嵌套有多深, 只要调用了inject 那么就可以注入provide中的数据 代码示例: 存在三个vue文件,分别死a,b,c。b是a的子组件,c是b的子组件。
a组件:
vue
<template>
<div>
<b></b>
</div>
</template>
<script>
import b from './b'
export default {
components:{
b,
},
provide:{
message:'来至最大的消息'
},
}
</script>b组件:
vue
<template>
<div>
<div>b:{{messageB}}</div>
<c></c>
</div>
</template>
<script>
import c from './c'
export default {
inject:['message'],
data(){
return{
messageB:this.message,
}
},
components:{
c
}
}
</script>c组件:
vue
<template>
<div>
c:{{messageC}}
</div>
</template>
<script>
export default {
inject:['message'],
data(){
return{
messageC:this.message
}
}
}
</script>四、ref
ref如果在dom元素上使用,指向的就是dom元素。在子组件上使用,指向的就是组件实例。可以访问数据或者方法。
父组件通过ref声明子组件标识,通过this.$refs.子组件标识名来获取子组件实例。父组件引用子组件,如果子组件在父组件的循环中,父组件调用子组件方法时,需要用this.refs.子组件[0].方法, 进行调用方法 示例:
js
// 组件使用
<Upload :ref="`fileUpload${index}`" @findFileUrl="findFileUrl" :urlIndex="`${index}`" urlType="file"></Upload>
// 组件调用
this.$refs[`fileUpload${index}`][0].submitUpload()
// 模板使用
<div v-for="(item, index) in list" :key="index">
<div v-for="(value, key) in item.mediaMaterialList" :key="key" :ref="`list${index}`">
<div v-if="item.type == 'image'" :ref="`list1${key}`">
</div>
</div>
</div>
// js获取
this.$refs[`list${index}`][key].scrollIntoView()五、eventBus
eventBus中央事件总线,所有组件的事件中心,组件可以向中心注册发送事件和接收事件,组件可以和其他任何组件通信。通过bus.emit触发事件,bus.on监听触发的事件
代码示例: 需要新建一个事件总线并且导出, 新建js文件eventBus.js文件
js
import Vue from 'vue'
export default EventBus = new Vue()vue
<template>
<div style="margin-top:100px">
<b></b>
<c></c>
</div>
</template>
<script>
import b from './b'
import c from './c'
export default {
components:{
b,
c
},
}
</script>分别有两个组件b和c,b和c为兄弟组件。在b组件中发送事件,在c组件中接收。
vue
<template>
<div>
<button @click="clickB">b组件的点击事件{{num}}</button>
</div>
</template>
<script>
import {EventBus} from './eventBus.js'
console.log(EventBus)
export default {
data(){
return{
num:1,
}
},
methods:{
clickB(){
EventBus.$emit('addition',{
num:this.num++
})
}
}
}
</script>vue
<template>
<div>
c:{{count}}
</div>
</template>
<script>
import {EventBus} from './eventBus.js'
export default {
data(){
return{
count:0,
}
},
mounted(){
EventBus.$on('addition',param => {
this.count = this.count + param.num;
})
}
}
</script>移除事件监听:
js
import { eventBus } from 'event-bus.js'
EventBus.$off('addition', {})六、vuex
1.vuex模块介绍
- state:存储数据
- mutations:修改state中的数据,不能处理异步事件。
- actions:提交mutation,而不直接更状态,可以包含异步操作
- getters:计算state中的数据,像vue中的计算属性一样,基于state数据的二次包装,常用于数据的筛选和多个数据想关计算。
- modules:模块,用于将项目中各个模块的状态分开定义和操作,便于维护。
2.vuex创建
在main.js同级下新建store文件夹,中新建index.js,actions.js,getters.js,mutations.js,state.js文件
在main.js中引入: import store from './store'
在实例中添加store:
js
new Vue({
el: '#app',
router,
store,
components: { App },
template: ''
})index.js文件:
js
import Vue from "vue";
import Vuex from "vuex";
import state from "./state";
import mutations from "./mutations";
import * as actions from "./actions";
import * as getters from "./getters";
Vue.use(Vuex);
export default new Vuex.Store({
state,
mutations,
actions,
getters,
});state.js文件:
js
const state = {
token: "",
}
export default statemutations.js文件:
js
const updateToken = (state, token) => {
state.token = token;
}
export default { token }actions.js文件:
js
// context对象会自动传入,它与store实例具有相同的方法和属性
const updateToken = (context, token) => {
// 1. 发异步请求, 请求数据
// 2. commit调用mutation来修改/保存数据
// context.commit('mutation名', 载荷)
context.commit('updateToken', token)
}getters.js文件:
js
export const getToken = (state) => state.token3.使用
js
import { mapActions. mapState } from 'vuex'
// 2中方式
// 方法一
this.$store.dispatch('updateToken', '1212312')
// 方法二
// 通过methods方法添加映射
methods: {
...mapActions([
'updateToken'
])
}
// 2.使用
this.updateToken()
// 获取数据
computed: {
...mapState([
'token'
])
// 推荐写法
...mapState({
token: state => state.token,
test: test => state.test
})
}七、$attrs/$listeners
vue2.4中引用了$attrs和$listeners,新增了inheritAttrs选项。inheritAttrs:默认为true。true的时候会把没有在props中声明的属性挂载到组件的根元素上。listeners:官方文档解释:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="listeners" 传入内部组件——在创建更高层次的组件时非常有用。即子组件可以触发父组件的事件。
内部组件通过this.emit('方法名')去调用。 attrs:存放没有被子组件继承的的数据对象 父组件:
vue
<template>
<div style="margin-top:80px">
<test2 :name='name' :age='age' :gender='gender'
:height='height' :width='width' @event1='ev1' @event2='ev2'></test2>
</div>
</template>
<script>
import test2 from './test2'
export default {
data(){
return{
name:'sun',
age:'18',
gender:'男',
height:'182',
width:'1222',
}
},
components:{
test2,
},
methods:{
ev1(){
console.log('触发了方法1')
},
ev2(){
console.log('触发了方法2')
}
}
}
</script>子组件:
vue
<template>
<div>
name{{name}}
<div>子组件一的$attrs{{$attrs}}</div>
<test3 v-bind="$attrs" v-on="$listeners"></test3>
</div>
</template>
<script>
import test3 from './test3'
export default {
props:{
name:String,//可以不写,直接冲attrs取
},
inheritAttrs: false,//关闭自动挂载在组件根元素上,没有在props中声明的属性
components:{
test3
},
created(){
console.log('组件1'+this.$attrs)
},
mounted(){
this.$emit('event1')
}
}
</script>test3代码:
vue
<template>
<div>
<!-- age{{age}} -->
<div>子组件二的$attrs{{$attrs}}</div>
</div>
</template>
<script>
export default {
// props:{
// age:String,
// },
inheritAttrs: false,
created(){
console.log('组件2'+this.$attrs)
},
mounted(){
this.$emit('event2')
}
}
</script>总结:
- 父子组件通信:props; $parent / $children; provide / inject ; ref ; $attrs / $listeners
- 兄弟组件通信:eventBus,vuex
- 跨级通信:eventBus;Vuex;provide / inject 、$attrs / $listeners