在模块系统中,组件都是单文件组件,即每一个组件都在不同的组件文件中,所以传递数据需要借助一些工具和方法。传递数据的情况有,父子数据传递,子父数据传递,不同组件间数据传递。
父组件向子组件传递数据
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;
const Student=Vue.extend({
props:["name","age","sex"],
template:`
<div>
<p>学生姓名:{{name}}</p>
<p>学生年龄:{{age}}</p>
<p>学生性别:{{sex}}</p>
</div>`
})
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:
- 在父组件通过v-bind 向子组件传递一个函数类型的props,
- 子组件通过props 接受,
- 在适当的事件或者钩子函数内调用传递过来的事件,并传递对应的数据。
代码: 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>
<School :getSchoolName="getSchoolName"/>
<hr>
<Student @getStudentName = 'getStudentName'/>
<hr>
<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() {
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 组件,可以实现,但是比较繁琐。 还可以通过全局事件总线和消息的订阅和发布来实现
全局事件总线
全局事件总线的使用需要三步骤:安装,监听和触发
- 安装全局事件总线:
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')
- 在需要数据的组件内部定义并监听全局事件总线的一个事件,并在回调函数内部,获取到需要的数据。
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>
- 在提供数据的组件内触发全局事件总线的该事件,并传入数据。
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>
- 在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 插件:
- 打开命令行,进入到项目代码中,下载
pubsub-js 插件: - 订阅:谁需要数据,谁就订阅消息,在订阅消息之前需要引入
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() {
this.pubId = pubsub.subscribe("SchoolName",(msgName,data)=>{
console.log("消息名字:",msgName);
console.log("Student组件收到学校名字:",data);
})
},
beforeDestory(){
pubsub.unsubScribe(this.pubId);
}
}
</script>
- 订阅完消息,就等待消息的发布了,谁提供数据,谁就发布消息,并传入所需的数据。本案例中是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>
- 效果:(点击了‘传递学校名给Student组件’ 按钮)
- 解析:
- 引入插件后得到一个
subpub 对象 。 - 订阅消息:
pubsub.subscribe("SchoolName",(msgName,data)=>{})
- 最好在页面挂在完毕的钩子函数内部订阅:
mounted(){} - 订阅的回调函数最好写成箭头函数,保证this指向vm。
- 回调函数接受两个参数:
- msgName 消息的名字(SchoolName)
- data:需要的数据,本案例是学校的名字
- 订阅的消息最好在组件销毁前取消订阅:
beforsDestory(){} - 发布消息:
pubsub.publish("SchoolName",this.name) 。
- 消息的发布一般实在一个合适的时机,比如点击时触发,同时传入需要的数据。
|