vue组件通信
组件通信:组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用,而组件间相互传递数据,就是组件通信
vue组件通信方案
1.父传子 2.子传父 3.兄弟组件 4.任何组件通信(bus中央事件总线)
5.vuex插件
父传子
父组件像子组件传值,子组件接收值是使用props处理的
第一步:创建子组件和父组件
子组件 bagSon.vue 父组件 father.vue
第二步: 配置子组件和父组件的路由
第三步:在父组件里面引入子组件
import bagSon from '@/components/bagSon'
第四步:在父组件里面注册子组件
//注册组件
components:{
bagSon
},
第五步:在父组件里面使用注册的子组件,也就是使用子组件标签
<bagSon></bagSon>
第六步 :把父组件的数据绑定到子组件的注册的子组件标签里面
<bagSon :msg="msg" :msgArray="msgArray"></bagSon>
第七步:在子组件标签里面使用propos接收父组件的传过来的值
props : [“msg”,“msgArray”]
在子组件里面接收父组件的传值有两种方式,上面的是第一种数组的方式,还有一种对象的方式,例如下面的这种方式
//第二种书写的方式 对象书写方式
props :{
msg :{
//设置传入类型
type : String,
//如果不传值的话,会显示默认设置的值
default : "默认消息"
},
msgArray :{
//传值的类型
type :Array,
//如果不传值的话,会显示默认设置的值
default :[
{
id : "1",
msg : "成绩单拿出来我看看!!!!!!"
}
]
}
}
完整代码
父组件 father.vue
<template>
<div class = "father">
<h1>我是老子!!!!!!</h1>
<!-- 使用组件 :msg="msg" 等同于v-bind:msg="msg" 给子组件绑定父组件的数据
第一个msg代表着我们传入的数据起了一个名字,第二msg代表父组件里面传入的数据的名字,在子组件使用的
时第一个msg -->
<bagSon :msg="msg" :msgArray="msgArray"></bagSon>
</div>
</template>
<script>
//引入组件
import bagSon from '@/components/bagSon'
export default {
//注册组件
components:{
bagSon
},
data(){
return {
msg :"儿子,我是你爸爸!!!!!!",
msgArray :[
{
id : "1",
msg : "数学考了多少?"
},{
id : "2",
msg : "语文考了多少?"
},{
id : "3",
msg : "英语考了多少?"
}
]
}
}
}
</script>
<style scoped>
.father{
height: 1000px;
border: blue solid 1px;
}
</style>
子组件bagSon.vue代码
<template>
<div class="bagSon">
<h1>我是老大尼古拉斯赵四!!!!</h1>
<h1>{{msg}}</h1>
<ul>
<li v-for = "item in msgArray" :key ="item.id">
{{item.id}} ============={{item.msg}}
</li>
</ul>
</div>
</template>
<script>
export default {
//第一种接收的书写方式,数组的方式
//props : ["msg","msgArray"]
//第二种书写的方式 对象书写方式
props :{
msg :{
//设置传入类型
type : String,
//如果不传值的话,会显示默认设置的值
default : "默认消息"
},
msgArray :{
//传值的类型
type :Array,
//如果不传值的话,会显示默认设置的值
default :[
{
id : "1",
msg : "成绩单拿出来我看看!!!!!!"
}
]
}
}
}
</script>
<style scoped>
.bagSon{
height: 300px;
border:red solid 1px;
}
</style>
子传父
第一步:在子组件里面发送数据到父组件
//第一个参数是自定义的事件类型 第二个参数是需要发送的数据
this.$emit("toSendFather",this.toFatherMsg)
第二步:在父组件里面的子组件标签里面绑定子组件的自定义事件,并且给事件定义一个方法
<!-- 接收儿子传过来的数据,需要绑定儿子的自定义事件toSendFather getSonMsg方法 -->
<bagSon :msg="msg" :msgArray="msgArray" @toSendFather="getSonMsg" ></bagSon>
第三步:写方法 处理数据
methods:{
getSonMsg(sonMsg){
this.sonMsg = sonMsg;
}
}
全部代码
子组件 bagSon.vue
<template>
<div class="bagSon">
<h1>我是老大尼古拉斯赵四!!!!</h1>
<h1>{{msg}}</h1>
<ul>
<li v-for = "item in msgArray" :key ="item.id">
{{item.id}} ============={{item.msg}}
</li>
</ul>
<button @click = "toSendFather">给父组件发送数据</button>
</div>
</template>
<script>
export default {
//第一种接收的书写方式,数组的方式
//props : ["msg","msgArray"]
//第二种书写的方式 对象书写方式
props :{
msg :{
//设置传入类型
type : String,
//如果不传值的话,会显示默认设置的值
default : "默认消息"
},
msgArray :{
//传值的类型
type :Array,
//如果不传值的话,会显示默认设置的值
default :[
{
id : "1",
msg : "成绩单拿出来我看看!!!!!!"
}
]
}
},
data(){
return {
toFatherMsg : "爸,全是满分!!!!!!!!!!"
}
},
methods:{
toSendFather(){
//第一个参数是自定义的事件类型 第二个参数是需要发送的数据
this.$emit("toSendFather",this.toFatherMsg)
}
}
}
</script>
<style scoped>
.bagSon{
height: 300px;
border:red solid 1px;
}
</style>
父组件 father.vue
<template>
<div class = "father">
<h1>我是老子!!!!!!</h1>
<!-- 使用组件 :msg="msg" 等同于v-bind:msg="msg" 给子组件绑定父组件的数据
第一个msg代表着我们传入的数据起了一个名字,第二msg代表父组件里面传入的数据的名字,在子组件使用的
时第一个msg -->
<!-- 接收儿子传过来的数据,需要绑定儿子的自定义事件toSendFather getSonMsg方法 -->
<bagSon :msg="msg" :msgArray="msgArray" @toSendFather="getSonMsg" ></bagSon>
<h1>{{sonMsg}}</h1>
</div>
</template>
<script>
//引入组件
import bagSon from '@/components/bagSon'
export default {
//注册组件
components:{
bagSon
},
data(){
return {
msg :"儿子,我是你爸爸!!!!!!",
msgArray :[
{
id : "1",
msg : "数学考了多少?"
},{
id : "2",
msg : "语文考了多少?"
},{
id : "3",
msg : "英语考了多少?"
}
],
sonMsg : ""
}
},
methods:{
getSonMsg(sonMsg){
this.sonMsg = sonMsg;
}
}
}
</script>
<style scoped>
.father{
height: 1000px;
border: blue solid 1px;
}
</style>
兄弟组件
兄弟组件之间通信,在vue里面,兄弟组件指的是拥有同一个父亲的组件,一个兄弟把数据传递到父组件,然后这个父组件在传递给另外一个子组件
bus中央事件总线
公共事件总线eventBus的实质就是创建一个vue实例,通过一个空的vue实例作为桥梁实现vue组件间的通信。它是实现非父子组件通信的一种解决方案。
第一步:创建爷爷father_bus.vue,大儿子bagSon_bus.vue,大儿子的儿子bagSon_son_bus.vue,小儿子smallSon_bus.vue,小儿子的儿子smallSon_son_bus.vue,现在我们要两个孙在之间传递数据的话
第二步,创建一个vue实例,并且把这个实例挂在vue原型上
main.js里面的
//创建一个新的vue实例
let bus = new Vue();
//把创建的这个vue实例挂在vue的原型上
//只要挂在vue原型上,在任何的vue组件上,通过this就可以获取bus
Vue.prototype.bus = bus;
第三步:传递数据
//第一个参数自定义事件fnSendMsg
//第二个参数传递的数据
this.bus.$emit("fnSendMsg",this.msg)
第四步:
接收数据
//第一个参数fnSendMsg自定义事件
//第二个参数 方法对象
this.bus.$on("fnSendMsg",msg =>{
this.msg = msg
})
完整代码
father_bus.vue`
<template>
<div class="bagSon_bus">
<h1>我是大儿子</h1>
<bagSon_son_bus />
</div>
</template>
<script>
import bagSon_son_bus from "../busVue/bagSon_son_bus";
export default {
components: {
bagSon_son_bus
}
};
</script>
<style scoped>
.bagSon_bus {
border: seagreen 2px solid;
height: 300px;
}
</style>
bagSon_son_bus.vue
<template>
<div class= "bagSon_son_bus">
<h1>我是大儿子的儿子</h1>
<h1>传递过来的数据{{msg}}</h1>
</div>
</template>
<script>
export default {
data(){
return{
msg:""
}
},
created(){
//第一个参数fnSendMsg自定义事件
//第二个参数 方法对象
this.bus.$on("fnSendMsg",msg =>{
this.msg = msg
})
}
}
</script>
<style scoped>
.bagSon_son_bus{
border: rgb(105, 46, 139) 2px solid;
height: 150px;
}
</style>
smallSon_bus.vue
<template>
<div class="smallSon_bus">
<h1>我是小儿子</h1>
<smallSon_son_bus/>
</div>
</template>
<script>
import smallSon_son_bus from "../busVue/smallSon_son_bus";
export default {
components:{
smallSon_son_bus
}
}
</script>
<style scoped>
.smallSon_bus{
border: solid 2px coral;
height: 300px;
}
</style>
smallSon_son_bus.vue
<template>
<div class="smallSon_son_bus">
<h1>我是小儿子的儿子</h1>
<button @click="fnSendMsg">传递数据</button>
</div>
</template>
<script>
export default {
data(){
return {
msg : "我是小儿子的儿子,我想对大儿子的儿子说:哥哥,我是通过bus传递的!!!!!"
};
},
methods :{
fnSendMsg(){
//第一个参数自定义事件fnSendMsg
//第二个参数传递的数据
this.bus.$emit("fnSendMsg",this.msg)
}
}
}
</script>
<style scoped>
.smallSon_son_bus{
border: solid 2px rgb(144, 255, 80);
height: 300px;
}
</style>
vuex
vuex主要应用于vue里面管理数据状态的一个库,通过创建一个集中的数据存储,供应用中的所有组件使用。
vuex包含state,getter,mutation,action,module五个核心概念 ,5个api
state状态
state状态 组件中需要共享和使用的数据
使用state的数据
第一步:安装
npm install vuex --save
第二步:
在src下新建store文件夹,文件夹下新建一个index.js
在index.js里面初始化vuex
//引入vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//注册vuex
Vue.use(Vuex);
//创建仓库,并把仓库暴漏出去
export default new Vuex.Store({
//state状态 组件中需要共享和使用的数据
state: {
num: 110
},
//Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。
//就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters: {
},
//更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:
//每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
//这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
mutations: {
},
//Action 类似于 mutation,不同在于:
//Action 提交的是 mutation,而不是直接变更状态。
//Action 可以包含任意异步操作。
actions: {
},
//由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
//当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
//为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
//每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
modules: {
}
})
第三步:把仓库传入vue实例对象里面渲染
main.js里面 引入仓库
import store from './store' //引入仓库
把仓库传入vue实例对象里面渲染
new Vue({ //创建vue实例
el: '#app', //挂载dom
router, //传入路由渲染
store, //把仓库传入vue实例对象里面渲染
components: { App }, //传入顶级组件渲染
template: '<App/>'
})
第四步:新建一个vue组件use_vuex_state并配置路由
因为已经在把仓库注册到vue实例对象里面渲染了,所以使用
this.$store就可以获取到state,从而可以获取state里面的对象数据
this.$store.state.num
完整的代码
<template>
<div>
<h1>使用vuex的state</h1>
<h1>{{num}}</h1>
</div>
</template>
<script>
export default {
//计算属性
computed:{
num(){
return this.$store.state.num;
}
}
}
</script>
<style scoped>
</style>
其实vuex给我们提供了一个辅助函数,专门用来获取state里面的数据
在需要获取state的页面里面引入辅助函数
//引入vuex的辅助函数
import {mapState} from 'vuex'
使用
//计算属性
computed:{
// num(){
// return this.$store.state.num;
// }
//直接通过state里面定义的名字就可以获取到,如果多个,那就在数组里面多谢几个,用逗号隔开
...mapState(['num'])
}
上面这种使用是数组用法,下面的这个是对象用法
//计算属性
computed:{
// num(){
// return this.$store.state.num;
// }
//直接通过state里面定义的名字就可以获取到,如果多个,那就在数组里面多谢几个,用逗号隔开
//...mapState(['num'])数组写法
...mapState({//对象写法 第一个参数是你自定义的对象名字,第二个参数是state里面的对象名字
num : "num"
})
}
getters仓库里面的计算属性
如果我有一份学生的单,放在了state里面了,但是我现在只需要成绩高于90份的,如果不使用getters的话,我需要先取出来,在页面里面判断后,获取高于90分的学生。
现在有了getters,我可以直接在getters里面计算完,直接取出来就可以了,就算多个组件都需要这份数据,我也只需要在getters里面判断一次就可以了。
//引入vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//注册vuex
Vue.use(Vuex);
//创建仓库,并把仓库暴漏出去
export default new Vuex.Store({
//state状态 组件中需要共享和使用的数据
state: {
num: 110,
users: [{
id: "1",
name: "张三",
scores: 99
}, {
id: "2",
name: "李四",
scores: 98
}, {
id: "3",
name: "王五",
scores: 97
}, {
id: "4",
name: "赵六",
scores: 59
}]
},
//Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。
//就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters: {
//传入的参数是整个state的数据
toGetUserDone(state) {
return state.users.filter(v => (v.scores > 90));
}
},
//更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:
//每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
//这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
mutations: {
},
//Action 类似于 mutation,不同在于:
//Action 提交的是 mutation,而不是直接变更状态。
//Action 可以包含任意异步操作。
actions: {
},
//由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
//当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
//为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
//每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
modules: {
}
})
<template>
<div>
<h1>使用vuex的getters</h1>
<ul>
<li v-for="item in users" :key="item.id">{{item.name}}</li>
</ul>
</div>
</template>
<script>
//引入vuex的辅助函数
import { mapGetters } from "vuex";
export default {
//计算属性
computed: {
// users(){ 直接获取
// return this.$store.getters.toGetUserDone;
// }
//使用辅助对象mapGetters 对象获取方式
...mapGetters({
users: "toGetUserDone"
})
}
};
</script>
<style scoped>
</style>
mutation唯一改变state的方式(同步)
当我们需要修改 仓库里面的数据时,就要用到mutation。
第一步 在store文件夹下的index里面的mutations里面定义对state修改的方法
方法里面存在两个参数,第一个参数是vuex里面定义的state,第二个参数是传进来的值
//更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:
//每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
//这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数
mutations: {
updateNum(state, payload) {
this.state.num += payload.num;
},
},
第二步创建一个vue组件,并配置路由
<template>
<div>
<h1>使用vuex的mutation</h1>
<h1>{{num}}</h1>
<button @click="fnAdd(num)">num++</button>
<button @click="fnDel(num)"> num--</button>
<button @click="updateNum({num : 10})">辅助函数数组方式num++</button>
<button @click="updateNum({num : -10})">辅助函数数组方式num--</button>
<button @click="fnAddObj({num : 10})">辅助函数对象方式num++</button>
<button @click="fnDelObj({num : -10})">辅助函数对象方式num--</button>
</div>
</template>
<script>
//引入vuex的辅助函数
import { mapState ,mapMutations} from "vuex";
export default {
//计算属性
computed: {
...mapState({
num : "num"
})
},
methods:{
fnAdd(num){
//第一种方式
//this.$store.commit("updateNum",{num:10});
//第二种方式,对象方式
this.$store.commit({type : "updateNum",num:10});
},
fnDel(num){
this.$store.commit("updateNum",{num:-10});
},
//辅助函数数组方式
...mapMutations(
["updateNum"]
),
//辅助函数对象方式
...mapMutations({
fnAddObj : "updateNum",
fnDelObj : "updateNum"
})
}
};
</script>
<style scoped>
</style>
第三步引入vuex的辅助函数
//引入vuex的辅助函数
import { mapState ,mapMutations} from "vuex";
第四步在计算属性里面获取需要修改的数据
//计算属性
computed: {
...mapState({
num : "num"
})
},
第五步修改数据
修改数据的第一种方式
第一个参数是在vuex里面的mutation定义的方法名字,第二个参数是修改的参数值,是一个对象,对象里面可以有多个属性
//第一种方式
this.$store.commit("updateNum",{num:10});
第二种方式 第一个参数的key是type,固定写法,参数值是mutations里面定义的方法名,第二个参数是修改的参数,可以多个,用逗号隔开
//第二种方式,对象方式
this.$store.commit({type : "updateNum",num:10});
第三种方式,辅助对象数组方式
这个只有一个参数,这个参数就是mutations的方法名字,并且调用的时候也必须使用这个名字,也就是说如果我们添加点击事件来调用mutations里面的方法修改state的话,定义的click的方法名也必须是updateNum,那么我们要传参数怎么办,我们只需要在方法里面添加参数对象就可以,就像这样
@click=“updateNum({num : 10})”
//辅助函数数组方式
...mapMutations(
["updateNum"]
),
第四种:辅助函数的对象方式
对象里面的key是我们定义的别名,value是我们在mutations里面定义的方法名,传参数和数组模式传参一样@click=“fnDelObj({num : -10})”,只是点击事件的方法名这次是我们自己定义的别名。
//辅助函数对象方式
...mapMutations({
fnAddObj : "updateNum",
fnDelObj : "updateNum"
})
actions异步修改state
actions自己不处理需要修改的数据,他需要调用vuex里面mutations里面的方法进行修改,那么为什么还要引入这个东西呢,因为actions是异步的,好多需要异步处理的地方需要用到。
第一步:在store文件夹下的index里面的actions里面定义对state修改的方法
方法里面存在两个参数,第一个参数是vuex里面commit,用来调用mutations里面的方法,进行数据修改,第二个参数是传进来的值
//Action 类似于 mutation,不同在于:
//Action 提交的是 mutation,而不是直接变更状态。
//Action 可以包含任意异步操作。
actions: {
updateActionNum({ commit }, payload) {
commit("updateNum", payload)
},
},
第二步,定义一个vue的组件,并配置路由
<template>
<div>
<h1>使用vuex的action</h1>
<h1>{{num}}</h1>
<button @click="fnAdd(num)">num++</button>
<button @click="fnDel(num)"> num--</button>
<button @click="updateActionNum({num : 10})">辅助函数数组方式num++</button>
<button @click="updateActionNum({num : -10})">辅助函数数组方式num--</button>
<button @click="fnAddObj({num : 10})">辅助函数对象方式num++</button>
<button @click="fnDelObj({num : -10})">辅助函数对象方式num--</button>
</div>
</template>
<script>
//引入vuex的辅助函数
import { mapState ,mapActions} from "vuex";
export default {
//计算属性
computed: {
...mapState({
num : "num"
})
},
methods:{
fnAdd(num){
//第一种方式
// this.$store.dispatch("updateActionNum",{num:10});
//第二种方式,对象方式
this.$store.dispatch({type : "updateActionNum",num:10});
},
fnDel(num){
this.$store.dispatch("updateActionNum",{num:-10});
},
//辅助函数数组方式
...mapActions(
["updateActionNum"]
),
//辅助函数对象方式
...mapActions({
fnAddObj : "updateActionNum",
fnDelObj : "updateActionNum"
})
}
};
</script>
<style scoped>
</style>
第三步引入vuex的辅助函数
//引入vuex的辅助函数
import { mapState ,mapActions} from "vuex";
第四步获取需要修改的参数
//计算属性
computed: {
...mapState({
num : "num"
})
},
第五步修改数据
修改的方式和mutations的方式基本类似
methods:{
fnAdd(num){
//第一种方式
// this.$store.dispatch("updateActionNum",{num:10});
//第二种方式,对象方式
this.$store.dispatch({type : "updateActionNum",num:10});
},
fnDel(num){
this.$store.dispatch("updateActionNum",{num:-10});
},
//辅助函数数组方式
...mapActions(
["updateActionNum"]
),
//辅助函数对象方式
...mapActions({
fnAddObj : "updateActionNum",
fnDelObj : "updateActionNum"
})
}
modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。
当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。
每个模块拥有自己的 state、mutation、action、getter
现在相当于我一个vuex仓库里面分割成了好多的小格子,每个格子都拥有自己的存储空间,和之前一个大仓库相比,一切都没改变,只是取值的时候,之前是this.
s
t
o
r
e
处
理
的
,
现
在
需
要
加
上
t
h
i
s
.
store处理的,现在需要加上this.
store处理的,现在需要加上this.store.storeA或者其他的小格子的store名字
代码如下:
//引入vue
import Vue from 'vue'
//引入vuex
import Vuex from 'vuex'
//注册vuex
Vue.use(Vuex);
//模块A
const modelA = {
state: {
},
getters: {
},
mutations: {
},
actions: {
}
}
//模块B
const modelB = {
state: {
},
getters: {
},
mutations: {
},
actions: {
}
}
//创建仓库,并把仓库暴漏出去
export default new Vuex.Store({
modules: {
storeA: modelA,
storeB: modelB
}
})