一、自定义组件
1、自定义组件的意义
当一个布局或模块需要在很多地方使用时,我们就会对这个布局的代码和样式进行多次复制粘贴,所以选择对这个布局进行封装可以方便我们在工作中多次使用。
2、组件名
定义组件名的方式:
① 短横线分隔命名(kebab-case)
Vue.component('my-component-name', { /* ... */ })
当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>。
注意:使用短横线分隔命名需要使用单引号引起来
② 首字母大写命名(PascalCase)
Vue.component('MyComponentName', { /* ... */ })
当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name> 和 <MyComponentName> 都是可接受的
3、局部组件(components{ })
局部注册的组件,只能在当前Vue实例中使用,并且在其子组件中不可用。
在components选项中定义局部组件。每个组件就是一个小型的Vue实例,它里面除了不能设置el选项,其他选项它都有。
组件名称:自定义,可以使用驼峰命名方式或者短横线的命名方式,但是需要注意的是如果应用到DOM中,就只能用短横线命名的方式,否则就会报错。注意组件的名称不要跟原生html元素重名。
template选项:定义组件的模板。模板中必须包含一个根标签。
props选项:定义组件标签上的属性。驼峰命名法的 prop 名(postTitle)需要使用其等价的短横线分隔命名法(post-title)命名。注意:props是只读的,不能修改(解决办法:在data中对props接收到的数据进行中转)。
- props的值可以是一个字符串数组,里面定义每一个标签属性名称,这是简单用法,不能对属性做严格的验证。例如:props:["count"]。
- props的值可以是一个对象,里面定义每个标签属性名称,以及对应的类型。例如:props:{ count:Number }。
- props的值可以是一个对象,里面定义的每个标签属性名称也可以是一个对象,在这个对象里面定义该属性的完整信息。type 定义类型,required 非空,default 默认值。例如:props: {count: { type: Number, required: true, default: 1 } }
data:定义组件的数据。注意:Vue实例的data选项可以是一个对象,也可以是一个方法,由该方法返回一个对象。 但是在组件中,data选项必须是一个方法,由该方法返回一个对象。因为组件可能会使用很多次,如果data选项是对象的话,会导致多个组件使用了同一份数据。
new Vue({
el: '#app',
components: {
// 自定义组件名
'component-name': {
template:`
<div>
<p> hello </p>
</div>
`,
props:{ },
data() {
return { }
}
}
}
})
<div id="app">
<component-name> </component-name>
</div>
使用示例 counter组件
<div id="app">
<ul class="list">
<li v-for="(item,index) in list" :key="index">{{item.label}}--{{item.count}}</li>
</ul>
<b-counter v-for="(item,index) in list" :key="index" :label="item.label" :count="item.count"
@synccount="synccount(index,$event)"></b-counter>
</div>
new Vue({
el: '#app',
// 定义数据
data: {
list: [
{ label: '衣服',count: 5 },
{ label: '裤子',count: 8 },
{ label: '鞋子',count: 3 },
{ label: '袜子',count: 10 }
]
},
methods: {
synccount(index, e) {
// 同步更新商品的数量
this.list[index].count = e
}
},
// 注册组件
components: {
'b-counter': {
template: `
<div class="counter">
<div class="label">{{label}}</div>
<div class="btns">
<button @click="myCount--" :disabled="myCount===minCount">-</button>
<input readonly class="text" type="text" :value="myCount">
<button @click="myCount++" :disabled="myCount===maxCount">+</button>
</div>
</div>
`,
//props选项
props: {
// 文本
label: {
type: String,
// 允许为空
required: false,
},
// 数量
count: {
type: Number,
// 非空
required: true
},
// 最大值
maxCount: {
type: Number,
default: 999
},
// 最小值
minCount: {
type: Number,
default: 1
}
},
//定义数据
data() {
return {
// 将props接收到的count,中转给myCount
myCount: this.count
}
},
// 监听器
watch: {
// 侦听myCount是否发生变化
myCount(val) {
// 触发一个自定义事件synccount,将count的最新值作为事件对象传出去
this.$emit('synccount', val)
}
}
}
}
})
实现效果:
4、全局组件
全局注册的组件可以用在任何新创建的 Vue 根实例 (new Vue) 的模板中。
使用Vue.component来创建全局组件。Vue.component的第一个参数就是组件名。
new Vue({
el: '#app',
components: {
// 自定义组件名
'component-name': {
template:`
<div>
<p> hello </p>
</div>
`,
props:{ },
data() {
return { }
}
}
}
})
<div id="app">
<component-name> </component-name>
</div>
使用示例 评分组件
<div id="app">
<ul class="list">
<li v-for="(item,index) in list" :key="index">{{item.title}}--{{item.value}}</li>
</ul>
<b-star v-for="(item,index) in list" :key="index"
:title="item.title" :value="item.value" @syncvalue="syncvalue(index,$event)"></b-star>
</div>
// 注册全局组件
Vue.component('b-star', {
//模板
template:`
<div class="star">
<div class="title">{{title}}</div>
<div>
<i v-for="item in 10" :key="item" class="iconfont"
:class="item<=myValue?'icon-xingxing':'icon-star'"
@mouseenter="enter(item)" @mouseleave="leave(item)" @click="okValue=item"></i>
</div>
</div>
`,
// 定义组件的标签属性
props:{
title:{
type:String,
required:false
},
value:{
type:Number,
default:0
}
},
// 数据
data() {
return {
// 中转props传进来的value值
myValue:this.value,
// 定义一个确定值
okValue:this.value
}
},
// 方法
methods: {
// 鼠标进入时,调用的方法
enter(val){
this.myValue = val
},
// 鼠标离开时,调用的方法
leave(val){
this.myValue = this.okValue
}
},
// 监听器
watch:{
// 侦听okValue的变化
okValue(val){
// 触发自定义事件
this.$emit('syncvalue',val)
}
}
})
new Vue({
el:'#app',
data:{
list:[
{
title:'产品质量',
value:5
},
{
title:'物流速度',
value:7
},
{
title:'客服态度',
value:2
}
]
},
methods: {
// 同步评分
syncvalue(index,e){
this.list[index].value = e
}
},
})
实现效果:
二、自定义时间$emit
$emit()用于触发自定义事件。注意:事件名称中不能采用大写字母。
// 触发一个自定义事件,事件名称是synccount,将val作为事件对象传出去
this.$emit('synccount',val)
<component-name @synccount="synccount(doSomething)"> </component-name>
三、组件中的插槽
slot 用于在组件的内部定义插槽,组件标签之间的所有html内容,会在插槽所在位置呈现。
<div id="app">
<b-tab :list="list" :active="activeIndex">
<h2>全国著名小吃</h2>
</b-tab>
</div>
Vue.component('b-tab', {
template: `
<div class="tab">
<slot></slot>
<ul class="titles">
<li @click="activeIndex=index" :class="{active:activeIndex===index}" v-for="(item,index) in list" :key="index">{{item.title}}</li>
</ul>
<ul class="contents">
<li v-show="activeIndex===index" v-for="(item,index) in list" :key="index">{{item.content}}</li>
</ul>
</div>
`,
props: ['list', 'active'],
data() {
return {
activeIndex: this.active
}
}
})
new Vue({
el: '#app',
data: {
// 高亮索引
activeIndex: 0,
list: [
{
title: '北京',
content: '北京的糖葫芦真好吃'
},
{
title: '南京',
content: '南京的盐水鸭真好吃'
},
{
title: '武汉',
content: '武汉的热干面真好吃'
},
{
title: '长沙',
content: '长沙的臭豆腐真好吃'
}
]
}
})
实现效果: