一、組件
(一)什么是組件
組件(Component)是 Vue.js最強大的功能之一。組件可以擴展 HTML元素,封裝可重用的代碼組件是自定義元素(對象)。
(二)創建組件的兩種方式
官方推薦組件標簽名是使用-
連接的組合詞,例如:<my-hello></my-hello>
。
1、使用構造器創建組件
使用這種方式創建組件首先需要使用Vue.extend()
創建一個組件構造器,然后使用Vue.component(標簽名,組件構造器)
,根據組件構造器來創建組件。
//1.創建構造器
var MyComponent=Vue.extend({
template:'<h3>Hello World</h3>'
});
//2.創建組件
Vue.component('my-hello',MyComponent);
//3.使用組件
<div id="app">
<my-hello></my-hello>
</div>
這種創建組件的方式比較麻煩,使用的較少。
2、直接創建組件
使用Vue.component(標簽名,組件模板)
,根據組件構造器來創建組件。
//1.創建組件
Vue.component('my-world', {
template: '<h2>hello vue.js</h2>'
});
//2.使用組件
<div id="app">
<my-world></my-world>
</div>
(三)組件的分類
組件分為全局組件和局部組件。
1、全局組件
使用Vue.component()
創建的組件都是全局組件。這樣的組件在任何組件內都能使用。上面我們創建就是全局組件。
2、局部組件
局部組件一般都是定義在實例的選項中,稱為實例的子組件。相應的,實例也被稱為父組件。
//1.定義組件
new Vue({
el: '#app',
components: {
dawei: {
template: '<h2>my name is dawei</h2>'
}
}
});
//2.使用組件
<div id="app">
<dawei></dawei>
</div>
(四)引用模板
很多時候我們的template
模板中需要存放很多標簽內容,這樣的話寫起來會很麻煩。這時候我們可以使用template
標簽。
用法如下:
<template id="wbs"> //使用template標簽
<div>
<h2>hello {{msg}}</h2>
<ul>
<li v-for="value in arr">
{{value}}
</li>
</ul>
</div>
</template>
new Vue({
el: '#app',
components: {
'my-dawei': {
template: '#wbs', //選擇template標簽
data() {
return {
msg: 'vue.js',
arr: ["a", "b", "c", "d"]
}
}
}
}
});
這里涉及到的幾個知識點得著重提一下:
在
template
模板中,所有的元素必須放置在一個根元素中,要不然會報錯。例子中我們將元素放置在了<div>
標簽中。組件中的
data
選項必須是一個函數類型,使用return
返回所有的數據。
(五)動態組件
很多時候項目中需要在某一個地方動態的使用不同的組件,這時候就需要使用動態組件。
動態組件的使用需要綁定is
屬性:
<component :is="flag"></component>
簡單示例:
//點擊按鈕顯示不同的組件
<div id="app">
<button type="button" @click="flag='my-a'">顯示a組件</button>
<button type="button" @click="flag='my-b'">顯示b組件</button>
<component :is="flag"></component> //傳入flag
</div>
new Vue({
el: '#app',
data: {
flag: 'my-a' //給flag賦值
},
components: {
'my-a': {
template: '<p>我是a組件</p>',
},
'my-b': {
template: '<p>我是b組件</p>'
}
}
});
(六)keep-alive組件
使用keep-alive
組件緩存非活動組件,可以保留狀態,避免重新渲染,默認每次都會銷毀非活動組件并重新創建。
使用范例:
<keep-alive>
<component :is="flag"></component>
</keep-alive>
<div id="app">
<button type="button" @click="flag='my-x'">x</button>
<button type="button" @click="flag='my-y'">y</button>
<keep-alive>
<component :is="flag"></component>
</keep-alive>
</div>
new Vue({
el: '#app',
data: {
flag: 'my-x'
},
components: {
'my-x': {
template: '<p>{{x}}</p>',
data() {
return {
x: Math.random()
}
}
},
'my-y': {
template: '<p>{{y}}</p>',
data() {
return {
y: Math.random()
}
}
}
}
});
這樣的話,第一次產生的隨機數就會被緩存,再次切換的時候也不會發生改變。
二、 組件間數據傳遞
(一)父子組件
在一個組件內部定義另一個組件,那么這對組件稱為父子組件。子組件只能在父組件內部使用。默認情況下,每個組件實例的作用域是獨立的,子組件無法訪問父組件中的數據,同樣,父組件也無法訪問子組件中的數據。
<div id="app">
<my-a></my-a>
<!-- 父組件 -->
</div>
<template id="a">
<div>
<p>{{msg}}</p>
<my-b></my-b> <!-- 在父組件中調用子組件 -->
</div>
</template>
<template id="b">
<div>
<p>{{mydata}}</p>
</div>
</template>
<script>
new Vue({ //根組件
el: '#app',
components: { //子組件寫在components選項中
"my-a": { //b組件的父組件
template: "#a",
data() {
return {
msg: '我是父組件',
}
},
components: { //子組件寫在父組件的components選項中
"my-b": {
template: "#b",
data() {
return {
mydata: "我是子組件"
}
}
}
}
}
}
});
</script>
(二)組件間數據傳遞(通信)
1、子組件訪問父組件的數據
步驟:
- a、調用子組件時,綁定想要獲取的父組件中的數據
- b、在子組件內部,使用props選項聲明獲取的數據,即接收來自父組件的數據
改進上面的例子:
<div id="app">
<my-a></my-a>
</div>
<template id="a">
<div>
<p>{{msg}}</p>
<p>這是要傳遞給子組件的值:{{myname}}</p>
<my-b :name="myname"></my-b> <!-- 綁定子組件想要獲取的數據 -->
</div>
</template>
<template id="b">
<div>
<p>{{mydata}}</p>
<p>這是父組件傳遞過來的數據:{{name}}</p>
</div>
</template>
<script>
new Vue({
el: '#app',
data: {},
components: {
"my-a": {
template: "#a",
data() {
return {
msg: '我是a組件',
myname: '子組件b你好,我是父組件a'
}
},
components: {
"my-b": {
template: "#b",
data() {
return {
mydata: "我是b組件"
}
},
props: ["name"] //子組件使用props聲明想要獲取的數據
}
}
}
}
});
</script>
2、父組件訪問子組件的數據
步驟:
- a 在子組件中使用vm.$emit(事件名,數據)觸發一個自定義事件,將數據發送給父組件,事件名自定義
- b 父組件在使用子組件的地方監聽子組件觸發的事件,并在父組件中定義方法,用來獲取數據
//子組件‘my-b’內部
methods:{
send(){//使用$emit()觸發一個事件,發送數據,this指當前子組件實例
this.$emit('e-world', this.senddata);
}
}
//在調用子組件的地方監聽子組件觸發的事件,調用自己的方法獲取數據
<my-b @e-world="getData"></my-b>
methods: {
getData(data) { //參數是子組件傳遞過來的數據
this.revicedata = data;
}
}
3、單向數據流
props是單向數據綁定的,當父組件數據發生變化時,將傳遞給子組件,但是不會反過來。而且不允許子組件直接修改父組件中的數據,強制修改會報錯。
解決方案:
- 如果子組件想把它作為局部數據來使用,可以將數據存入另一個變量中再操作,不影響父組件中的數據
- 如果子組件想修改數據并且同步更新到父組件,兩個方法:
- 使用.sync顯式地觸發一個更新事件(1.0版本中支持,2.0版本中不支持,2.3版本又開始支持)
//使用.sync
<my-b :name.sync="myname"></my-b>
//子組件修改父組件傳入的值name,觸發update更新事件
this.$emit('update:name', "vuejs");
- 可以將父組件中的數據包裝成對象,然后在子組件中修改對象的屬性(因為對象是引用類型,指向同一個內存空間),推薦使用這種方式。
data() {
return { //將要傳遞的數據放入message對象中
message: {
hello: '子組件b你好,我是父組件a'
}
}
}
<my-b :message="message"></my-b> //傳遞這個對象給子組件
methods: { //在子組件內部修改這個值,這樣就會同步傳遞給父組件。
edit() {
this.message.hello = "hahahahh";
}
}
4. 非父子組件間的通信
非父子組件間的通信,可以通過一個空的Vue實例作為中央事件總線(事件中心),用它來觸發事件和監聽事件,從而實現非父子組件間的通信。
var Event = new Vue(); //空vue實例
methods: {
send() { //觸發emit事件
Event.$emit("hello", this.asmsg);
}
}
mounted() { //在子組件的鉤子函數中監聽該事件
Event.$on('hello', data => { //獲取值
this.bsmsg = data;
})
}
(三)slot內容分發
用來獲取組件中的原內容
var vm = new Vue({
el: '#app',
components: {
'my-hello': {
template: '#hello'
}
}
});
<div id="app">
<my-hello>hello vue.js</my-hello>
</div>
<template id="hello">
<div>
<slot>如果沒有原內容,則顯示該內容</slot>
</div>
</template>
如果組件標簽中沒有內容就會顯示slot中的內容,這也就是所謂的單個插槽。
還可以對顯示的內容進行分組,這就是具名插槽,可以操作標簽組中的內容:
<div id="app">
<my-hello>
<ul slot="s1">
<li>aaa</li>
<li>bbb</li>
<li>ccc</li>
</ul>
<ol slot="s2">
<li>111</li>
<li>222</li>
<li>333</li>
</ol>
</my-hello>
</div>
<template id="hello">
<div>
<slot name="s2"></slot> //為插槽指定名稱 將名為s2的內容放置在這里
<p>hello vue.js</p>
<slot name="s1"></slot> //將名為s1的內容放置在這里
</div>
</template>
這樣,就可以對組件中的內容實時操作。