目录
Vue.js学习笔记——10.组件化开发(一)
Vue.js学习笔记——12.组件化开发(三)
六、父子组件的通信
1.父传子——props
在实际开发中,我们从服务器请求到了很多数据,并不是在整个页面的大组件来展示的,而是需要下面的子组件进行展示,这时不会让子组件再次发生一个网络请求,而是直接让父组件将数据传递给子组件。父组件向子组件传递数据有两种方式:一是通过props;二是通过事件。
在组件中,使用选项props来声明需要从父级接收到的数据,有两种方式:
- 字符串数组,数组中的字符串就是传递时的名称;
- 对象,对象可以设置传递时的类型,也可以设置默认值等;
篇幅有限,这里只说明常用的对象方式。
由于Vue实例可以看作所有组件的根组件,Vue实例的数据要传入子组件时,在注册时往props传入一个对象,然后在使用时用v-bind将props中的属性和data中的数据绑定即可实现传值。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<!--模板标签-->
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<p>{{cmessage}}</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件
Vue.component(`cpn`,{
template:`#cpn`,
props:{
//指定类型
cmessage:String,
cmovies:Array
}
})
const app = new Vue({
el:"#app",
data:{
message:`你好啊`,
movies:[`海王`,`海贼王`,`海尔兄弟`]
}
})
</script>
</body>
</html>
执行结果:
指定类型支持以下类型:
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
除了指定类型,还可以设置默认值或是否必填等:
Vue.component(`cpn`,{
template:`#cpn`,
props:{
// 基础的类型检查('null'匹配任何类型)
propA:Number,
// 多个可能的类型
propB:[String,Number],
// 必填的字符串
propC:{
type:String,
required:true
},
// 带默认值的数字
propD:{
type: Number,
default:100
},
// 带有默认值的对象
propE:{
type:Object,
// 对象或数组默认值必须从一个工厂函数获取
default: function () {
return {message: `hello`}
}
},
// 自定义验证函数
propF:{
validator:function (value) {
// 这个值必须匹配下列字符串中的一个
return [`success`,`warning`,`danger`].indexOf(value) !== -1
}
}
}
})
需要注意的是v-bind中不支持驼峰语法,如需绑定propA或childMyMessage这样命名的属性就要在绑定时写成用-连接:<cpn :prop-a="movies" :child-my-message="message"></cpn>
2.子传父——自定义事件
当子组件有数据传递到父组件时,需要通过自定义事件来实现。v-on不仅可以用于监听DOM事件,还可以用于组件间的自定义事件。
自定义事件的流程:
- 在子组件中,通过$emit()来触发事件;
- 在父组件中,通过v-on来监听子组件事件;
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!-- v-on监听子组件事件 -->
<cpn @item-click="cpnClick"></cpn>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<!-- 点击按钮触发发射事件 -->
<button v-for="item in categories" @click="btnClick(item)">{{item.name}}</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 注册全局组件
Vue.component(`cpn`,{
template:`#cpn`,
data(){
return {
categories: [
{id: `aaa`, name: `热门推荐`},
{id: `bbb`, name: `家电家器`},
{id: `ccc`, name: `手机数码`},
]
}
},
methods:{
btnClick(item){
//发射事件$emit(发射事件名,发射参数)
this.$emit(`item-click`,item)
}
}
})
const app = new Vue({
el:"#app",
data:{
},
methods:{
cpnClick(item){
console.log(item.name);
}
}
})
</script>
</body>
</html>
执行结果:
原本methods需要有参数而监听时没有加括号,就会默认传入事件参数,而在这里<cpn @item-click="cpnClick"></cpn>
因为监听的是组件的自定义事件,所以会默认传一个自定义事件中btnClick(item){this.$emit(‘item-click’,item)}
发射的参数即item。
还需要注意我这个版本的Vue的v-on所监听的事件名(即此处:@item-click
)好像也不支持驼峰语法,写了驼峰会报错,例子中我都给改成了横杠连接。
七、父子组件的访问方式
有时我们父子之间并不需要传递数据,可能只是想访问父组件或者子组件的对象进行一些操作,这就需要用到:
- 父组件访问子组件:使用
$children
或$refs
- 子组件访问父组件:使用
$parent
1.父访问子——$children
this.$children是一个数组类型,它包含所有子组件对象,先来看看组件对象内有什么。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<!--模板标签-->
<template id="cpn">
<div>
{{message}}
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
},
methods:{
btnClick(){
console.log(this.$children);
}
},
components:{
cpn:{
template:`#cpn`,
data(){
return {
message:`我是子组件的message`
}
},
methods:{
showMessage(){
console.log(`我是子组件的方法`);
}
}
}
}
})
</script>
</body>
</html>
执行结果:
点击按钮打印组件对象可以看到有3个子组件对象,每一个对象内包括很多属性,也包括子组件data中的message和methods里的方法,那么就可以通过访问对象属性的方法来访问子组件的属性了。
往btnClick()添加:
console.log(this.$children[0].message);
this.$children[1].showMessage();
执行结果:
即可访问this.$children
数组中,0号子组件对象的message和1号子组件对象的showMessage()。
2.父访问子——$refs
通过this.$children
数组访问子组件时,一旦之后添加了组件,数组内的顺序就可能发生改变,使得想固定访问某个组件变得困难,这时就需要使用this.$refs
。
this.$refs
数组内默认是没有任何东西的,需要在使用组件时添加ref属性才会生效。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
<cpn ref="aaa"></cpn>
<cpn></cpn>
<button @click="btnClick">按钮</button>
</div>
<!--模板标签-->
<template id="cpn">
<div>
{{message}}
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el:"#app",
data:{
},
methods:{
btnClick(){
console.log(this.$refs);
console.log(this.$refs.aaa);
this.$refs.aaa.showMessage()
}
},
components:{
cpn:{
template:`#cpn`,
data(){
return {
message:`我是子组件的message`
}
},
methods:{
showMessage(){
console.log(`我是子组件的方法`);
}
}
}
}
})
</script>
</body>
</html>
执行结果:
ref属性相当于给组件取了个名字,在this.$refs
中取得的是一个对象,给对象取的名字就是key,value为它的组件对象,通过this.$refs.ref属性值
访问组件对象在进行下一步访问。
3.子访问父——$parent
与$root
在子组件内想要访问父组件的元素,就要通过this.$parent
或this.$root
。this.$parent
是访问当前组件的父组件,this.$root
是访问根组件,即访问Vue实例。
举例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!-- 使用组件 -->
<cpn></cpn>
</div>
<!--模板标签-->
<template id="cpn">
<div>
我是cpn组件
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
我是ccpn组件
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 1.Vue实例
const app = new Vue({
el:"#app",
data:{
message:`你好啊`
},
//2.子组件
components:{
cpn:{
template:`#cpn`,
data(){
return{
message:`我是子组件的message`
}
},
//3.子组件的子组件
components:{
ccpn:{
template:`#ccpn`,
methods:{
btnClick(){
console.log(this.$parent);
console.log(this.$parent.message);
console.log(this.$root);
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>
</body>
</html>
执行结果:
例子中在Vue实例内注册了cpn组件,有在cpn组件中注册了ccpn组件(禁止套娃! ) ,即ccpn的父组件是cpn,cpn组件的父组件是Vue实例。在ccpn中通过this.$parent
访问到的是cpn的组件对象,通过this.$root
访问到的是跟组件,即Vue实例。