有时候一组HTML代码可能会在多出使用(比如页眉页脚)。如果我们把这样的代码到处都进行复制粘贴,虽然一时方便了,但后期维护代价非常高,而且代码重复性较高,导致可读性也有所下降。这时候我们就可以将这些重复代码封装成一个组件,以后在使用的时候只需要写上自定义组件的标签即可直接调用。
自定义组件Vue.component('标签名', {})
一般我们的HTML模板和data
数据以及方法之类的都会放在组件的大括号中。下面我会创建一个可以记录点击次数的自定义组件。
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>dometitle>
head>
<body>
<component-button>component-button>
<component-button>component-button>
<component-button>component-button>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建自定义组件
Vue.component('component-button', {
// 自定义组件中 template 负责放入HTML代码
template: '',
// data功能和Vue实例中的相同, 但自定义组件中的data是一个函数! 并且用返回值的方式定义属性,如下:
// 有一定js基础或者看过我之前博客的一定知道data(){}是js创建函数data:function(){}的简写版!
data(){
return {
num: 0
}
}
});
// 创建Vue实例
new Vue({
el: '#app'
});
script>
html>
注意:创建自定义组件无论是否用到了Vue实例,都必须要有Vue实例,并且只能在Vue实例绑定的标签内才能使用自定义组件!!!
上述代码中包含了template
和data
的使用方式。但需要注意在自定义组件中设置属性和在Vue实例中有所不同! 为什么我们要在自定义组件中创建属性,而不是使用Vue实例中的属性?因为如果我们多次调用自定义组件,使用组件内的属性是互相独立互不干扰的!
给组件添加属性
有时候我们需要所有组件使用的属性是共享的,需要使用Vue实例中的属性,而不是在自定义组件中创建的,这时候我就就可以通过自定义组件中的props
创建属性接收值,而调用的时候我们只需要向props
创建的属性中传入值即可。
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定义组件添加属性title>
head>
<body>
<props-text :vfor="vfor_dict">props-text>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建自定义组件
Vue.component("props-text", {
// props中设定的是标签中的属性,我们从标签中接收此属性传入的值即可
props: ["vfor"],
// 如果需要写多行HTML代码,不能使用单双引号,而是使用反单引号``(英文状态下Tab键上方的键)
// 多行时候需要注意的是自定义组件遵循单一根元素(根元素只能有一个,多了会出现问题)
// 下方我的根元素就是table这里不能出现和table同级的元素!!!
template: `
称号
名字
{{key}}
{{value}}
`,
})
// 创建Vue实例
new Vue({
el: '#app',
data: {
vfor_dict: {
人类懂王 : '川建国',
祖传百万 : '孙宇晨',
这饭真香 : '王境泽',
行业冥灯 : '罗老师'
}
},
methods: {
}
});
script>
html>
添加的属性不仅能用于绑定Vue中的属性,主要功能是用于传值,将组件外的值传入组件内!然后组件可以根据传入的值做一些变化。
需要注意的是:多行时候需要注意的是自定义组件遵循单一根元素,在上述代码中我也有提及。如果有多个根元素,很可能会发生只执行首个根元素而又没有任何报错的情况!
传递事件
上述给组件添加属性的功能主要是用于将自定义组件中的值传入组件内,但如果我们需要将组件内的值传入组件外呢?这时候就需要使用this.$emit
函数来实现
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>dome21title>
head>
<body>
<component-input v-for='i in vfor_dict' :value='i' @v='item'>component-input>
{{vdata}}
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建自定义组件
Vue.component("component-input", {
// 创建一个value的属性用于接收参数
props: ['value'],
template: `
`,
methods: {
// 每次点击多选框时都会触发此方法
transmit(){
// 此方法定义了一个属性名为v,并将其参数通过v传出
this.$emit("v", this.value.name)
}
}
})
// 创建Vue实例
new Vue({
el: '#app',
data: {
vfor_dict: [
{title: '人类懂王', name : '川建国'},
{title: '祖传百万', name : '孙宇晨'},
{title: '这饭真香', name : '王境泽'},
{title: '行业冥灯', name : '罗永浩'}
],
vdata : []
},
methods: {
item(value){
// indexOf方法用于判断数据是否已经存在
// 因为上述多选框选择和取消选择都会传出相同的参数,我用indexOf方法判断是否当前选择状态,如果vdata中还没有此数据,反之亦然
index = this.vdata.indexOf(value)
// 如果数据存在,indexOf这会返回数据的角标,如果不存在则会返回-1,根据此可以做一个判断,用来添加或者删除数据
if (index>=0) {
this.vdata.splice(index, 1)
} else {
this.vdata.push(value)
}
}
}
});
script>
html>
自定义组件的v-model
剖析v-model原理
组件除了向内或者向外绑定外,还可以使用v-model
进行双向绑定。不过这里我们需要对v-model
深入剖析,v-model
可以看成为v-bind
和v-on
的组合体。就如同下述代码中使用v-bind
+v-on
绑定的输入框和v-model
绑定的输入框达到了相同的效果!
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定义组件内的v-modeltitle>
head>
<body>
<input type="text" v-model="vmodel">
<p>{{vmodel}}p>
<input type="text" :value="vbind" @input="vbind=$event.target.value">
<p>{{vbind}}p>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建Vue实例
new Vue({
el: '#app',
data: {
vmodel: '',
vbind: ''
}
});
script>
html>
正如我上述例子中v-model
会默认绑定value属性(prop)和input方法(event)。(不过在碰到单选、多选、下拉菜单等一些特殊情况也会绑定其他的属性和方法,如果想了解可以看我的另一篇专门介绍v-bind,v-on,v-model的博客)
当犹豫我们是自定义组件,v-model
则只会默认绑定value属性(prop)和input方法(event)
使用示例
从上述v-model原理中我们可以看出v-model自动绑定属性(prop)和方法(event)限制较大,如果我们在自定义组件中想要v-model绑定指定的属性(prop)和方法(event)需要在自定义组件中添加一个model属性进行指定!
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定义组件的v-model_2title>
head>
<body>
<use-model v-model="vdata">use-model>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建自定义组件
Vue.component('use-model', {
props: ['vitem'],
model: {
// 绑定的属性和上述props中对应
prop: 'vitem',
// 绑定的事件名
event: 'item-changed'
},
template: `
{{vitem}}
`,
methods: {
sub(){
// 这里绑定的事件名和model中事件名是一个
this.$emit('item-changed', this.vitem-1)
},
add(){
this.$emit('item-changed', this.vitem+1)
}
}
})
// 创建Vue实例
new Vue({
el: '#app',
data: {
vdata : 0
}
});
script>
html>
上述程序我们先从自定义组件中的模板开始看,如果我们点击了-或者+,则会触发执行相应的方法,而上述传递事件中我们讲过,this.$emit
会向外在指定的事件名中传入值,这时候,我就让其想item-changed
(这个名字是可以自己随意命名的)中传值,而这个事件名刚好是被v-model
所绑定,一旦被触发这个事件,则其中的值则会传入v-model
绑定的属性中。这样就刚好完成了我们的双向绑定!
上述我只是使用了v-model其中一种的使用方法,但v-model还能玩出很多其他花样。
插槽
有时候一个组件,可能其中绝大部分都相同,只有极个别的地方文本或者标签不同,这时候我们就可以用到自定义组件中的插槽来完成这个需求。
当我们使用单个插槽时,非常方便,只需要在需要插入文本或者标签的自定义组件的模板中写入
标签,然后在调用的自定义组件的时候,把我们需要插入的文本或者标签直接卸载自定义组件中即可。如果我们设置了多个插槽,这时候则需要name属性来指定插入那个插槽,这时候调用的时候,我们必须要使用标签来指定slot绑定的属性名。如下
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定义组件的插槽title>
head>
<body>
<use-slot url="https://www.baidu.com">
<template v-slot:name>
<h6>百度一下h6>
template>
<template v-slot:remarks>
<span>比360和搜狗好用太多了span>
template>
use-slot>
<use-slot url="https:cn.bing.com">
<template v-slot:name>
<h1>必应h1>
template>
<template v-slot:remarks>
<span>蛮不错的span>
template>
use-slot>
div>
body>
<script src="https://cdn.jsdelivr.net/npm/vue">script>
<script>
// 创建自定义组件
Vue.component('use-slot', {
props:['url'],
// 如果有多个插槽标签 ,最好在插槽中添加一个name属性,方便在插入时插到正确的位置
template:`
`
})
// 创建Vue实例
new Vue({
el: '#app'
});
script>
html>