生命周期、插槽
介紹: 每個(gè) Vue 實(shí)例/組件在被創(chuàng)建時(shí)都要經(jīng)過一系列的初始化過程——例如剪菱,需要設(shè)置數(shù)據(jù)監(jiān)聽挠阁、編譯模板、將實(shí)例掛載到 DOM 并在數(shù)據(jù)變化時(shí)更新 DOM 等塑崖。同時(shí)在這個(gè)過程中的某些階段也會(huì)運(yùn)行一些特定的函數(shù),這些函數(shù)就被叫做生命周期鉤子,這給了用戶在不同階段添加自己的代碼的機(jī)會(huì)痛倚。
生命周期鉤子函數(shù)詳解(前面帶*號(hào)的生命周期表示非常用生命周期)
*beforeCreate: 在實(shí)例初始化之后规婆,數(shù)據(jù)觀測 (data observer) 和 event/watcher 事件配置之前被調(diào)用。該生命周期中無法訪問data 計(jì)算屬性 props
new Vue({
el: '#app',
data: {
name: '小明',
age: 18
},
beforeCreate() {
console.log(
'組件被實(shí)例化后觸發(fā),但是該生命周期階段數(shù)據(jù)還沒有初始化完成',
this,
this.$data // undefined
)
}
})
created: 在實(shí)例創(chuàng)建完成后被立即調(diào)用蝉稳。在這一步抒蚜,實(shí)例已完成以下的配置:數(shù)據(jù)觀測 (data observer),property 和方法的運(yùn)算耘戚,watch/event 事件回調(diào)嗡髓。然而,掛載階段還沒開始收津,$el
目前尚不可用饿这。(在這個(gè)生命周期中不要操作頁面中DOM元素,因?yàn)樵剡€沒有被渲染出來)
new Vue({
el: '#app',
data: {
name: '小明',
age: 18
},
created() {
console.log(
'組件被實(shí)例化后觸發(fā),data/計(jì)算屬性/watch/methods已經(jīng)配置完成,這里可以執(zhí)行一些初始網(wǎng)絡(luò)請求操作',
this,
this.$data
)
this.getDataFromServer()
},
methods: {
getDataFromServer() {
console.log('開啟網(wǎng)絡(luò)請求,向服務(wù)器請求數(shù)據(jù)')
}
}
})
*beforeMount: 在掛載(渲染)開始之前被調(diào)用: 相關(guān)的 render 函數(shù)首次被調(diào)用。
mounted: 實(shí)例被掛載后調(diào)用撞秋,這時(shí) el 被新創(chuàng)建的 vm.$el
替換了长捧。如果根實(shí)例掛載到了一個(gè)文檔內(nèi)的元素上,當(dāng) mounted 被調(diào)用時(shí) vm.$el
也在文檔內(nèi)吻贿。
注意: mounted 不會(huì)保證所有的子組件也都一起被掛載串结。如果你希望等到整個(gè)視圖都渲染完畢,可以在 mounted 內(nèi)部使用 vm.$nextTick
new Vue({
el: '#app',
data: {
name: '小明',
age: 18
},
beforeMount() {
console.log('實(shí)例將要被渲染',this.$refs) // this.$refs空對(duì)象
},
mounted() {
console.log('組件掛載完畢,掛載的意思就是實(shí)例第一次渲染完成,這里是一個(gè)操作DOM的好時(shí)機(jī)', this.$refs)
//this.$refs元素已經(jīng)獲取完畢
this.$nextTick(()=> {
// 這里所有子組件全部掛載完畢
})
}
})
beforeUpdate: 數(shù)據(jù)更新時(shí)調(diào)用舅列,發(fā)生在虛擬 DOM更新渲染之前奉芦。這里適合在更新之前訪問現(xiàn)有的 DOM,比如手動(dòng)移除已添加的事件監(jiān)聽器剧蹂。
updated: 由于數(shù)據(jù)更改導(dǎo)致的虛擬 DOM 重新渲染和打補(bǔ)丁声功,在DOM更新渲染之后會(huì)調(diào)用該鉤子。
當(dāng)這個(gè)鉤子被調(diào)用時(shí)宠叼,組件 DOM 已經(jīng)更新先巴,所以你現(xiàn)在可以執(zhí)行依賴于 DOM 的操作其爵。然而在大多數(shù)情況下,你應(yīng)該避免在此期間更改狀態(tài)伸蚯。如果要相應(yīng)狀態(tài)改變摩渺,通常最好使用計(jì)算屬性或 watcher 取而代之。
注意: updated 不會(huì)保證所有的子組件也都一起被重繪剂邮。如果你希望等到整個(gè)視圖都重繪完畢摇幻,可以在 updated 里使用 vm.$nextTick
new Vue({
el: '#app',
data: {
name: '小明',
age: 18
},
beforeUpdate() {},
updated: function () {
// 不能保證所有子組件更新完畢
this.$nextTick(function () {
//保證所有子組件更新完畢
})
}
})
beforeDestroy: 實(shí)例銷毀之前調(diào)用。在這一步挥萌,實(shí)例仍然完全可用绰姻。(在該生命周期中我們?nèi)匀豢梢栽L問,實(shí)例對(duì)象的屬性 方法 計(jì)算屬性 DOM節(jié)點(diǎn)等)。
該生命周期中應(yīng)該去做一些清理工作如:解綁計(jì)時(shí)器引瀑,取消網(wǎng)絡(luò)請求狂芋,清理掉哪些原生事件監(jiān)聽或者在mounted階段創(chuàng)建DOM節(jié)點(diǎn)
new Vue({
el: '#app',
data: {
name: '小明',
age: 18,
index: 0
},
mounted() {
// 模擬啟動(dòng)輪播圖
this.timer = setInterval(() => {
this.index++
}, 1000);
},
beforeDestroy() {
// 解綁計(jì)時(shí)器
clearInterval(this.timer)
}
})
*destroyed: 實(shí)例銷毀后調(diào)用。該鉤子被調(diào)用后憨栽,對(duì)應(yīng) Vue 實(shí)例的所有指令都被解綁帜矾,所有的事件監(jiān)聽器被移除,所有的子實(shí)例也都被銷毀屑柔。
activated: 被 keep-alive 緩存的組件激活時(shí)調(diào)用屡萤。
deactivated: 被 keep-alive 緩存的組件停用時(shí)調(diào)用。
<div id="app">
<keep-alive>
<component :is="show? 'demo': 'test'"></component>
</keep-alive>
<button @click="show = !show">show/hidden</button>
</div>
<script>
new Vue({
el: '#app',
data: {
show: true
},
components: {
demo: {
template: '<div>我是一個(gè)demo組件</div>',
mounted() {
console.log('組件第一次掛載完畢')
},
beforeDestroy() {
console.log('組件將要被銷毀')
},
activated() {
console.log('demo組件被激活')
},
deactivated() {
console.log('demo組件被停用')
}
},
test: {
template: '<h2 @click="count++">count{{count}}</h2>',
data() {
return {
count: 7
}
},
activated() {
console.log('test組件被激活')
},
deactivated() {
console.log('test組件被停用')
}
}
}
})
</script>
插槽
概念: Vue 實(shí)現(xiàn)了一套內(nèi)容分發(fā)的 API掸宛,為組件提供了一個(gè) <slot>
元素作為承載分發(fā)內(nèi)容的出口死陆。
語法:
<script>
Vue.component('login-component',{
template: `
<div>
<div>
// 分發(fā)內(nèi)容的內(nèi)容會(huì)被承載到這個(gè)slot標(biāo)簽位置
<slot></slot>
</div>
<p>
賬號(hào): <input>
</p>
<p>
密碼: <input type="password">
</p>
<button>登錄</button>
</div>
`
})
</script>
// 這時(shí)你可以這樣使用組件
<login-component>
<h2>科技</h2>
</login-component>
// 渲染在瀏覽器的結(jié)果是
<div>
<div>
<!--分發(fā)內(nèi)容的內(nèi)容會(huì)被承載到這個(gè)slot標(biāo)簽位置 -->
<h2>科技</h2>
</div>
<p>
賬號(hào): <input>
</p>
<p>
密碼: <input type="password">
</p>
<button>登錄</button>
</div>
注意:
如果
<login-component>
的 template 中沒有包含一個(gè)<slot>
元素,則該組件對(duì)稱標(biāo)簽內(nèi)部的任何內(nèi)容都會(huì)被拋棄旁涤。<slot>
元素內(nèi)部可以設(shè)置后備內(nèi)容,如果當(dāng)前組件對(duì)稱標(biāo)簽內(nèi)部沒有插入任何內(nèi)容的話,組件最終會(huì)渲染后備內(nèi)容
Vue.component('login-component',{
template: `
<div>
<div>
<slot>后備內(nèi)容</slot>
</div>
<p>
賬號(hào): <input>
</p>
<p>
密碼: <input type="password">
</p>
<button>登錄</button>
</div>
`
})
具名插槽
概念: 有時(shí)我們組件需要多個(gè)插槽翔曲∑认瘢可以將不同的組件插入到不同插槽內(nèi)部劈愚,實(shí)現(xiàn)方法是使用具名插槽,給組件中的<slot>
元素設(shè)置一個(gè)name屬性闻妓。在向具名插槽提供內(nèi)容的時(shí)候菌羽,我們可以在一個(gè) <template>
元素上使用 v-slot 指令將對(duì)應(yīng)的內(nèi)容插入到指定的<slot>
元素上
語法:
<script>
Vue.component('login-component',{
template: `
<div>
<div>
<slot>后備內(nèi)容</slot>
</div>
<p>
賬號(hào): <slot name="user"></slot>
</p>
<p>
密碼: <slot name="psd"></slot>
</p>
<button>登錄</button>
<slot></slot>
</div>
`
})
</script>
<login-component>
<h2>科技</h2>
<template v-slot:user>
<!-- 這里所有的內(nèi)容都會(huì)被插入到name="user" 插槽中 -->
<div>
123
</div>
</template>
<input slot="psd" type="password" placeholder="這個(gè)元素會(huì)被插入到name=psd 插槽中">
<component-a slot="psd"></component-a>
</login-component>
注意:跟 v-on
和 v-bind
一樣,v-slot
也有縮寫由缆,即把參數(shù)之前的所有內(nèi)容 v-slot:
替換為字符 #
注祖。
例: v-slot:header
可以被縮寫為 #header
<login-component>
<h2>科技</h2>
<template #user>
這里所有的內(nèi)容都會(huì)被插入到name="user" 插槽中
<div>
123
</div>
</template>
<template #psd>
<input type="password" placeholder="這個(gè)元素會(huì)被插入到name=psd 插槽中">
</template>
</login-component>
注意:插槽在項(xiàng)目開發(fā)中不太常用,常用于一些UI庫的開發(fā)均唉。如果想對(duì)插槽有更深的了解可以查閱官方文檔