[toc]
介绍
- 在开发中,往往一些数据确实需要从上层传递到下层:
- 比如在一个页面中,我们从服务器请求到了很多的数据。
- 其中一部分数据,并非是我们整个页面的大组件来展示的,而是需要下面的子组件进行展示。
- 这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
- 如何进行父子组件间的通信呢?Vue官方提到:
- 通过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>
<!--vue操作的实例-->
<div id="app">
<!--使用组件, 在组件中绑定两个属性, 这两个属性必须是在组件注册的时候, 通过props传递进来的-->
<!--props中的变量使用驼峰式写法, 这里在这里绑定时, 需要在大写字母前用-连起来,且大写要转换为小写-->
<cpn :c-title="title" :cdevops="devops" :my-blog-address="blogAddress"></cpn>
</div>
<!--在模板中使用传递进来的属性(变量)-->
<template id="cpn">
<div>
<!--模板里引用的变量要和传递进来v-bind绑定的属性名称一致-->
<!--cname并未在实例中传递,而是在组件中定义了默认值,所以可以直接使用-->
<h2>{{cTitle}}</h2>
<h4>{{cname}}</h4>
<h4>{{myBlogAddress}}</h4>
<ul>
<li v-for="item in cdevops">{{item}}</li>
</ul>
</div>
</template>
<script src="../../js/vue.v2.6.12.js"></script>
<script>
// 父组件传给子组件, 通过props, 值有两种:
// 字符串数组,数组中的字符串就是传递时的名称。
// 对象,对象可以设置传递时的类型,也可以设置默认值等
// 子组件传给父组件, 通过自定义事件
Vue.component(
'cpn', {
template: '#cpn',
// 第一种: 传递一个数组
// props: ['ctitle', 'cdevops']
// 第二种: 传递对象, 名称不能使用驼峰式, 如cTitle, 会出问题. 因为浏览器在html标签里不区分大小写,
// 如果一定要用驼峰式写法, 可以在v-bind绑定属性时, 使用-来连接
props: {
// 可以直接指定传递的类型
cTitle: String,
// 也可以定义传递类型,默认值,可是否必传递
cdevops: {
// 指定类型
type: Array,
// 指定为必须传递的值
request: true,
// 定义默认值, 如果默认值是数组或者对象, 那么必须是一个函数, 使用return返回
default: function () {
return ['keepalived', 'nginx', 'zabbix']
}
},
cname: {
type: String,
default: 'i\'m baiyongjie.'
},
myBlogAddress: {
type: String,
default: 'https://baiyongjie.com'
}
}
}
)
const app = new Vue({
el: '#app',
data: {
title: '我是一个标题',
devops: ['python', 'linux', 'javaScript'],
blogAddress: 'https://www.baiyongjie.com'
}
})
</script>
</body>
</html>
子组件传递数据给父组件
案例一: 点击传递对象
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--监听son-click自定义事件, 监听到执行sonClick函数-->
<!--这里因为在字幕版中定义了点击事件调用了带参数的btnClick(item), 后面又在btnClick方法中将item通过emit传递到父组件事件中,所以这里不需要带参数就能在父组件中输出-->
<son @son-click="sonClick"></son>
</div>
<!--子组件模板-->
<template id="son">
<div>
<button v-for="item in categories" @click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
<script src="../../js/vue.v2.6.12.js"></script>
<script>
// 子组件传给父组件, 通过自定义事件
// 四步:1.子组件加事件 2. 子组件this.$emit('事件名字', 数据) 3. 父组件加事件 4. 父组件执行事件函数(数据)
// 定义子组件
Vue.component('son', {
template: '#son',
data: function () {
return {
// 分类列表
categories: [
{'id': 1, name: '热门推荐'},
{'id': 2, name: '手机数码'},
{'id': 3, name: '日用物品'},
{'id': 4, name: '电脑办公'},
]
}
},
methods: {
// 点击后执行的函数
btnClick(item) {
// 发送一个自定义事件, 事件名称是son-click, 参数是item
this.$emit('son-click', item)
// 点击button打印
console.log('子组件: id', item.id, '名称:', item.name, '完整对象: ', item);
}
}
});
// 父组件
const app = new Vue({
el: '#app',
data: {
message: 'baiyongjie'
},
component: {
son
},
methods: {
// 监听到事件后执行的函数
sonClick(item) {
// 打印传递过来的数据
console.log('父组件id', item.id, '名称:', item.name, '完整对象: ', item)
}
}
})
</script>
</body>
</html>
案例二: 计数器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
当前计数: {{total}}
<!--监听子组件的自定义事件, 监听到了执行changeTotal函数-->
<cpn @decremenet="changeTotal" @incremenet="changeTotal"></cpn>
</div>
<!--子组件cpn的模板-->
<template id="cpn">
<div>
<!--点击加号时, 调用increment函数-->
<button @click="incremenet">+</button>
<!--点击减号时,调用decrement函数-->
<button @click="decremenet">-</button>
</div>
</template>
<script src="../../js/vue.v2.6.12.js"></script>
<script>
// 定义cpn子组件
const cpn = {
template: '#cpn',
data() {
return {
counter: 0
}
},
methods: {
// 进行结果相加, 然后传给父组件
incremenet() {
this.counter++
this.$emit('incremenet', this.counter)
},
// 进行结果相减, 然后传给父组件
decremenet() {
this.counter--
this.$emit('decremenet', this.counter)
}
}
}
const app = new Vue({
el: '#app',
data: {
total: 0,
},
// 注册子组件
components: {
cpn
},
methods: {
// 修改total为传递过来的值
changeTotal(counter) {
this.total = counter
console.log('当前计数:', counter);
}
}
})
</script>
</body>
</html>
父子组件双向通信
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change"/>
</div>
<template id="cpn">
<div>
<h2>props:{{number1}}</h2>
<h2>data:{{dnumber1}}</h2>
<!--<input type="text" v-model="dnumber1">-->
<input type="text" :value="dnumber1" @input="num1Input">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<!--<input type="text" v-model="dnumber2">-->
<input type="text" :value="dnumber2" @input="num2Input">
</div>
</template>
<script src="../../js/vue.v2.6.12.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
num1: 1,
num2: 0
},
methods: {
num1change(value) {
this.num1 = parseFloat(value)
},
num2change(value) {
this.num2 = parseFloat(value)
}
},
components: {
cpn: {
template: '#cpn',
props: {
number1: Number,
number2: Number
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
methods: {
num1Input(event) {
// 1.将input中的value赋值到dnumber中
this.dnumber1 = event.target.value;
// 2.为了让父组件可以修改值, 发出一个事件
this.$emit('num1change', this.dnumber1)
// 3.同时修饰dnumber2的值
this.dnumber2 = this.dnumber1 * 100;
this.$emit('num2change', this.dnumber2);
},
num2Input(event) {
this.dnumber2 = event.target.value;
this.$emit('num2change', this.dnumber2)
// 同时修饰dnumber2的值
this.dnumber1 = this.dnumber2 / 100;
this.$emit('num1change', this.dnumber1);
}
}
}
}
})
</script>
</body>
</html>