概述
1、父子组件的通信
在上一个小节中,我们提到了子组件是不能引用父组件或者Vue实例的数据的。
但是,在开发中,往往一些数据确实需要从上层传递到下层:
- 比如在一个页面中,我们从服务器请求到了很多的数据。
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
Vue官方关于父子组件间的通信提到了:
- 通过prop向子组件传递数据。
- 通过事件向父组件发送消息。
真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。
2、父传子--props基本用法
在组件中,使用选项props来声明需要从父级接收到的数据。
props的值有两种方式:
- 方式一:字符串数组,数组中的字符串就是传递时的名称。
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
(1)当props是数组的时候的用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>
<cpn :cmovies="movies" :cmessage="message"></cpn>
</h2>
</div>
<template id='cpn'>
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<div>{{cmessage}}</div>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
// 父传子:使用props
const cpn = {
template: '#cpn',
props: ['cmovies', 'cmessage']
}
const app = new Vue({
el: '#app',
data: {
message: '你好呀',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
</script>
</body>
</html>
(2)当props是对象的时候,props数据验证
当需要对props进行类型等验证时,就需要对象写法了。
验证都支持哪些数据类型呢?
- String
- Number
- Boolean
- Array
- Object
- Date
- Function
- Symbol
当我们有自定义构造函数时,验证也支持自定义的类型。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>
<!--当没有传message的时候,会使用默认值-->
<cpn :cmovies="movies"></cpn>
</h2>
</div>
<template id='cpn'>
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
<div>{{cmessage}}</div>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
// 父传子:使用props
const cpn = {
template: '#cpn',
// props: ['cmovies', 'cmessage', 'propB', 'propC', 'propD']
props: {
// 1.类型限制
// cmovies: Array,
// cmessage: String
// 2.提供一些默认值
cmessage: {
type: String,
default: 'hello world',
// 3、当required是true的时候必须传这个值
// required: true
},
cmovies: {
type: Array,
// Vue2.5.17以下不会报错,但是以上版本会报错,因为类型是对象或者数组时,默认值必须为一个函数
// default: [],
default() {
return []
}
},
// 4、多个可能的类型
propB: [String, Number],
// 5、自定义验证函数
propC: {
validater(value) {
return ['success', 'warning', 'error'].indexOf(value) !== -1
}
},
// 6、自定义类型
propD: Person
}
}
const app = new Vue({
el: '#app',
data: {
message: '你好呀',
movies: ['海王', '海贼王', '海尔兄弟']
},
components: {
cpn
}
})
// 自定义一个Person类型
function Person() {
this.message='111'
}
</script>
</body>
</html>
当props对象的required值为true的时候,必须要传这个值,否则会报错,报错信息见下图:
(3)props 驼峰标识
注意:v-bind那里不支持驼峰,所以不能使用cInfo,而是要c-info
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<!--v-bind这里不支持驼峰,所以不能使用cInfo,而是要c-info-->
<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
<template id='cpn'>
<div>
<div>{{cInfo}}</div>
<div>{{childMyMessage}}</div>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
// 父传子:使用props
const cpn = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {}
}
},
childMyMessage: {
type: String,
default: ''
}
}
}
const app = new Vue({
el: '#app',
data: {
info: {
name: 'why',
age: 18,
height: 1.88
},
message: 'hello world'
},
components: {
cpn
}
})
// 自定义一个Person类型
function Person() {
this.message='111'
}
</script>
</body>
</html>
3、子传父--自定义事件
什么时候需要自定义事件呢?
- 当子组件需要向父组件传递数据时,就要用到自定义事件了
- 我们之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件流程:
- 在子组件中,通过$emit()来触发事件。
- 在父组件中,通过v-on来监听子组件事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--接收子组件发送的事件-->
<cpn @emit-categories="getCategories"></cpn>
</div>
<!--子组件模板-->
<template id='cpn'>
<div>
<button v-for="item in categories" @click="emitCategories(item)">{{item.name}}</button>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
data() {
return {
categories: [
{id: "111", name: "家用电器"},
{id: "222", name: "美容美发"},
{id: "333", name: "小吃零食"},
{id: "444", name: "柴米粮油"}
]
}
},
methods: {
// 子组件传递给父组件,自定义事件
emitCategories(item) {
this.$emit('emit-categories', item)
}
}
}
const app = new Vue({
el: '#app',
data: {
message: 'hello world'
},
components: {
cpn
},
methods: {
getCategories(item) {
console.log('begin get category', item)
}
}
})
</script>
</body>
</html>
4、父子组件通信--结合双向绑定
不正确的用法:
通过v-model确实是可以绑定到props里面的两个属性,但是可以看到程序有报错。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--接收子组件发送的事件-->
<cpn :number1="num1" :number2="num2"></cpn>
</div>
<!--子组件模板-->
<template id='cpn'>
<div>
<h2>{{number1}}</h2>
<!--不建议这么使用,会报错。避免直接改变props的值,尽量由父组件去修改这个值。-->
<input type="text" v-model="number1"></input>
<h2>{{number2}}</h2>
<input type="text" v-model="number2"></input>
</div>
</template>
<script src="../../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
}
}
}
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
components: {
cpn
}
})
</script>
</body>
</html>
报错信息:避免直接改变props的值,尽量由父组件去修改这个值。
正确的用法:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<h2>父组件的num1:{{num1}}</h2>
<!--组件也可以使用单标签-->
<cpn :number1="num1" :number2="num2" @num1change="num1change"/>
</div>
<template id='cpn'>
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dataNumer1}}</h2>
<!--不建议这么使用,会报错。避免直接改变props的值,尽量由父组件去修改这个值。-->
<!-- <input type="text" v-model="number1"> -->
<!--建议绑定到data或者computed计算属性上-->
<!-- <input type="text" v-model="dataNumer1"> -->
<input type="text" :value="dataNumer1" @input="number1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dataNumer2}}</h2>
<input type="text" :value="dataNumer2" @input="dataNumer2=$event.target.value">
</div>
</template>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 2
},
methods: {
num1change(value) {
this.num1 = parseInt(value)
}
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dataNumer1: this.number1,
dataNumer2: this.number2
}
},
methods: {
number1Input(event) {
// 1、将input中的value赋到dnumber中
this.dataNumer1 = event.target.value;
// 2、同时发送一个事件,让父组件修改值
this.$emit('num1change', this.dataNumer1)
// 3、同时修改dnumber2的值, 变为number1的100倍
this.dataNumer2 = this.dataNumer1 * 100
// 4、发送一个事件,让父组件修改值
this.$emit('num2change', this.dataNumer2)
}
}
}
}
})
</script>
</body>
</html>
5、父访问子
(1)父组件访问子组件的方式: $children
有时候我们需要父组件直接访问子组件,子组件直接访问父组件,或者是子组件访问根组件。
父组件访问子组件:使用$children或$refs(reference引用)
我们先来看下$children的访问
this.$children是一个数组类型,它包含所有子组件对象。
我们这里通过一个遍历,取出所有子组件的message状态。
<!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>我是子组件</div>
</template>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
btnClick() {
// 1.$children
console.log (this.$children)
for(let c of this.$children) {
console.log(c.name)
c.showMessage()
}
// 在实际开发中一般不会这么使用,因为在实际开发中组件顺序不固定,不能通过坐标来获取,如果有新需求在多个组件中间加了一个组件,就会导致问题
this.$children[1].showMessage()
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('子组件show message.')
}
}
}
}
})
</script>
</body>
</html>
(2) 父组件访问子组件的方式: $refs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="cpn1"></cpn>
<button @click="btnClick">按钮</button>
</div>
<template id='cpn'>
<div>我是子组件</div>
</template>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
methods: {
btnClick() {
//2.refs属性 对象属性,默认refs中是空对象
console.log(this.$refs)
// 需要在组件上加个ref属性,ref="cpn1",则可以通过ref属性给的名称cpn1拿到组件对象
this.$refs.cpn1.showMessage()
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('子组件show message.')
}
}
}
}
})
</script>
</body>
</html>
6、子访问父
(1)子组件访问父组件:使用$parent
(在开发里不建议这样使用,因为一旦这样使用,子组件就不够独立了。在开发中,我们会把很多东西抽成一个个组件,一方面是为了组件思想,但是主要是为了复用性。这样用则复用性不是很强,因为在不同的父组件中使用,可能不一定会有要获取的内容,会导致和父组件耦合性过高。)
(2)直接访问根组件:$root
<!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>
我是子组件
<button @click="btnClick">按钮</button>
<ccpn></ccpn>
</div>
</template>
<template id='ccpn'>
<div>
我是孙子组件
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: '根组件,您好呀'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
methods: {
btnClick() {
// 1.访问父组件 $parent
console.log(this.$parent)
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
// 1.访问父组件 $parent, 在开发里不建议这样使用,因为一旦这样使用,子组件就不够独立了。
// 在开发中,我们会把很多东西抽成一个个组件,一方面是为了组件思想,但是主要是为了复用性。
// 这样用则复用性不是很强,因为在不同的父组件中使用,可能不一定会有要获取的内容,会导致和父组件耦合性过高。
console.log(this.$parent)
console.log(this.$parent.name)
// 2.访问根组件$root
console.log(this.$root)
console.log(this.$root.message)
}
}
}
}
}
}
})
</script>
</body>
</html>
执行结果如下:
最后
以上就是执着花瓣为你收集整理的Vue学习总结(十三)——父子组件通信的全部内容,希望文章能够帮你解决Vue学习总结(十三)——父子组件通信所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复