VUE - 8.2 - VUE组件间的通讯

4 篇文章 0 订阅


在模块系统中,组件都是单文件组件,即每一个组件都在不同的组件文件中,所以传递数据需要借助一些工具和方法。传递数据的情况有,父子数据传递,子父数据传递,不同组件间数据传递。

父组件向子组件传递数据

props

  • prop用于从父组件向子组件传递数据时自组建的接收器:props:["name","age","sex"]
  • 在父组件传递数据时:<my-student name="张三" :age="16" sex="男"></my-student>
    • 注意age的传参方式,完整写法是 v-bind:age= "18",这样动态地向子组件传递数据,子组件接受到的数据就是引号里面的 数字类型的18。
    • 如果不使用v-bind:的方式传递数据,子组件接收到的数据则都是字符串类型的。
    • prop还可以是对象类型的:
       props:{
          name:[String,Number],
          age:{
              type: Number,
              default: 10
          },
          sex:{
              type: String,
              required: true
          }
      },
      

案例代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Hello Vue</title>
    <link rel="icon" href="../logo.svg">
</head>
<body>
    <div id="root">
        <h1>{{msg}}</h1>
        <hr>
        <!--使用组件-->
        <School></School>        
    </div> 
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<script>
    Vue.config.productionTip=false;
    // 创建Student组件
    const Student=Vue.extend({
        props:["name","age","sex"],//接收数据
        template:`
            <div>
                <p>学生姓名:{{name}}</p>
                <p>学生年龄:{{age}}</p>
                <p>学生性别:{{sex}}</p>
            </div>`
    })    
    // 创建School组件
    const School = Vue.extend({
        data(){
            return{
                name:"五道口技术学院",
                add:"北京"               
            }
        },       
        components:{ //在组建内注册局部组件
            'my-student':Student
        },
        template:`
        <div>
            <p>学校名字:{{name}}</p>
            <p>学校地址:{{add}}</p>
            <hr>       
            学生组件:  
            <!--嵌套组件并传递数据-->
            <my-student name="张三" :age="16" sex="男"></my-student>
        </div>
        `
    })    
     //注册组件(全局注册)
    Vue.component("School",School);    
    var vm = new Vue({
        el:"#root",
        data:{
            msg:"hello Vue!" 
        }
    })
</script>
</body>
</html>

效果:
在这里插入图片描述
在这里插入图片描述

子组件向父组件传递数据

一、props

把学校名传递给App组件

子组件向父组件传递数据也可以借助props:

  1. 在父组件通过v-bind 向子组件传递一个函数类型的props,
  2. 子组件通过props 接受,
  3. 在适当的事件或者钩子函数内调用传递过来的事件,并传递对应的数据。

代码:
App.vue(本案例通过子组件的按钮点击事件,实现数据的传递给父组件)

<template>
  <div id="app"> 
    <h1>{{msg}}</h1>  
    <School :getSchoolName="getSchoolName"/> 
    <hr>
    <Student/>   
  </div>
</template>
<script>
import School from './components/School';
import Student from './components/Student';
export default {
  name: 'App',
  data() {
    return { msg:"你好啊!"}
  },
  components: {School,Student},
  methods: {
    getSchoolName(name){console.log("接收学校名字:",name) }
  },
}
</script>
<style scope>
 #app{ background: #ccc;padding:5px 10px;}
</style>

School.vue

<template>
  <div class="school">
        <p>学校名字:{{name}}</p>
        <p>学校地址:{{add}}</p>
        <p>办校年限:{{age}}</p>
        <button @click = "sendSchoolName">传递学校名给App组件</button>
  </div>
