13.父组件和子组件
组件与组件之间存在层级关系,如下构造父组件与子组件
<body>
<div id="text">
<!-- 使用父组件 -->
<father_cnp></father_cnp>
</div>
<script src="../js/vue.js"></script>
<script>
//构造子组件构造器
//注意这里需要先构造子组件构造器
const cnp2 = Vue.extend({
template: `<div><h2>vue子组件</h2></div>`
})
//构造父组件构造器
const cnp1 = Vue.extend({
template: `<div>
<h2>vue父组件</h2>
<son_cnp></son_cnp>
</div>
`,
//在父组件构造器中注册子组件
components: {
son_cnp: cnp2
}
})
var em = new Vue({
el: '#text',
data: {
message: 100
},
//注册父组件
components: {
father_cnp: cnp1
}
})
</script>
</body>
运行结果:
2.注册组件语法糖
不需要使用extend构造组件构造器,使用如下方法注册(全局组件),
Vue.component('cpn',{
template: `<div>
<h2>这是一个组件</h2>
</div>`
})
省略extend,(其实vue已经帮我们构造好了)
与data同级注册局部组件(省略extend)
var em = new Vue({
el: '#text',
data: {
message: 100
},
//注册局部组件
components: {
'cpn3': {
template: `<div>
<h2>这是一个私有组件</h2>
</div>`
},
}
})
组件能否访问vue实例中data的数据
!不能直接访问,vue组件有自己存储数据的地方
!组件对象里面也有一个data属性(也可以有methods等属性),只是这个data属性必须是一个函数,而且这个函数返回一个对象,对象内部保存数据
当data属性为一个函数时,各个组件调用data中的数据是不会相互影响的,
例如:
<body>
<div id="text">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>1.当前计数{{sum}}</h2>
<button @click="add">+1</button>
<button @click="sub">-1</button>
</div>
</template>
<script src="js/vue.js"></script>
<script>
Vue.component('cpn', {
template: '#cpn',
data() {
return {
sum: 0
}
},
methods: {
add() {
this.sum++
},
sub() {
this.sum--
}
}
}
)
var em = new Vue({
el: '#text',
data: {
message: 100
},
})
</script>
</body>
运行结果如下,当你点击每一个组件计数器时是不会影响其他组件计数器的值的
但是当你这样定义data时,各个组件调用data中数据时会产生连锁反应,
例如:
<body>
<div id="text">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>1.当前计数{{sum}}</h2>
<button @click="add">+1</button>
<button @click="sub">-1</button>
</div>
</template>
<script src="js/vue.js"></script>
<script>
const obj ={
sum:0
}
Vue.component('cpn', {
template: '#cpn',
data() {
return obj
},
methods: {
add() {
this.sum++
},
sub() {
this.sum--
}
}
}
)
var em = new Vue({
el: '#text',
data: {
message: 100
},
})
</script>
</body>
运行结果如下,当你点击一个组件计数器时,会同时影响其他组件计数器数据,产生连锁反应!!!
14.父子组件传值
- 父向子传值
父组件通过props向子组件传递数据
例子:
<body>
<div id="text">
//v-bind绑定传递过来的数据名,用子组件定义的数据名进行接收
<cpn v-bind:sonmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<h2>父向子组件传递的数据为:{{sonmessage}}</h2>
</div>
</template>
<script src="js/vue.js"></script>
<script>
//子组件
const cpn = {
template: "#cpn",
props: ['sonmessage'],
data() {
return {}
}
}
//root组件(父组件)
var em = new Vue({
el: '#text',
data: {
message: '你好'
},
components: {
cpn
}
})
</script>
</body>
运行结果:
Props不仅仅可以是数组的形式,还可以是对象的形式
例如:
props:{
Sonmessage: string //限制传递过来的数据的类型
default: ‘aaa’ //默认值,当传递过来的数据为空时,则采取默认值
require: true //当require为true时,限制必须传递数据,否则报错
}
当需要对数据类型进行验证时,就需要props对象类型了,可以验证的类型有
- string
- array
- Boolean
- Object
- Date
- Number
- Function
- Symbol
注意:通过props接收的数据不推荐直接在子组件中进行修改,(或者说通过props接收的数据为只读数据)
- 子向父传值
子组件通过自定义事件向父组件传递数据
在子组件中通过$emit来触发事件,父组件通过v-on来监听子组件事件
<body>
<div id="text">
<!-- 父组件接收子组件传递的数据 -->
<cpn @sentdata="receivedata"></cpn>
</div>
<template id="cpn">
<div>
<button v-for="item in category" @click="btnclick(item)">{{item.name}}</button>
</div>
</template>
<script src="js/vue.js"></script>
<script>
//子组件向父组件传递数据
const cpn = {
template: "#cpn",
data() {
return {
category: [
{ id: "a", name: "海贼王" },
{ id: "b", name: "火影忍者" },
{ id: "c", name: "死神" }
]
}
},
methods: {
btnclick(item) {
//发射自定义事件
this.$emit('sentdata', item)
}
}
}
//root组件(父组件)
var em = new Vue({
el: '#text',
data: {
message: '你好'
},
components: {
cpn
},
//接收子组件通过自定义事件传递过来的值
methods: {
receivedata(item) {
console.log('receivedata', item);
}
}
})
</script>
</body>
3.
父子组件访问
父组件通过$refs 访问子组件
<body>
<div id="text">
<cpn ref="aaa"></cpn>
<button @click="btnclick">父访问子组件</button>
</div>
<template id="cpn">
<div>
<h2>这是一个子组件</h2>
</div>
</template>
<script src="js/vue.js"></script>
<script>
var em = new Vue({
el: '#text',
data: {
message: '你好'
},
methods: {
btnclick(){
//打印子组件内容
console.log(this.$refs);
}
},
components : {
cpn: {
template: "#cpn"
}
}
})
</script>
</body>
打印结果
子访问父组件
子组件通过this.$parent访问父组件(距离最近的子组件)
访问根组件
通过this.$root访问根组件vue实例
15.watch监听器:监听数据变化并作出反应
语法格式:
watch: {
数据名称(newvalue,oldvalue){
事件}
16.插槽slot
插槽的目的是让我们原来的设备具备更多的扩展性
组件的插槽让我们封装的组件更加有扩展性,一旦我们预留了插槽,就可以让使用者根据自己的需求,决定插槽中插入什么内容
例子:
<body>
<div id="text">
<cpn>
<button>第一个组件使用插槽</button>
</cpn>
<cpn>
<input type="text" value="第二个组件使用插槽" id="">
</cpn>
<cpn>
<p>第三个组件使用插槽</p>
</cpn>
<!-- 第四个组件没有使用预留插槽 -->
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>这是一个子组件</h2>
<!-- 预留插槽 -->
<slot></slot>
</div>
</template>
<script src="js/vue.js"></script>
<script>
var em = new Vue({
el: '#text',
data: {
message: '你好'
},
components : {
cpn: {
template: "#cpn"
}
}
})
</script>
</body>
运行结果: