動態(tài)組件
有的時候,在不同組件之間進行動態(tài)切換是非常有用的,比如在一個多標簽的界面里:
is
上述內(nèi)容可以通過 Vue 的 <component> 元素加一個特殊的 is 特性來實現(xiàn)
<component v-bind:is="currentTabComponent"></component>
在上述示例中,currentTabComponent 可以包括
- 已注冊組件的名字,或
- 一個組件的選項對象
-
component中綁定參數(shù)的方式和普通組件一樣
image - 解析
DOM模板時的注意事項:有些HTML元素,諸如<ul>、<ol>、<table>和<select>,對于哪些元素可以出現(xiàn)在其內(nèi)部是有嚴格限制的。而有些元素,諸如<li>、<tr>和<option>,只能出現(xiàn)在其它某些特定的元素內(nèi)部。這會導致我們使用這些有約束條件的元素時遇到一些問題。例如:
<table>
<blog-post-row></blog-post-row>
</table>
- 這個自定義組件
<blog-post-row>會被作為無效的內(nèi)容提升到外部,并導致最終渲染結(jié)果出錯。幸好這個特殊的is特性給了我們一個變通的辦法:
<table>
<tr is="blog-post-row"></tr>
</table>
keep-alive
當組件切換時,你有時會想保持這些組件的狀態(tài),以避免反復重渲染導致的性能問題
上圖中,你選擇了一篇文章,切換到
Archive 標簽,然后再切換回 Posts,不會繼續(xù)展示之前選擇的文章。因為你每次切換時,Vue 都創(chuàng)建了一個新的 currentTabComponent 實例。
重新創(chuàng)建動態(tài)組件的行為通常非常有用,但該案例中,我們更希望標簽的組件實例能夠被緩存下來。我們可以用 <keep-alive> 元素將其動態(tài)組件包裹起來。
注意這個
<keep-alive>要求被切換到的組件都有自己的名字,不論是通過組件的name選項還是局部/全局注冊。
組件中的name選項有什么作用?
根據(jù)上面的注意事項,延伸出該問題,下面來看個網(wǎng)上的回答:
- 當項目使用keep-alive時,可搭配組件name進行緩存過濾
//假設(shè)我們有個組件命名為detail,
//其中dom加載完畢后我們在鉤子函數(shù)mounted中進行數(shù)據(jù)加載
export default {
name:'Detail'
},
mounted(){
this.getInfo();
},
methods:{
getInfo(){
axios.get('/xx/detail.json',{
params:{
id:this.$route.params.id
}
}).then(this.getInfoSucc)
}
}
/**
在App.vue中使用了keep-alive導致
我們第二次進入的時候頁面不會重新請求,
即不會再次觸發(fā)mounted函數(shù)。
有兩個解決方案:
一個增加activated()函數(shù),每次進入新頁面的時候再獲取一次數(shù)據(jù)。
還有個方案就是在keep-alive中增加一個過濾exclude:
*/
<div id="app">
<keep-alive exclude="Detail">
<router-view/>
</keep-alive>
</div>
- DOM做遞歸組件時
<div>
<div v-for="(item,index) of list" :key="index">
<div>
<span class="item-title-icon"></span>
{{item.title}}
</div>
<div v-if="item.children" >
<!-- detail-list 就是該組件自身 -->
<detail-list :list="item.children"></detail-list>
</div>
</div>
</div>
<script>
export default {
name:'DetailList',//遞歸組件是指組件自身調(diào)用自身
props:{
list:Array
/**
const list = [{
"title": "A",
"children": [
{"title": "A-A","children": [{"title": "A-A-A"}]},
{"title": "A-B"}]
}, {
"title": "B"
}, {
"title": "C"
}, {
"title": "D"
}]
*/
}
}
</script>
迭代結(jié)果如下:
- 當你用
vue-tools時:該調(diào)試工具里顯示的組見名稱是由vue中組件name決定的
image
異步組件
在大型應(yīng)用中,我們可能需要將應(yīng)用分割成小一些的代碼塊,并且只在需要的時候才從服務(wù)器加載一個模塊。
為了簡化,
Vue允許你以一個工廠函數(shù)的方式定義你的組件,這個工廠函數(shù)會異步解析你的組件定義。
Vue只有在這個組件需要被渲染時才會觸發(fā)該工廠函數(shù),且會把結(jié)果緩存起來供未來重渲染
知識點
如你所見,這個工廠函數(shù)會收到一個
resolve回調(diào),這個回調(diào)函數(shù)會在你從服務(wù)器得到組件定義的時候被調(diào)用。你也可以調(diào)用reject(reason)來表示加載失敗。-
這里的
setTimeout是為了演示用的,如何獲取組件取決于你自己。一個推薦的做法是將異步組件和webpack的code-splitting功能一起配合使用:
image -
你也可以在工廠函數(shù)中返回一個
Promise,所以把webpack 2和ES2015語法加在一起,我們可以寫成這樣:
image
組件之間的循環(huán)引用
-
當使用局部注冊的時候,你也可以直接提供一個返回
Promise的函數(shù):
image -
vue處理邊界情況中有一種情況是:組件之間的循環(huán)引用,這里就涉及到了異步加載組件問題:
image 當你仔細觀察的時候,你會發(fā)現(xiàn)這些組件在渲染樹中互為對方的后代和祖先——一個悖論!
當通過Vue.component全局注冊組件的時候,這個悖論會被自動解開。
如果你是這樣做的,那么你可以跳過這里。-
然而,如果你使用一個模塊系統(tǒng)依賴/導入組件,例如通過
webpack或Browserify,你會遇到一個錯誤:
image -
這里我們可以異步
import(具體的我們可以在處理邊界情況一節(jié)中具體再學習)
image
處理加載狀態(tài)
2.3.0+新增:這塊的具體應(yīng)用后續(xù)再更新...
這里的異步組件工廠函數(shù)也可以返回一個如下格式的對象:
const AsyncComponent = () => ({
// 需要加載的組件 (應(yīng)該是一個 `Promise` 對象)
component: import('./MyComponent.vue'),
// 異步組件加載時使用的組件
loading: LoadingComponent,
// 加載失敗時使用的組件
error: ErrorComponent,
// 展示加載時組件的延時時間。默認值是 200 (毫秒)
delay: 200,
// 如果提供了超時時間且組件加載也超時了,
// 則使用加載失敗時使用的組件。默認值是:`Infinity`
timeout: 3000
})
注意如果你希望在
Vue Router的路由組件中使用上述語法的話,你必須使用Vue Router 2.4.0+版本。
Vue異步組件處理路由組件加載狀態(tài)的解決方案
- 在大型單頁面應(yīng)用中,處于對性能的考慮和首屏加載速度的要求,我們一般都會使用
webpack的代碼分割和vue-router的路由懶加載功能將我們的代碼分成一個個模塊,并且只在需要的時候才從服務(wù)器加載一個模塊。 - 但是這種解決方案也有其問題,當網(wǎng)絡(luò)環(huán)境較差時,我們?nèi)ナ状卧L問某個路由模塊,由于加載該模塊的資源需要一定的時間,那么該段時間內(nèi),我們的應(yīng)用就會處于無響應(yīng)的狀態(tài),用戶體驗極差。
- 【解決方案】這種情況,我們一方面可以縮小路由模塊代碼的體積,靜態(tài)資源使用
cdn存儲等方式縮短加載時間,另一方面則可以路由組件上使用異步組件,顯示loading和error等狀態(tài),使用戶能夠得到清晰明了的操作反饋。 - 【具體實現(xiàn)】聲明方法,基于
Vue動態(tài)組件工廠函數(shù)來返回一個Promise對象
image
調(diào)用:
image