</template>
<script>
export default {
    name:'School',
    props:['getSchoolName'],
    data() {
        return {
            name:"五道口技术学院",
            add:"北京",
            age:100
        }
    },
    methods: {
      sendSchoolName(){ this.getSchoolName(this.name); }
    },
}
</script>
<style scope>
  .school{background: rgb(180, 95, 95); color: #fff;padding: 5px 10px;}
</style>

效果(点击了按钮):
在这里插入图片描述

二、自定义事件

把学生名传递给App组件

  • 父组件内给子组件定义一个自定义事件
    • 定义自定义事件可以在组件上直接定义自定义事件。
    • 也可以给子组件添加 ref = student 属性,通过 $.refs. student.$on() 定位到组件,给组件添加自定义事件 。
  • 在子组件内部在合适的事件或者钩子函数内部,通过 $emit 触发这个自定义事件(本案例通过按钮的点击事件触发)。

App.vue

<template>
  <div id="app"> 
    <h1>{{msg}}</h1>  
    <!--子给父传数据: 通过父组件给子组件传递函数类型的props实现 -->
    <School :getSchoolName="getSchoolName"/> 
    <hr>
     <!--子给父传数据: 通过父组件给子组件绑定一个自定义事件实现 -->
    <Student @getStudentName = 'getStudentName'/>   
	<hr>
     <!--子给父传数据: 通过父组件利用 ref 给子组件绑定一个自定义事件实现 -->  
    <Student ref="student"/>   
  </div>
</template>

<script>
import School from './components/School';
import Student from './components/Student';
export default {
  name: 'App',
  data() {
    return {
      msg:"你好啊!"
    }
  },
  components: {School,Student},
  methods: {
    getSchoolName(name){
      console.log("接收学校名字:",name)
    },
    getStudentName(name){
      console.log("接收学生名字:",name)
    }
  },
}
</script>
<!--...-->

Student.vue

<template>
  <div class="student">
        <p>学生姓名:{{name}}</p>
        <p>学生年龄:{{age}}</p>
        <p>学生性别:{{sex}}</p>
        <button @click = "sendStudentName">传递学生名给App组件</button>
  </div>
</template>
<script>
export default {
    name:'Student',
    data() {
        return {
            name:"张三",
            age:18,
            sex:"男"
        }
    },
    methods: {
        sendStudentName(){
            this.$emit("getStudentName",this.name);
             this.$emit("getStuName",this.name);
        }
    },
    //页面挂在完毕
     mounted() {
      // 通过ref 为student组件绑定自定义事件
      this.$refs.student.$on("getStuName",this.getStudentName);
    },
}
</script>
<!--...-->

效果:(依次点击两个按钮后的效果)
在这里插入图片描述
注意:第一个和第二个组件的自定义事件页面加载完毕就可以触发,但是第三个自定义事件须要5秒后才可以触发成功。

自定义事件其他问题

  • 事件修饰符:自定义事件上也可以添加事件修饰符,默认事件可以添加的,自定义事件都可以绑定自定义事件是通过: <Student @getStudentName.sync = 'getStudentName'/>
  • 解绑自定义事件,定义的额自定义事件,最好在使用完以后找个合适的时机解绑掉,类似定时器使用完最好也要清除定时器一样:vm.$off( [event, callback] ) =>this.$off('getStudentName')
  • 扩展:组件上的事件都是通过v-on:/ @ 添加的自定义事件,那么要想在组件上使用原生事件,比如 click 事件,就需要添加一个后缀.native<Student @click.native = 'getStudentName'/>

自定义事件中的this

上例中第三个组件使用的 ref 标记组件,然后在methods中定义好回调函数,最后在mounted钩子函数内部使用:

  • this.$refs.student.$on("getStuName",fn):因为是 student 组件点用的自定义事件,所以 fn 回调函数中的this指向触发自定义事件的组件.
  • 案例中回调函数使用的是methods中的定义好的函数,因为methods中函数都是vue管理的函数,所以函数中this都是指向vue的实例对象vm
    • 解决办法: this.$refs.student.$on("getStuName",(name)=>{/*...*/})
      • 箭头函数没有this,所以如果使用this则需要向上一级查找。
      • mounted函数是vue管理的函数,this指向vm,所以回调函数内的this又指向了vm

组件间的通讯

还是用上述的案例,需求是把学生姓名传递给学校:
第一个办法可以借助 props 和自定义事件把数据传递给Student的名字传递父组件App,再通过父组件传递给School组件,可以实现,但是比较繁琐。
还可以通过全局事件总线和消息的订阅和发布来实现

全局事件总线

全局事件总线的使用需要三步骤:安装,监听和触发

  1. 安装全局事件总线:
    	// main.js
    	import Vue from 'vue'
    	import App from './App.vue';
    	Vue.config.productionTip = false
    	new Vue({
    	  beforeCreate(){
    	  //安装全局事件总线
    	    Vue.prototype.$bus = this
    	  },
    	  render: h => h(App), 
    	}).$mount('#app')
    
  2. 在需要数据的组件内部定义并监听全局事件总线的一个事件,并在回调函数内部,获取到需要的数据。
    School.Vue
    <template>
     <div class="school">
           <p>学校名字:{{name}}</p>
           <p>学校地址:{{add}}</p>
           <p>办校年限:{{age}}</p>
           <button @click = "sendSchoolName">传递学校名给App组件</button>
     </div>
    </template>
    <script>
    export default {
       name:'School',
       props:['getSchoolName'],
       data() {
           return {
               name:"五道口技术学院",
               add:"北京",
               age:100
           }
       },
       methods: {
         sendSchoolName(){}
       },
       mounted() {
           this.$bus.$on("StudentName",(name)=>{
             console.log("School接收到的数据",name);
           })
       },
    }
    </script>
    <!-- ...-->
    
  3. 在提供数据的组件内触发全局事件总线的该事件,并传入数据。
    Student.vue
    <template>
      <div class="student">
            <p>学生姓名:{{name}}</p>
            <p>学生年龄:{{age}}</p>
            <p>学生性别:{{sex}}</p>
            <button @click = "sendStudentName">传递学生名给School组件</button>
      </div>
    </template>	
    <script>
    export default {
        name:'Student',
        data() {
            return {
                name:"张三",
                age:18,
                sex:"男"
            }
        },
        methods: {
            sendStudentName(){
                this.$bus.$emit("StudentName",this.name);
            }
        },
    }
    </script>
    <!--...-->
    
  4. 在App根组件中分别引入两个子组件:
    App.vue
<template>
  <div id="app"> 
    <h1>{{msg}}</h1>  
    <School/> 
    <hr>
    <Student/> 
  </div>
</template>
<script>
import School from './components/School';
import Student from './components/Student';
export default {
  name: 'App',
  data() {
    return {
      msg:"你好啊!"
    }
  },
  components: {School,Student},
}
</script>
<!--...-->

5.点击Student组件的 ‘传递学生名给Schoo组件’ 按钮得到的效果:在这里插入图片描述

消息订阅和发布

需求:还是上述的案例,将学校名称传递学生组件,使用消息订阅和发布来完成数据的传递,消息的订阅和发布需要借助第三方工具,本案例用的是 pubsub-js 插件:

  1. 打开命令行,进入到项目代码中,下载 pubsub-js 插件:pubsub-js插件安装
  2. 订阅:谁需要数据,谁就订阅消息,在订阅消息之前需要引入 pubsub-js 插件:
    Student.vue(student组件接受学校名字,所以student组件订阅消息)
    <template>
      <div class="student">
            <p>学生姓名:{{name}}</p>
            <p>学生年龄:{{age}}</p>
            <p>学生性别:{{sex}}</p>
            <button @click = "sendStudentName">传递学生名给School组件</button>
      </div>
    </template>
    
    <script>
    import pubsub from 'pubsub-js';// 引入插件
    export default {
        name:'Student',
        data() {
            return {
                name:"张三",
                age:18,
                sex:"男"
            }
        },
        methods: {
            sendStudentName(){
                this.$bus.$emit("StudentName",this.name);
            }
        },
        mounted() {
            // 消息订阅,谁需要数据,谁订阅消息,
            //只有订阅了,发布的时候才能收到想要的数据。
            // 订阅消息最好在页面挂载完毕之后立即订阅。
            // msgName:消息的名字
            // data:传入的参数
            // this.pubId: 
             // pubsub订阅消息返回的时消息的ID(取消订阅也需要用到此ID)
             // 之所以用this.pubId,因为订阅和销毁在不同作用域中,无法使用变量。
            this.pubId = pubsub.subscribe("SchoolName",(msgName,data)=>{
              	console.log("消息名字:",msgName);
            	console.log("Student组件收到学校名字:",data);
            })
        },
        beforeDestory(){
        	//订阅的消息最好在组件销毁前取消订阅。
    		pubsub.unsubScribe(this.pubId);
    	}
    }
    </script>
    
  3. 订阅完消息,就等待消息的发布了,谁提供数据,谁就发布消息,并传入所需的数据。本案例中是School组件提供数据,所以在School组件内内部发布消息。触发机制是按钮点击时发布消息, 在发布消息之前也需要引入 pubsub-js 插件:
    School
    <template>
      <div class="school">
            <p>学校名字:{{name}}</p>
            <p>学校地址:{{add}}</p>
            <p>办校年限:{{age}}</p>
            <button @click = "sendSchoolName">传递学校名给Student组件</button>
      </div>
    </template>
    <script>
    import pubsub from 'pubsub-js';
    export default {
        name:'School',
        props:['getSchoolName'],
        data() {
            return {
                name:"五道口技术学院",
                add:"北京",
                age:100
            }
        },
        methods: {
          sendSchoolName(){
            // 发布消息:谁提供数据谁发布消息,同时传入需要的数据给消息订阅者
            pubsub.publish("SchoolName",this.name);
          }
        },
        mounted() {
            this.$bus.$on("StudentName",(name)=>{
              console.log("School接收到的数据",name);
            })
        },
    }
    </script>
    <!--...-->
    
  4. 效果:(点击了‘传递学校名给Student组件’ 按钮)在这里插入图片描述
  5. 解析:
    1. 引入插件后得到一个subpub对象 。
    2. 订阅消息:pubsub.subscribe("SchoolName",(msgName,data)=>{})
      1. 最好在页面挂在完毕的钩子函数内部订阅:mounted(){}
      2. 订阅的回调函数最好写成箭头函数,保证this指向vm。
      3. 回调函数接受两个参数:
        1. msgName 消息的名字(SchoolName)
        2. data:需要的数据,本案例是学校的名字
      4. 订阅的消息最好在组件销毁前取消订阅:beforsDestory(){}
    3. 发布消息:pubsub.publish("SchoolName",this.name)
      1. 消息的发布一般实在一个合适的时机,比如点击时触发,同时传入需要的数据。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vuexy – Vuejs + HTML 管理仪表板模板 – 是基于 Vue CLI、VuexVuexy 组件框架的对开发人员最友好且高度可定制的 VueJS + HTML 管理仪表板模板。 演示地址:https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/ 更新日志: HTML、HTML + Laravel版本 添加 ----基于 Ul/UX 原则的全新设计 ----新的边框和深色布局 ----新的电子商务仪表板 ----发票应用 ----看板应用程序(仅适用于 HTML 和 HTML + Laravel) ----文件管理器应用程序(仅适用于 HTML 和 HTML + Laravel) ----定价页面 ----博客页面(列表、详细信息、编辑页面) ----添加了布局选项 ----精简菜单 ----盒子布局 ----无菜单 ----空布局 ----空白布局 ----树组件 ----评级组件 ----传单地图 ----SVG 和字体图标支持 更新 ----将 Bootstrap 更新至 v4.5.3 ----更新 Laravel 8 ----更新 Laravel UI、Passport 到最新版本 ----将所有库更新到最新版本 ----改进的组件和扩展 ----改进的小部件和卡片(带有新的游戏化卡片、分析卡片) ----更新的日历应用 ----更新的待办事项应用程序 ----更新的用户应用程序 ----更新的身份验证页面(2 种类型,带有 SVG 插图) ----更新帐户设置 ----更新的常见问题 ----更新的知识库 ----带有实际使用示例的改进表 ----使用新组件改进表单 ----改进的图表 ----更新的杂项页面 ----改进的文档以更好地理解和使用 已弃用 ----AgGrid(将在未来更新中移除) 已移除 ----删除了内容页面并移至文档 ----资料清单 ----芯片组件 ----图表 ----谷歌地图 设计文件【添加】 ----添加了带有原子设计系统的 Sketch & Figma 完整设计文件。 ----明暗设计系统 ----响应式支持 ----2 个仪表板和 6 个布局 ----9 个应用程序和 30 多个页面 ----所有 UI 和表单元素、小部件、图表 电子邮件模板【添加】 ----添加了随时可用的电子邮件模板 --------欢迎 --------验证账户 --------重设密码 --------关闭户口 --------发票 --------促销
Vuexy – Vuejs + HTML 管理仪表板模板 – 是基于 Vue CLI、VuexVuexy 组件框架的对开发人员最友好且高度可定制的 VueJS + HTML 管理仪表板模板。 演示地址:https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/ 更新日志: 反应【添加】 ----基于 Ul/UX 原则的全新设计 ----基于 React 17.x ----基于 Hooks 和功能组件 ----使用 Reactstrap 框架构建 ----可扩展布局 ----新的边框和深色布局 ----新的电子商务仪表板 ----新的小部件和卡片(带有新的游戏化卡片、分析卡片) ----带有完整日历的日历应用 ----发票应用 ----身份验证页面(2 种类型,带有 SVG 插图) ----带有实际使用示例的表格和图表 ----定价页面 ----博客页面(列表、详细信息、编辑页面) ----添加了布局选项 --------盒装布局 --------无菜单 ----评级组件 ----传单地图 ----甜蜜警报 ----烘烤 ----滑块 ----一般错误页面 ----SVG 和字体图标支持 ----有据可查 更新 ----JWT 认证 ----将所有库更新到最新版本 ----改进的组件和扩展 ----改进的小部件和卡片(带有新的游戏化卡片、分析卡片) ----ACL(更新到 CASL 库) ----自动建议 ----更新的待办事项应用程序 ----更新的用户应用程序 ----更新的身份验证页面(2 种类型,带有 SVG 插图) ----更新帐户设置 ----更新的常见问题 ----更新的知识库 ----带有实际使用示例的改进表 ----使用新组件改进表单 ----改进的图表 ----更新的杂项页面 ----改进的文档以更好地理解和使用 已弃用 ----带有基于类的组件的旧版本(我们计划从未来的更新中删除此版本) 已移除 ----农业网格 ----火力基地,Auth0 ----删除了内容页面并移至文档 ----树组件 ----资料清单 ----谷歌地图 ----搜索页面 ----锁屏页面 ----500页

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值