目录
一.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种组件通信方式总结如下。
适用于父子组件之间传递参数
(1).父组件 ==> 子组件 通信
(2).子组件 ==> 父组件 通信(要求父先给子一个函数)
传递数据:在子组件标签上写明传递的参数名=“参数”,若参数是js表达式,需要使用v-bind
接收数据:在子组件内部
1. 第一种方式(只接收)
props:['name']
2. 第二种方式(限制类型)
props:{name:String}
3. 第三种方式(限制类型、限制必要性、指定默认值):
props:{
name:{
type:String, //类型
required:true, //必要性
default:'老王' //默认值
}
}
若使用props在父组件中获取子组件的数据:这里在父组件中将addTodo()方法传递给子组件
在子组件内部获取input输入框的输入后,将输入信息封装成一个对象todoObj,调用父组件传来的addTodo()方法 ,以实参的形式将对象todoObj传给父组件,实现子 --> 父传参
props是只读的,Vue底层会监测你对props的修改,如果进行了修改,就会发出警告,若业务需求确实需要修改,那么请复制props的内容到data中一份,然后去修改data中的数据。
1.是一种组件间的通信方式,适用于:子组件 => 父组件
2.使用场景:A是父组件,B是子组件,B想给A传数据,那么就要在A中给B绑定自定义事件。
3. 使用
绑定事件:在父组件中使用@自定义事件名 ,不用写形参,事件回调的形参会自动接收最组件传递的实参内容
或
或使用ref获取子组件实例对象,调用组件实例.$on('事件名',回调函数)用于监听子组件中定义的xxx事件;
若想让自定义事件只能触发一次,可以使用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)去取消订阅。
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结构,也是一种组件间通信的方式,适用于
父组件 ===> 子组件 。
分类:默认插槽、具名插槽、作用域插槽
使用方式:
父组件中:
html结构1
子组件中:
插槽默认内容...
一个组件多个插槽,给每个插槽命名加以区分,子组件name属性对应父组件的slot属性
父组件中:
html结构1
html结构2
子组件中:
插槽默认内容...
插槽默认内容...
理解:数据在组件的自身,但根据数据生成的结构需要组件的使用者来决定。(games数据在Category组件中,但使用数据所遍历出来的结构由App组件决定)
具体编码:父组件通过scope属性值获取子组件的数据,作用域插槽和具名插槽可以结合使用。
父组件中:
- {{g}}
{{g}}
子组件中:
v-model实现的是双向数据绑定,常用于表单元素;但也可以用于自定义组件标签上,实现父子组件的通信,父子组件间的数据同步。
首先这里用v-bind和input事件实现一个v-model
其同样可以实现上面一样的双向数据绑定的效果,并且使用v-model替换组件的:value和@input也是等价的。
可以实现父子组件数据的同步
:money.async代表父组件给子组件传递字符串props[money],给当前子组件绑定一个自定义事件(update:money),子组件如下:
花费了100元
还剩{{money}}元
$attrs是组件实例的一个属性,可以获取父组件传递过来的props数据;
$listeners是组件实例的属性,获取父组件给子组件传递的自定义事件;
如果子组件通过props接收的属性,在$attrs属性当中是获取不到的;
console.log(this.$attrs):
console.log(this.$listeners)
使用el-button封装一个组件,使其具有提示功能
父组件:
子组件:可以采用type=”$attrs.type“ size="$attrs.size"......的写法,但像这样过于繁琐,如果传递的属性名和绑定标签的属性名是一致的,直接使用v-bind="$attrs"的形式,需要注意的是,不能写成:=''$attrs";
同理v-on不能用@进行替换
$children是组件实例的属性,可以获取到当前组件的全部子组件,返回的是一个数组
$parent属性获取到某一组件的父组件,可以操作父组件的数据和方法
现在有如下示例:
点击父组件按钮,父组件的存款要加100,同时子组件的存款要减100,父组件通过$children获取子组件的实例,由于只存在一个子组件,所以$children的索引0就是子组件的实例,从而可以让子组件的data中的money减少100。
爸爸有存款:{{money1}}