【Vue.js】vue2组件间通信方式总结
目录
一.props传参
1.父组件 ==> 子组件
2.子组件 ==> 父组件
二.组件的自定义事件
三.全局事件总线
四.消息订阅和发布
五.vuex
六.插槽
1.默认插槽
2.具名插槽
3.作用域插槽
七.v-model
八.sync修饰符
九.$attrs和$listeners属性
十.$children和$parent属性
vue项目中大量采用组件化开发,组件之间的通信传递数据是必不可少的一个环节,并且随着需求的不同,需要进行不同关系的组件之间的值传递,从而出现各种组件通信的方式。
组件和组件之间的关系常见有父子组件,兄弟组件,两类关系的组件通信适用的方法是不同的,方法使用的不当,极有可能事倍功半;最常见的父子组件通信方式有props传参,常用的兄弟组件通信方式有自定义事件,全局事件总线;较大型的项目,一般采用的是vuex统一管理数据;此外,在一些组件库中,也经常会使用到其它几种通信方式,比如element-ui中,当你需要在一个el-table-column中插入一张图片时,就需要使用作用域插槽,将要展现的页面结构传递给子组件;再比如,在el-dialog弹出框组件中,会使用sync修饰符,获取控制组件显示或隐藏的标志符等等。以下10种组件通信方式总结如下。
一.props传参
适用于父子组件之间传递参数
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
1.父组件 ==> 子组件
传递数据:在子组件标签上写明传递的参数名=“参数”,若参数是js表达式,需要使用v-bind
<Demo :name="xxx"/>
接收数据:在子组件内部
1. 第一种方式(只接收)
props:['name']
2. 第二种方式(限制类型)
props:{name:String}
3. 第三种方式(限制类型、限制必要性、指定默认值):
props:{name:{type:String, //类型 required:true, //必要性 default:'老王' //默认值}
}
2.子组件 ==> 父组件
若使用props在父组件中获取子组件的数据:这里在父组件中将addTodo()方法传递给子组件
<template><div id="root"><MyHeader :addTodo="addTodo"/></div>
</template><script>import MyHeader from './components/MyHeader'export default {name:'App',components:{MyHeader},data() {return {todos:[{id:'001',title:'xx',done:true},]}},methods: {//添加一个todoaddTodo(todoObj){this.todos.unshift(todoObj)},}}
</script>
在子组件内部获取input输入框的输入后,将输入信息封装成一个对象todoObj,调用父组件传来的addTodo()方法 ,以实参的形式将对象todoObj传给父组件,实现子 --> 父传参
<template><div class="todo-header"><input type="text" placeholder="请输入你的任务名称,按回车键确认" v-model="title" @keyup.enter="add"/></div>
</template><script>import {nanoid} from 'nanoid'export default {name:'MyHeader',//接收从App传递过来的addTodoprops:['addTodo'],data() {return {//收集用户输入的titletitle:''}},methods: {add(){//校验数据if(!this.title.trim()) return alert('输入不能为空')//将用户的输入包装成一个todo对象const todoObj = {id:nanoid(),title:this.title,done:false}//通知App组件去添加一个todo对象this.addTodo(todoObj)//清空输入this.title = ''}},}
</script>
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
二.组件的自定义事件
1.是一种组件间的通信方式,适用于:子组件 => 父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件。
3. 使用
绑定事件:在父组件中使用@自定义事件名 ,不用写形参,事件回调的形参会自动接收最组件传递的实参内容
<template><Demo @xxx="test">或<Demo v-on:xxx="test">
</template><script>export default {methods:{test(事件参数1,...事件参数2-n){}}}
</script>
或使用ref获取子组件实例对象,调用组件实例.$on('事件名',回调函数)用于监听子组件中定义的xxx事件;
<template><Demo ref="demo"/></template><script>export defalut {mounted{this.$refs.demo.$on('xxx',this.test)},methods:{test(事件参数1,...事件参数2-n){}}}</script>
若想让自定义事件只能触发一次,可以使用once修饰符,或$once方法。
触发事件:数据可以传递一个或多个,多个数据可以传递多个实参(第二个参数开始),或者写成一个对象
this.$emit('xxx',数据);
解绑事件
this.$off('xxx'); // 解绑指定事件
this.$off(['a','b']); // 解绑多个
this.$off(); // 解绑全部
注意:通过this.$refs.xxx.$on('aaa',回调)绑定自定义事件时,回调要么配置在methods中,要么用箭头函数,否则this指向会出问题
销毁了当前的组件实例对象时,其身上的所有自定义事件全部销毁了;销毁了vue实例时,其所有的子组件全部销毁了,子组件的自定义世事件也被销毁了,但所有的原生事件不会销毁
三.全局事件总线
一种组件间通信的方式,适用于任意组件间通信。
安装全局事件总线:
new Vue({......beforeCreate() {Vue.prototype.$bus = this //安装全局事件总线,$bus就是当前应用的vm},......
})
由于vc.prototype.__ proto __ = vm.prototype,所以组件实例是可以访问到vue实例上的属性的,可以访问到$bus
使用事件总线
1. 接收数据:A组件想接收数据,则在A组件中给$bus绑定自定义事件,事件的回调留在A组件自身,事件的绑定一般在mounted钩子中进行,事件只需绑定一次即可,这和原生js中给DOM元素绑定事件的回调是一个道理。
methods(){demo(data){......}
},
......
mounted() {this.$bus.$on('xxxx',this.demo)
}
2. 提供数据
this.$bus.$emit('xxxx',数据)
最好在beforeDestroy钩子中,用$off去解绑当前组件所用到的事件。
四.消息订阅和发布
一种组件间通信的方式,适用于任意组件间通信;
订阅方是数据的获取方,而发布方是数据的提供方;
使用步骤:
1. 安装:pubsub:npm i pubsub-js
2. 引入:import pubsub from 'pubsub-js'
3. 接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){demo(data){......}
}
......
mounted() {this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息
}
4.提供数据:
pubsub.publish('xxx',数据)
最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。
五.vuex
1.概念
在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
2.使用场景
多个组件需要共享数据时
原理图:vuex的核心主要是由Actions,Mutations和state三部分组成,vuex做的是状态管理,实际上就是存放多个组件可以共享的数据,这些数据存放在state中,一般存放的可以是服务器返回的信息。
在下图中,Vue Components 就是vue组件,在组件中使用this.$store.dispatch('xxxAction') 调用某个action执行,一般在action中请求服务器接口(Backend API),获取请求得到的异步结果,但action无法直接操作state存放数据,所以此时它会调用指定的mutation,在mutation的内就可以操作state修改数据,最后在各个组件内使用this.$store.state.xxx 拿到state存放的状态;
当然,如果没有额外的业务逻辑需要处理,组件无需派发actions,可以在组件内直接使用this.$store.commit('xxxmutation'),通知mutations修改状态。
3.搭建vuex环境
0. 安装:npm i vuex
1. 创建文件:src/store/index.js
必须得在store实例创建前使用Vuex插件,所以Vue.use(Vuex)必须写在index.js中,而不是main.js中。
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}
// 准备getters 对state中的内容做二次处理,类似于计算属性
const getters = {}//创建并暴露store
export default new Vuex.Store({actions,mutations,state
})
2. 在main.js中创建vm时传入store配置项
//引入store
import store from './store'
......//创建vm
new Vue({el:'#app',render: h => h(App),store
})
这样就可以在vue实例和组件实例中看到$store属性了。
4.vuex的具体使用
1. 初始化数据、配置actions、配置mutations,操作文件store.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)const actions = {//响应组件中加的动作jia(context,value){// console.log('actions中的jia被调用了',miniStore,value)context.commit('JIA',value)},}const mutations = {//执行加IA(state,value){// console.log('mutations中的JIA被调用了',state,value)state.sum += value}
}//初始化数据
const state = {sum:0
}//创建并暴露store
export default new Vuex.Store({actions,mutations,state,
})
2.组件中读取vuex中的数据:
$store.state.sum
3.组件中修改vuex中的数据:
$store.dispatch('action中的方法名',数据)
或
$store.commit('mutations中的方法名',数据)
备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
action中的方法第一个参数context还包括dispatch属性,即当业务逻辑较多可以链式调用多个action方法处理一个业务
const actions = {//响应组件中加的动作jia(context,value){// console.log('actions中的jia被调用了',miniStore,value)value = value + 1;context.dispatch('jia1',value)},jia1(context,value) {value = value * 2;context.dispatch('jia2',value);},jia2(context,value) {value = value * 3;context.commit('JIA',value);},}
六.插槽
作用:让父组件可以向子组件指定位置插入html结构,也是一种组件间通信的方式,适用于
父组件 ===> 子组件 。
分类:默认插槽、具名插槽、作用域插槽
使用方式:
1.默认插槽
父组件中:
<Category><div>html结构1</div>
</Category>
子组件中:
<template><div><!-- 定义插槽 --><slot>插槽默认内容...</slot></div>
</template>
2.具名插槽
一个组件多个插槽,给每个插槽命名加以区分,子组件name属性对应父组件的slot属性
父组件中:
<Category><template slot="center"><div>html结构1</div></template><template v-slot:footer><div>html结构2</div></template>
</Category>
子组件中:
<template><div><!-- 定义插槽 --><slot name="center">插槽默认内容...</slot><slot name="footer">插槽默认内容...</slot></div></template>
3.作用域插槽
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
具体编码:父组件通过scope属性值获取子组件的数据,作用域插槽和具名插槽可以结合使用。
父组件中:
<Category><template scope="scopeData"><!-- 生成的是ul列表 --><ul><li v-for="g in scopeData.games" :key="g">{{g}}</li></ul></template>
</Category><Category><template slot-scope="scopeData" slot="s1"><!-- 生成的是h4标题 --><h4 v-for="g in scopeData.games" :key="g">{{g}}</h4></template>
</Category>
子组件中:
<template><div><slot :games="games" name="s1"></slot></div>
</template><script>export default {name:'Category',props:['title'],//数据在子组件自身data() {return {games:['红色警戒','穿越火线','劲舞团','超级玛丽']}},}
</script>
七.v-model
v-model实现的是双向数据绑定,常用于表单元素;但也可以用于自定义组件标签上,实现父子组件的通信,父子组件间的数据同步。
首先这里用v-bind和input事件实现一个v-model
</template><input type="text" :value="text" @input="text=$event.target.value">
</template><script>export default {data(){return{text:0}},
}
其能实现v-model一样的效果,这里的value是input表单元素的属性,@input是表单的输入事件;如果一样的写法,写在组件标签上那么value就是组件的props参数,而@input则是自定义事件,若依旧i将@input作为原生的输入事件需要添加事件修饰符,即@input.native
接下去创建一个Inputs组件,给其传递一个参数value,并给其绑定一个自定义事件input
</template><Inputs :value="text" @input="text=$event"/><Inputs v-model="text"/>
</template><script>export default {data(){return{text:0}},
}
在组件内部接收参数value,触发自定义时间内input,将表单的输入内容$event.target.value作为自定义事件的参数传递。
<template><div><span>input:</span><input type="text" :value="value" @input="$emit('input',$event.target.value)"></div>
</template><script>
export default {props:['value']
}
</script>
其同样可以实现上面一样的双向数据绑定的效果,并且使用v-model替换组件的:value和@input也是等价的。
八.sync修饰符
可以实现父子组件数据的同步
<template><Money :money.sync="money"/><!--等价于 --><Money :money="money" @update:money="money=$event">
</template><script>
import Money from './components/Money.vue'
export default {data(){return {money:10000}},components:{Money}
}
</script>
:money.async代表父组件给子组件传递字符串props[money],给当前子组件绑定一个自定义事件(update:money),子组件如下:
<template><div><span>花费了100元</span><button @click="$emit('update:money',money-100)">花钱</button><span>还剩{{money}}元</span></div>
</template><script>
export default {props:['money']
}
</script>
九.$attrs和$listeners属性
$attrs是组件实例的一个属性,可以获取父组件传递过来的props数据;
$listeners是组件实例的属性,获取父组件给子组件传递的自定义事件;
如果子组件通过props接收的属性,在$attrs属性当中是获取不到的;
console.log(this.$attrs):
console.log(this.$listeners)
使用el-button封装一个组件,使其具有提示功能
父组件:
<template><ElButton type="success" icon="el-icon-delete" size="mini" title="按钮" @click="handler"/>
</template>
<script>
import ElButton from './components/elButton.vue'
export default {components:{ElButton},methods:{handler(){console.log('123')},}
}
</script>
子组件:可以采用type=”$attrs.type“ size="$attrs.size"......的写法,但像这样过于繁琐,如果传递的属性名和绑定标签的属性名是一致的,直接使用v-bind="$attrs"的形式,需要注意的是,不能写成:=''$attrs";
同理v-on不能用@进行替换
<template><a :title="title"><el-button v-bind="$attrs" v-on="$listeners"></el-button></a>
</template><script>
export default {props:['title'],mounted(){console.log(this.$attrs);}
}
</script>
十.$children和$parent属性
$children是组件实例的属性,可以获取到当前组件的全部子组件,返回的是一个数组
$parent属性获取到某一组件的父组件,可以操作父组件的数据和方法
现在有如下示例:
点击父组件按钮,父组件的存款要加100,同时子组件的存款要减100,父组件通过$children获取子组件的实例,由于只存在一个子组件,所以$children的索引0就是子组件的实例,从而可以让子组件的data中的money减少100。
<template><div><h2>爸爸有存款:{{money1}}</h2><button @click="borrowM(100)">找小明借钱100</button><Son/></div>
</template><script>
import Son from './components/Son.vue'
export default {data(){return{money1:1000}},methods:{borrowM(money){this.money1 += money;this.$children[0].money -= money;},},components:{Son}
}
如下,在子组件Son中,点击按钮,子组件中的存款减少50,同时父组件中的存款增加50,可以通过$parent获取父组件,从而操作父组件的data中的属性money1让其增加50. $parent返回的就是父组件实例对象。
<template><div><h3>儿子小明:现有存款:{{money}}</h3><button @click="giveMoney(50)">给爸爸50元</button></div>
</template><script>
export default {data(){return {money:1000}},methods:{giveMoney(money){this.money -= money;// 获取到父组件,让父组件的数据加50this.$parent.money1 += money;}}
}
</script>
【Vue.js】vue2组件间通信方式总结相关推荐
- Vue 组件间通信方式汇总,总有一款适合你( 附项目实战案例 )
前言 前期分享的 200行纯前端Vue代码!教你写一个专属TodoList[零基础友好] 这个项目案例中使用的组件间通信方式是通过 事件绑定与props 接收来实现的,具体使用方式将在下面进行详细介绍 ...
- Vue 组件间通信方式:自定义事件
前言 前期分享的测试开发系列!Vue 组件间通信方式汇总,总有一款适合你( 5分钟教程-附项目实战案例 )中介绍了 3 种组件间的通信方法,分别是: props 全局事件总线 消息订阅与发布 今天给大 ...
- 360奇舞团钟恒:选用Vue.js进行组件化开发,我们遇到了哪些坑?
责编:陈秋歌,关注前端开发领域,寻求报道或者投稿请发邮件chenqg#csdn.net. 欢迎加入"CSDN前端开发者"微信群,参与热点.难点技术交流.请加群主微信「Rachel_ ...
- Vue.js教程-组件化开发
Vue.js教程-组件化开发 前言 Vue组件化 什么是组件化 Vue单页面开发的解释 组件化思想 组件的使用 原理 实际开发中的使用-父子组件 父子组件传递数据 父传子-props用法 子传父-th ...
- Vue2组件间通信——父传子值props
Vue2组件间通信--父传子值props Vue2中组件间通信系列,本篇是关于父组件向子组件传值的介绍,这里我会用通俗的语言帮助大家理解props配置项 功能: 子组件可以接收到父组件传递的数据信息, ...
- Vue.js 父子组件通信的十种方式;告诉世界前端也能做 AI;你可能不知道的14个JavaScript调试技巧...
记得点击文章末尾的"阅读原文"查看哟~ 下面先一起看下本周的摘要吧~ 想了解老用户如何参与阿里云双十一1折拼团特惠主机的,可以看第二条推送,文中提供了两种方法~,一起看看本周有哪些 ...
- Vue.js 定义组件模板的七种方式
转载自 Vue.js 定义组件模板的七种方式 在 Vue 中定义一个组件模板,至少有七种不同的方式(或许还有其它我不知道的方式): 字符串 模板字面量 x-template 内联模板 render ...
- Vue实现兄弟组件间的方法调用及回调
Vue实现兄弟组件间的方法调用及回调 看兄弟间数据传递的看我写的这篇文章:vue实现兄弟组件间的实时通信 先说说我遇到的情况: 点击左侧树tree,需要调用右侧houseInfo组件中的新增方法,弹出 ...
- 两个读书笔记:springboot+vue.js分布式组件全栈开发训练营 + 大数据开发基础
(springboot+vue.js分布式组件全栈开发训练营原文在notion中, 大数据开发在思维导图中, 这个博客只是保存, 无法阅读. ) what is different between s ...
最新文章
- 【python数据挖掘课程】二十.KNN最近邻分类算法分析详解及平衡秤TXT数据集读取
- 在VS中创建多个项目
- MongoDB 教程五: MongoDB固定集合和性能优化
- (四)Raspberry Pi上的人工智能人脸检测
- 如何在Mac OSX系统下安装Tomcat
- SQL Server-【知识与实战VIII】触发器(中)
- H3C CLI基础笔记(交换机,链路聚合-DHCP)
- 遗传算法MATLAB
- 某中学校校园网络方案设计(课程设计)
- 基于微信小程序的人脸识别
- 软件工程专业大学四年学什么
- Js、 replace 全部内容替换、替换全部匹配内容、替换第一个
- Android反编译工具与反编译步骤及常见问题
- 河南python培训班
- 【转】WPF自定义控件与样式(13)-自定义窗体Window 自适应内容大小消息框MessageBox...
- 用计算机看影碟是数字化过程,2012年上海市高中学业水平考试信息科技
- php 统计汉字,PHP 统计实时统计汉字个数和区别
- Win10 下报错 WerFault.exe -解决方法亲测有效
- Scrapy糗事百科爬虫实战代码分析
- asp生成带参数的二维码并合成推广海报图片,asp合并合成推广海报图片asp代码