一、通过props配置项传递数据(适用于父组件给子组件传递数据)
父组件向子组件传递数据:
父组件代码:在子组件的标签中传递数据
<template>
<div>
<h2>学校名称:{{schoolName}}</h2>
<!-- 方式一:props(父传子) 教师收到学校的名称 -->
<h1>教师组件:</h1>
<Teacher :schoolName="schoolName"></Teacher>
</div>
</template>
<script>
import Teacher from './Teacher.vue';
export default {
name:'School',
components:{
Teacher
},
data() {
return {
schoolName:'尚硅谷',
}
}
}
</script>
<style>
</style>
子组件代码:定义props配置项去获取父组件在组件标签中传递的数据,props中的数据会存入组件实例对象中,使用数据时直接调用即可。
<template>
<div >
<h2>所属学校名称:{{schoolName}}</h2>
</div>
</template>
<script>
export default {
name:'Teacher',
props:['schoolName']
}
</script>
<style>
</style>
子组件传递数据到父组件:
父组件:定义一个函数接收子组件传递的数据,并把函数放到子组件的标签中传递给子组件
<template>
<div>
<h2>教师名称:{{teacherName}}</h2>
<Teacher :getTeacher="getTeacher"></Teacher>
</div>
</template>
<script>
import Teacher from './Teacher.vue';
export default {
name:'School',
components:{
Teacher
},
data() {
return {
teacherName:''
}
},
methods: {
getTeacher(teacherName){
this.teacherName=teacherName
}
}
}
</script>
<style>
</style>
子组件:使用props配置项接受父组件传递的函数,定义点击事件,在事件中调用父组件的函数并把数据传递
<template>
<div >
<button @click="sendName">点我传递教师名</button>
</div>
</template>
<script>
import StuOne from './StuOne.vue';
import StuTwo from './StuTwo.vue';
export default {
name:'Teacher',
props:['getTeacher'],
data () {
return {
tName:'桥老师'
}
},
methods:{
sendName(){
this.getTeacher(this.tName)
}
}
}
</script>
<style>
</style>
二、组件的自定义事件(适用于子组件向父组件传递数据)
自定义事件写法一:
父组件中:使用@或v-on指令为子组件绑定一个事件,并在父组件中定义一个事件被触发后的回调函数
<template>
<div>
<h2>教师年龄:{{teacherAge}}</h2>
<!-- 方式二 自定义事件(子传父) -->
<Teacher @getAge="gotAge"></Teacher>
</div>
</template>
<script>
import Teacher from './Teacher.vue';
export default {
name:'School',
components:{
Teacher
},
data() {
return {
teacherAge:''
}
},
methods: {
gotAge(age){
this.teacherAge=age
}
}
}
</script>
<style>
</style>
子组件中:使用$emit()去触发父组件为自己绑定的事件,并把数据携带给父组件的回调函数。($emit()在子组件的实例对象上,使用this获取) 在此案例中使用钩子函数触发事件。
<template>
<div >
</div>
</template>
<script>
export default {
name:'Teacher',
data () {
return {
tAge:22
}
},
mounted () {
this.$emit('getAge',this.tAge)
}
}
</script>
<style>
</style>
自定义事件写法二:
父组件中:为子组件设置ref属性,在钩子函数中使用this.$refs.属性名获取子组件实例对象,再使用$on()为子组件实例对象绑定事件并设置回调函数
<template>
<div>
<Teacher ref="tea"></Teacher>
</div>
</template>
<script>
import Teacher from './Teacher.vue';
export default {
name:'School',
components:{
Teacher
},
data() {
return {
teacherAge:''
}
},
methods: {
gotAge(age){
this.teacherAge=age
}
},
mounted(){
//回调函数可以使用箭头函数,也可以定义到methods中再使用this调用
this.$refs.tea.$on('getAge',(age)=>{
this.teacherAge=age
})
}
}
</script>
<style>
</style>
子组件中:此案例中定义了一个点击事件触发自定义事件。不可使用钩子函数触发自定义事件,因为Vue脚手架默认的编译顺序是先编译被父组件引入的子组件,这样会导致触发函数无法匹配到父组件定义的自定义事件。
<template>
<div >
<button @click="sendAge">点我传递教师年龄</button>
</div>
</template>
<script>
export default {
name:'Teacher',
data () {
return {
tAge:22
}
},
methods:{
sendAge(){
this.$emit('getAge',this.tAge)
}
},
}
</script>
<style>
</style>
当子组件废弃时,使用this.$off(事件名)即可解绑事件
三、全局事件总线(适用于任意组件间通信,最适合祖孙组件、兄弟组件之间)
原理:
全局事件总线,全局两个字,意思是在全局都能够访问到。并且能够绑定方法呢?
我们之前给子组件绑定自定义事件的时候,其本质就是为子组件的实例对象new VueComponent绑定上一个自定义事件。
在这个全局事件总线中,我们就不能再给每个组件的实例对象来绑定自定义事件了,而是要将自定义事件绑定到一个全部组件都能够访问的对象上。看到这张图你就有了答案:
我们将它放在vue原型上,那么全局事件总线就能够做到任意间组件通信。
在main.js中设置事件总线:
import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = false
const vm=new Vue({
render: h => h(App),
//在Vue实例对象创建前在Vue原型创建全局事件总线
beforeCreate(){
//把$bus变成Vue实例对象vm,组件就可以通过this调用它
Vue.prototype.$bus=this
}
}).$mount('#app')
console.log(Vue.prototype.$bus)
在获取数据的组件中:为传递数据的组件绑定事件,并定义回调函数。
<template>
<div class="stu">
<h3>我是李四</h3>
<h3>张三的年龄是:{{Zage}}</h3>
</div>
</template>
<script>
export default {
name:'StuTwo',
data () {
return {
Zage:''
}
},
mounted(){
this.$bus.$on('getAge',(age)=>{
this.Zage=age
})
}
}
</script>
<style>
</style>
在传递数据的组件中:触发事件并传递数据
<template>
<div class="stu">
<h3>我是张三</h3>
<button @click="sendAge">把我的年龄告诉李四</button>
</div>
</template>
<script>
export default {
name:'StuOne',
data() {
return {
age:24
}
},
methods: {
sendAge(){
this.$bus.$emit('getAge',this.age)
}
}
}
</script>
<style>
</style>
四、消息的订阅以及发布
一种组件间通信的方式,适用于任意组件间通信。
使用步骤:
1.安装pubsub:npm i pubsub-js
2.引入: import pubsub from 'pubsub-js'
1.接收数据:A组件想接收数据,则在A组件中订阅消息,订阅的回调留在A组件自身。
methods(){ demo(message,data){......} } ...... mounted() { this.pid = pubsub.subscribe('xxx',this.demo) //订阅消息 }
2.提供数据:pubsub.publish('xxx',数据)
3.最好在beforeDestroy钩子中,用PubSub.unsubscribe(pid)去取消订阅。
注意: subscribe的回调函数中有两个参数,第一个参数是消息的名称,第二个参数才是发布消息所携带的数据
例:School组件获取Student组件的数据
School组件:在钩子函数中订阅消息
<template>
<div>
<h2>学生的姓名:{{studentName}}</h2>
</div>
</template>
<script>
import Teacher from './Teacher.vue';
import pubsub from 'pubsub-js'
export default {
name:'School',
data() {
return {
studentName:''
}
},
mounted(){
//订阅消息
pubsub.subscribe('getName',(msg,name)=>{
this.studentName=name
})
}
}
</script>
<style>
</style>
Student组件:发布消息
<template>
<div>
<button @click="sendName">把我的姓名告诉学校</button>
</div>
</template>
<script>
import pubshb from 'pubsub-js'
export default {
name:'StuOne',
data() {
return {
name:'张三'
}
},
methods: {
sendName(){
//发布消息
pubshb.publish('getName',this.name)
}
}
}
</script>
<style>
</style>
五、使用VueX集中管理数据
概念:
专门在Vue中实现集中式状态(数据)管理的Vue插件,对Vue应用中多组件的共享数据进行管理(读/写),也是组件间通信方式之一,适用于任意组件间通信。
何时使用?
当多个组件共同使用同一状态(数据)时
当多个组件修改同一状态(数据)时
工作原理:
首先,VueX中有四个角色,分别是Actions对象、Mutations对象、State对象、Store对象,它们是用来干什么的呢?
State:存放共享数据
Mutations:对数据进行操作,开发者工具中可以看到Mutations的工作记录
Actions:负责编写代码逻辑,并调用Mutations去处理State中数据,可以连接接口、AJAX等
Store:管理者State、Mutations、Actions,在组件中使用this.$Store去操作他们
工作流程:
组件调用dispatch('Actions中函数名',参数)去找Actions,Actions帮组件执行指定函数,函数中通过commit('Mutations函数名',value)去找Mutations,Mutations去操作State中的数据,然后把操作后的数据渲染到页面上。当对数据操作的逻辑较为简单时,组件可以直接通过commit找Mutations去操作数据。
了解三层架构的小伙伴应该可以更快速的理解他们之间的关系,Actions类似于Service层,Mutations类似于Dao层。
生动理解:
再举一个形象的例子:把Vuex看做一个餐厅,Store就是餐厅的经理,Actions是餐厅服务员,Mutations是厨师,而State就是菜品,组件扮演顾客。
顾客来到餐厅吃饭,喊服务员过来点菜,服务员把点的菜通知厨师,厨师去做菜,菜被端到顾客的桌子上。那么也有一种情况,就是当这个顾客和这家餐厅很熟,那么顾客可以直接联系厨师去做菜。
代码实现:
注意!在下载vuex时最好指定vuex的版本(npm i vuex@3)。vue2使用vuex3版本,vue3使用vuex4以及以上版本。
根据vue官方推荐形式,首先在src中创建store文件夹,在文件夹下定义index.js,在index.js中创建Actions对象、Mutations对象、State对象并把他们封装到使用vuex.Store创建的store中并暴露。
import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
const actions ={
//actions中函数有两个参数,第一个参数是上下文参数,其中有state中属性,mutations中函数等
//第二个参数才是传过来的数据
changeName(context,value){
context.commit('CHANGE_NAME',value)
}
}
const mutations={
//mutations中函数也有两个参数,第一个是state的属性,第二个参数才是传过来的值
CHANGE_NAME(state,value){
state.firstStu=value
}
}
const state={
//定义一个变量存放姓名
firstStu:'张三'
}
export default new vuex.Store({
actions,
mutations,
state
})
在main.js中引入store并使用
import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
Vue.config.productionTip = false
const vm=new Vue({
render: h => h(App),
store,
}).$mount('#app')
创建Teacher组件读取store.state中的姓名,StudentOne组件负责修改姓名
<template>
<div >
<h2>学生:{{ $store.state.firstStu}}!</h2>
<StuOne></StuOne>
</div>
</template>
<script>
import StuOne from './StuOne.vue';
export default {
name:'Teacher',
components:{
StuOne
}
}
</script>
<style>
</style>
<template>
<div class="stu">
<h3>我是{{$store.state.firstStu}}</h3>
<!-- 使用vuex技术修改姓名 -->
<input type="text" v-model="name">
<button @click="changeName">改名按钮</button>
</div>
</template>
<script>
export default {
name:'StuOne',
data() {
return {
name:''
}
},
methods: {
changeName(){
this.$store.dispatch('changeName',this.name)
}
}
}
</script>
<style>
</style>
由于使用store的state中的值时,代码过长($store.state....),vuex为我们提供了mapState以便我们简便的操作state中数据:
1.引入mapState:import {mapState} from 'vuex'
2.使用:
<template>
<div >
<h2>学生:{{firstStu}}!</h2>
<StuOne></StuOne>
</div>
</template>
<script>
import StuOne from './StuOne.vue';
import {mapState} from 'vuex'
export default {
name:'Teacher',
components:{
StuOne,
},
computed: {
...mapState({firstStu:'firstStu'})
//简写形式:
//...mapState(['firstStu])
}
}
</script>
<style>
</style>
vuex还为我们提供了mapActions、mapMutations、mapGetters等方便我们操作函数
<template>
<div class="stu">
<h3>我是{{firstStu}}</h3>
<!-- 使用vuex技术修改姓名 -->
<input type="text" v-model="name">
<!--调用函数时就传值-->
<button @click="changeName(name)">改名按钮</button>
</div>
</template>
<script>
import {mapState,mapActions} from 'vuex'
export default {
name:'StuOne',
data() {
return {
name:'张三'
}
},
methods: {
...mapActions(['changeName'])
},
computed:{
...mapState(['firstStu'])
}
}
</script>
<style>
</style>
注意!由于使用了mapActions去为我们调用store中actions的函数,所以需要我们在模板中使用函数时传值。