使用意義
有時候一組HTML代碼可能會在多出使用(比如頁眉頁腳)。如果我們把這樣的代碼到處都進行復制粘貼,雖然一時方便了,但後期維護代價非常高,而且代碼重複性較高,導致可讀性也有所下降。這時候我們就可以將這些重複代碼封裝成一個組件,以後在使用的時候只需要寫上自定義組件的標籤即可直接調用。
基礎使用
自定義組件Vue.component('標籤名', {})
一般我們的HTML模板和data
數據以及方法之類的都會放在組件的大括號中。下面我會創建一個可以記錄點擊次數的自定義組件。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>dome</title>
</head>
<body>
<div id ="app">
<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: '<button @click="num+=1">當前點擊了{{num}}次</button>',
// 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
創建的屬性中傳入值即可。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定義組件添加屬性</title>
</head>
<body>
<div id ="app">
<!-- vfor是自定義標籤中通過props定義的一個屬性,我們需要通過此屬性傳入值 -->
<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: `
<table>
<tr>
<td>稱號</td>
<td>名字</td>
</tr>
<tr v-for="(key, value) in vfor">
<td>{{key}}</td>
<td>{{value}}</td>
</tr>
</table>
`,
})
// 創建Vue實例
new Vue({
el: '#app',
data: {
vfor_dict: {
人類懂王 : '川建國',
祖傳百萬 : '孫宇晨',
這飯真香 : '王境澤',
行業冥燈 : '羅老師'
}
},
methods: {
}
});
</script>
</html>
添加的屬性不僅能用於綁定Vue中的屬性,主要功能是用於傳值,將組件外的值傳入組件內!然後組件可以根據傳入的值做一些變化。
需要注意的是:多行時候需要注意的是自定義組件遵循單一根元素,在上述代碼中我也有提及。如果有多個根元素,很可能會發生只執行首個根元素而又沒有任何報錯的情況!
傳遞事件
上述給組件添加屬性的功能主要是用於將自定義組件中的值傳入組件內,但如果我們需要將組件內的值傳入組件外呢?這時候就需要使用this.$emit
函數來實現
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>dome21</title>
</head>
<body>
<div id ="app">
<!-- 先看創建自定義屬性後再看這個 -->
<!-- value用於傳入參數,他將遍歷出來的參數i傳入自定義組件中 -->
<!-- v用於接收參數,他接收自定義組件傳出的參數並在傳給item方法 -->
<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: `
<div>
<!-- checkbox是多選框 -->
<input type="checkbox" @input="transmit">
<label>{{value.title}}</label>
</div>
`,
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
綁定的輸入框達到了相同的效果!
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定義組件內的v-model</title>
</head>
<body>
<div id ="app">
<input type="text" v-model="vmodel">
<p>{{vmodel}}</p>
<!-- $event.target.value是獲取當前輸入框中的值 -->
<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屬性進行指定!
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定義組件的v-model_2</title>
</head>
<body>
<div id ="app">
<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: `
<div>
<button @click="sub"> - </button>
<span>{{vitem}}</span>
<button @click="add"> + </button>
</div>
`,
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還能玩出很多其他花樣。
插槽
有時候一個組件,可能其中絕大部分都相同,只有極個別的地方文本或者標籤不同,這時候我們就可以用到自定義組件中的插槽來完成這個需求。
當我們使用單個插槽時,非常方便,只需要在需要插入文本或者標籤的自定義組件的模板中寫入<slot></slot>
標籤,然後在調用的自定義組件的時候,把我們需要插入的文本或者標籤直接卸載自定義組件中即可。如果我們設置了多個插槽,這時候則需要name屬性來指定插入那個插槽,這時候調用的時候,我們必須要使用<template v-slot:name名>標籤來指定slot綁定的屬性名。如下
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>自定義組件的插槽</title>
</head>
<body>
<div id ="app">
<use-slot url="https://www.baidu.com">
<!-- 如果插槽中指定name屬性的標籤,必須使用<template v-slot:name名></template>包裹其想要插入的標籤或者文本!-->
<!-- 注意:這裏的template不能改變 -->
<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'],
// 如果有多個插槽標籤<slot></slot>,最好在插槽中添加一個name屬性,方便在插入時插到正確的位置
template:`
<div>
<a :href="url">
<slot name="name"></slot>
</a>
<span>備註: </span><slot name="remarks"></slot>
</div>
`
})
// 創建Vue實例
new Vue({
el: '#app'
});
</script>
</html>