1.1 父组件向子组件传递
真实的开发中,Vue实例和子组件的通信和父组件和子组件的通信过程是一样的。
子组件是不能引用父组件或者Vue实例的数据的。这个时候,并不会让子组件再次发送一个网络请求,而是直接让大组件(父组件)将数据传递给小组件(子组件)。
props基本用法
- 在组件中,使用选项props来声明需要从父级接收到的数据。
- props的值有两种方式
- 方式一:字符串数组,数组中的字符串就是传递时的名称。
- 方式二:对象,对象可以设置传递时的类型,也可以设置默认值等。
- props传递案例
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>父组件向子组件传递参数</title>
</head>
<body>
<div id="app">
<!--3.使用组件-->
<!--绑定属性-->
<cpn1 :cmessage="message" :cstarts="starts"></cpn1>
</div>
<!--创建模板-->
<template id="cpn">
<div>
<ul>
<li v-for="item in cstarts">{{item}}</li>
</ul>
<h3>{{cmessage}}</h3>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn1 = {
// 定义模板
template: '#cpn',
// 父组件向子组件传递内容(父传子: props)
props: {
// 类型限制,提供默认值,和及其必传值
cmessage: {
type: String,
dafault: 'Curry',
required: true
},
// 类型是数组或者对象类型时候,默认值必须是一个函数
cstarts: {
type: Array,
default(){
return []
}
}
}
}
// 创建对象
const app = new Vue({
// 挂载要管理的元素
el: '#app',
// 定义数据
data: {
message: 'hello world!',
starts: ['kobe', 'Jmaes', 'Curry', 'Duncan']
},
// 注册组件
components: {
cpn1
}
})
</script>
</body>
</html>
执行结果
1.2 props驼峰标识问题
注意: props传递中的不支持驼峰标识!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>props中的驼峰标识</title>
</head>
<body>
<div id="app">
<!--这里不支持驼峰标识-->
<cpn1 :c-info="info" :child-my-message="message"></cpn1>
</div>
<!--定义模板-->
<template id="cpn">
<!--定义子组件模板的时候,要用外层的div将其包裹起来-->
<div>
<h2>{{cInfo}}</h2>
<h2>{{childMyMessage}}</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn1 = {
template: '#cpn',
props: {
cInfo: {
type: Object,
default() {
return {}
}
},
childMyMessage: {
type: String,
default: ''
}
}
}
// 创建对象
const app = new Vue({
// 挂载要管理的元素
el: '#app',
// 定义数据
data: {
info: {
name: 'guardWhy',
age: 26,
height: 1.71
},
message: 'curry'
},
// 注册组件
components:{
cpn1
}
})
</script>
</body>
</html>
执行结果
1.3 子级向父级传递(自定义事件)
什么时候需要自定义事件呢?
当子组件需要向父组件传递数据时,就要用到自定义事件了。
之前学习的v-on不仅仅可以用于监听DOM事件,也可以用于组件间的自定义事件。
自定义事件的流程
在子组件中,通过$emit()来触发事件。
在父组件中,通过v-on来监听子组件事件。
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>子传父自定义事件</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<!--父组件监听(v-on)事件-->
<cpn1 @item-click="cpnClick"></cpn1>
</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>
// 子组件
const cpn1 = {
// 定义模板
template: '#cpn',
data(){
return{
categories: [
{id: 'aaa', name: '热门推荐'},
{id: 'bbb', name: '手机数码'},
{id: 'ccc', name: '家用家电'},
{id: 'ddd', name: '电脑办公'},
]
}
},
methods: {
btnClick(item){
// console.log(item);
// 发射事件:自定义事件
this.$emit('item-click', item)
}
}
}
// 父组件
const app = new Vue({
// 挂载要管理的元素
el: '#app',
components : {
cpn1
},
methods: {
cpnClick(item){
// 输出item
console.log('cpnClick', item);
}
}
})
</script>
</body>
</html>
执行结果
1.4 组件通信集合双向绑定
方式一
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件通信(双向绑定)</title>
</head>
<body>
<!--父组件模板-->
<div id="app">
<cpn1 :number1="num1" :number2="num2"
@num1change="num1change"
@num2change="num2change"></cpn1>
</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.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: {
// 子组件
cpn1:{
// 定义模板
template: '#cpn',
// 父子之间的通信
props:{
number1: Number,
number2: Number
},
// 添加一个data属性,从而实现双向绑定
data(){
return{
dnumber1: this.number1,
dnumber2: this.number1
}
},
methods: {
num1Input(event){
// 将input中的value赋值到dnumber中
this.dnumber1 = event.target.value;
// 让父组件可以修改值,发出一个事件
this.$emit('num1change', this.dnumber1);
// 修改dnumber2的值
this.dnumber2 = this.dnumber1 * 100;
this.$emit('num2change', this.dnumber2)
},
num2Input(event){
// 将input中的value赋值到dnumber中
this.dnumber2 = event.target.value;
this.$emit('num2change', this.dnumber2);
// 修改dnumber1的值
this.dnumber1 = this.dnumber2 / 100;
this.$emit('num1change', this.dnumber1)
}
}
}
}
})
</script>
</body>
</html>
执行结果
方式二
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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">
<h2>props:{{number2}}</h2>
<h2>data:{{dnumber2}}</h2>
<input type="text" v-model="dnumber2">
</div>
</template>
<script src="../js/vue.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,
name: ''
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2
}
},
watch: {
dnumber1(newValue) {
this.dnumber2 = newValue * 100;
this.$emit('num1change', newValue);
},
dnumber2(newValue) {
this.number1 = newValue / 100;
this.$emit('num2change', newValue);
}
}
}
}
})
</script>
</body>
</html>
执行结果
1.5 父组件直接访问子组件
1.5.1 $children的访问
- this.$children是一个数组类型,它包含所有子组件对象。
- 能通过一个遍历,取出所有子组件的message状态。
1.5.2 $children的缺陷
- 通过$children访问子组件时,是一个数组类型,访问其中的子组件必须通过索引值。
- 但是当子组件过多,我们需要拿到其中一个时,往往不能确定它的索引值,甚至还可能会发生变化。
- 有时候,想明确获取其中一个特定的组件,这个时候就可以使用$refs。
1.5.3 $refs的使用
- $refs和ref指令通常是一起使用的。
- 首先,通过ref给某一个子组件绑定一个特定的ID。
- 其次,通过this.$refs.ID就可以访问到该组件了。
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件访问(父访问子)</title>
</head>
<body>
<!--2.定义一个div元素-->
<div id="app">
<cpn1></cpn1>
<cpn1></cpn1>
<cpn1 ref="aaa"></cpn1>
<button @click="btnClick">按钮</button>
</div>
<!--子组件模板-->
<template id="cpn">
<div>
<h2>我是子组件</h2>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
// 创建对象
const app = new Vue({
// 挂载要管理的元素
el: '#app',
// 定义数据
data: {
message: 'hello world!'
},
methods: {
btnClick(){
// 1.$children
/*
console.log(this.$children);
for(let c of this.$children){
console.log(c.name);
c.showMessage();
}
console.log(this.$children[1].name);
*/
// 2.$refs ==> 对象类型,默认这是一个空对象
console.log(this.$refs.aaa.name);
}
},
// 创建子组件
components: {
cpn1: {
template: '#cpn',
data(){
return{
name:'我是子组件的name'
}
},
methods:{
showMessage(){
console.log("showMessage!!!");
}
}
}
}
})
</script>
</body>
</html>
执行结果
1.6 子组件直接访问父组件
如果想在子组件中直接访问父组件,可以通过$parent。
注意事项
- 尽管在Vue开发中,允许通过$parent来访问父组件,但是在真实开发中尽量不要这样做。
- 子组件应该尽量避免直接访问父组件的数据,因为这样耦合度太高了。
- 如果将子组件放在另外一个组件之内,很可能该父组件没有对应的属性,往往会引起问题。
- 另外,更不好做的是通过$parent直接修改父组件的状态,那么父组件中的状态将变得飘忽不定,很不利于调试和维护。
代码示例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>组件访问(子访问父)</title>
</head>
<body>
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</template>
<template id="ccpn">
<div>
<h2>我是子组件</h2>
<button @click="btnClick">按钮</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: '#app',
data: {
message: 'hello Vue.js!!!'
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
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>
执行结果