一、全局组件和局部组件
全局组件:即可以在多个Vue实例中使用
Vue实例1
<div id="app">
<cpn></cpn>
</div>
Vue实例2
<div id="app2">
<cpn></cpn>
</div>
<script src="../js/vue.js"></script>
<script>
// 1.创建组件构造器
const cpnC = Vue.extend({
template: `
<div>
<h2>我是标题</h2>
<p>我是内容</p>
</div>`
})
//2.注册组件
Vue.component('cpn',cpnC)
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
}
})
const app2 = new Vue({
el: "#app2"
})
</script>
局部组件:只能在当前Vue实例中使用
使用:在Vue实例中使用components属性引用
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
},
components:{
cpn: cpnC
}
})
二、父组件和子组件
在一个组件中注册另一个组件,这个组件就可以称为另一个组件的父组件,注Vue实例可以看成一个root组件
<div id="app">
<cpn2></cpn2>
</div>
<script src="../js/vue.js"></script>
<script>
//1.创建第一个组件(子组件)
const cpnC1 = Vue.extend({
template: `
<div>
<h2>我是组件1</h2>
<p>哈哈哈</p>
</div>`
})
//1.创建第二个组件(父组件)
const cpnC2 = Vue.extend({
template: `
<div>
<h2>我是组件2</h2>
<p>呵呵呵</p>
<cpn1></cpn1>
</div>`,
components: {
cpn1: cpnC1
}
})
//可以看成root组件
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
},
components: {
cpn2: cpnC2
}
})
</script>
三、注册组件的语法糖写法
全局组件语法糖写法:
Vue.component('cpn1',{
template: `
<div>
<h2>我是组件1</h2>
<p>哈哈哈</p>
</div>`
})
局部组件语法糖写法:
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
},
components: {
'cpn2': {
template: `
<div>
<h2>我是组件2</h2>
<p>呵呵呵</p>
</div>`
}
}
})
四、组件模版抽离的写法
使用script标签或者template标签
1.script标签 text/x-template类型
<script type="text/x-template" id="cpn1">
<div>
<h2>我是标题1</h2>
<p>我是内容,哈哈哈</p>
</div>
</script>
2.template标签
<template id="cpn2">
<div>
<h2>我是标题2</h2>
<p>我是内容,呵呵呵</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1.注册一个全局组件
Vue.component('cpn',{
template: '#cpn2'
})
五、组件中的数据存放问题
1.组件中不能访问Vue实例中的data数据。但是在注册组件时,组件内可以使用data函数来定义数据
<div id="app">
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>{{title}}</h2>
<p>我是内容 哈哈哈</p>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
Vue.component('cpn',{
template: '#cpn',
data() {
return {
title: 'abc'
}
}
})
const app = new Vue({
el: "#app",
data: {
message: "你好啊!",
title: "我是标题"
}
})
</script>
2.组件中的data为什么必须是函数
下面是一个简单的计数器的例子,我们创建3个组件实例,data是函数使得这三个实例间的数据不会相互干扰,即改变其中一个并不会影响到其余组件实例
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
</div>
<template id="cpn">
<div>
<h2>当前计数:{{counter}}</h2>
<button @click="increment">+</button>
<button @click="decrement">-</button>
</div>
</template>
<script src="../js/vue.js"></script>
<script>
//1.注册组件
Vue.component('cpn',{
template: '#cpn',
data() {
return {
counter: 0
}
},
methods: {
increment(){
this.counter++
},
decrement(){
this.counter--
}
}
})
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
}
})
</script>
六、父子组件间的通信
在开发中,我们会在整个页面的大组件中向服务器请求许多数据,但是有些数据并非整个页面的大组件中要展示的,而是需要下面的子组件进行展示,这个时候,我们就需要将数据从大组件(父组件)传递给小组件(子组件)
父子组件间的通信方式
- 1.通过props向子组件传递数据 父传子
-
2.通过事件向父组件发送消息 子传父
父子.png
(一)props基本用法 父传子
<div id="app">
<cpn :cmovies="movies" :cmessage="message"></cpn>
</div>
<template id="cpn">
<div>
<ul>
<li v-for="item in cmovies">{{item}}</li>
</ul>
{{cmessage}}
</div>
</template>
<script src="../js/vue.js"></script>
<script>
const cpn = {
template: '#cpn',
!!!注意:此处未用驼峰写法 下面会有解释
props: ['cmovies','cmessage'],
data() {
return {}
},
methods: {
}
}
const app = new Vue({
el: "#app",
data: {
message: "你好啊!",
movies: ['海王','海贼王','海尔兄弟']
},
components: {
cpn
}
})
</script>
props除了数组外,还可以用对象
props: {
//1.类型限制
cmovies: Array,
cmessage: String,
//2.提供一些默认值,以及必传值
cmessage: {
type: String,
default: 'aaaaaa',
required: true
},
//类型是对象或者数组时,默认值必须是一个函数
cmovies: {
type: Array,
default() {
return []
}
}
},
注意:目前版本v-bind不支持驼峰标识
例如一下代码 当props中使用驼峰标识
props: {
cInfo: {
type: Object,
default() {
return {}
}
},
childMyMessage: ''
}
如果在绑定时v-bind中也使用驼峰,并不会识别
<div id="app">
<cpn :cInfo="info" :childMyMessage="message"></cpn>
</div>
这时需要做如下转换,就可以正常显示了
<div id="app">
<cpn :c-info="info" :child-my-message="message"></cpn>
</div>
(二)自定义事件 子传父
1.子组件自定义事件,然后发射给父组件:$emit()
<template id="cpn">
<div>
<button v-for="item in categories"
@click="btnClick(item)">
{{item.name}}
</button>
</div>
</template>
......
methods: {
btnClick(item) {
// console.log(item);
//子组件向父组件发射事件:自定义事件
this.$emit('item-click',item)
}
}
2.父组件模版中监听自定义事件
<!--父组件模版-->
<div id="app">
<!--父组件监听子组件发射的事件,并处理 此处不支持驼峰-->
<cpn @item-click="cpnClick"></cpn>
</div>
3.父组件中处理
methods: {
cpnClick(item) {
console.log('btnClick',item);
}
}
实际上就是使用v-on监听自定义事件
<!--父组件模版-->
<div id="app">
<!--父组件监听子组件发射的事件,并处理 此处不支持驼峰-->
<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>
//子组件
const cpn = {
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",
data: {
message: "你好啊!"
},
components: {
cpn
},
methods: {
cpnClick(item) {
console.log('btnClick',item);
}
}
})
</script>.
真实项目中,组件会封装在vue文件中,解析成render函数
(三)父子组件的访问方式
有时我们需要父组件直接访问子组件,或者子组件直接访问父组件,或者子组件访问跟组件,这是我们可以通过对象方式直接访问
- 父组件访问子组件:使用refs
- 子组件访问父组件:使用$parent
1.$children
一个父组件可能有多个子组件,所以获取的是个数组
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn></cpn>
<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: {
},
methods: {
btnClick() {
console.log(this.$children);
// this.$children[0].showMessage()
for (let c of this.$children){
console.log(c.name);
c.showMessage()
}
}
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是子组件的name'
}
},
methods: {
showMessage() {
console.log('showMessage');
}
}
}
}
})
</script>
2.$refs——对象类型,默认是空对象
在开发中,并不推荐使用children,当需求取指定第三个子组件时它是通过数组索引去取到,但是如果前面添加了一个新的子组件,我们还需要修改索引值。因此推荐使用$refs
我们只需要先给要取到的子组件加一个ref属性
<div id="app">
<cpn></cpn>
<cpn></cpn>
<cpn ref="aaa"></cpn>
<button @click="btnClick">按钮</button>
</div>
然后就可以根据属性名称找到对应的子组件
methods: {
btnClick() {
console.log(this.$refs.aaa.name);
}
},
就是键值对的形式
3.$parent:不推荐使用,会使子组件复用性变差,耦合度提高
4.$root:访问跟组件,即vue实例
<div id="app">
<cpn></cpn>
</div>
<tempalte id="cpn">
<div>
<h2>我是cpn组件</h2>
<ccpn></ccpn>
</div>
</tempalte>
<tempalte id="ccpn">
<div>
<h2>我是ccpn组件</h2>
<button @click="btnClick">按钮</button>
</div>
</tempalte>
<script src="../js/vue.js"></script>
<script>
const app = new Vue({
el: "#app",
data: {
message: "你好啊!"
},
components: {
cpn: {
template: '#cpn',
data() {
return {
name: '我是cpn组件的name'
}
},
components: {
ccpn: {
template: '#ccpn',
methods: {
btnClick() {
console.log(this.$parent);
console.log(this.$parent.name);
console.log(this.$root.message);
}
}
}
}
}
}
})
</script>
七、watch属性
watch来监测Vue实例上的数据变动
<div id="app">
<cpn :number1="num1"
:number2="num2"
@num1change="num1change"
@num2change="num2change">
</cpn>
</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,
},
data() {
return {
dnumber1: this.number1,
dnumber2: this.number2,
}
},
//使用watch监视dNumber值的改变
watch: {
dnumber1(newValue) {
this.dnumber2 = newValue * 100;
this.$emit('num1change',newValue);
},
dnumber2(newValue) {
this.dnumber1 = newValue / 100;
this.$emit('num2change',newValue);
}
}
}
}
})
</script>
网友评论