偶然看見別人代碼裏component :is的寫法,一臉懵逼,故查了相關的一些特性,記錄下來,供參考html
應用場景:在不一樣組件之間進行動態切換
vue
實現:webpack
<!-- 組件會在 `currentTabComponent` 改變時改變 -->
<component v-bind:is="currentTabComponent"></component>複製代碼
說明:currentTabComponent
能夠包括git
查看demo(忽略難看的樣式和佈局):github
TestComponents.vue:
web
<template><div> <!-- 按鈕,用於切換組件 --> <button @click="currentTabComponent= 'A'">showA</button> <button @click="currentTabComponent= 'B'">showB</button> <!-- 動態切換顯隱,組件 --> <component :is="currentTabComponent"></component></div></template><script>//引入組件A以及組件Bimport A from "./A.vue";import B from "./B.vue"; export default { data() { return { //默認顯示組件A,若字符串爲B則顯示組件B,name在component內聲明 showWhat: "A" }; }, methods: { handleClick(tab, event) { console.log(tab, event); } }, components: { //聲明組件A,B A, B }, methods:{ } };</script><style>.tab-button { padding: 6px 10px; border-top-left-radius: 3px; border-top-right-radius: 3px; border: 1px solid #ccc; cursor: pointer; background: #f0f0f0; margin-bottom: -1px; margin-right: -1px;}.tab-button:hover { background: #e0e0e0;}.tab-button.active { background: #e0e0e0;}.tab { border: 1px solid #ccc; padding: 10px;}.posts-tab { display: flex;}.posts-sidebar { max-width: 40vw; margin: 0; padding: 0 10px 0 0; list-style-type: none; border-right: 1px solid #ccc;}.posts-sidebar li { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; cursor: pointer;}.posts-sidebar li:hover { background: #eee;}.posts-sidebar li.selected { background: lightblue;}.selected-post-container { padding-left: 10px;}.selected-post > :first-child { margin-top: 0; padding-top: 0;}</style>複製代碼
<template> <ul class="posts-sidebar"> <li v-for="post in posts" v-bind:key="post.id" v-bind:class="{ selected: post === selectedPost }" v-on:click="selectedPost = post" > {{ post.title }} </li> </ul> </ul></template><script>export default { name:'A', data() { return{ selectedPost: null, posts: [ { id: 1, title: 'Cat Ipsum', content: '<p>Dont wait for the storm to pass, dance in the rain kick up litter decide to want nothing to do with my owner today demand to be let outside at once, and expect owner to wait for me as i think about it cat cat moo moo lick ears lick paws so make meme, make cute face but lick the other cats. Kitty poochy chase imaginary bugs, but stand in front of the computer screen. Sweet beast cat dog hate mouse eat string barf pillow no baths hate everything stare at guinea pigs. My left donut is missing, as is my right loved it, hated it, loved it, hated it scoot butt on the rug cat not kitten around</p>' }, { id: 2, title: 'Hipster Ipsum', content: '<p>Bushwick blue bottle scenester helvetica ugh, meh four loko. Put a bird on it lumbersexual franzen shabby chic, street art knausgaard trust fund shaman scenester live-edge mixtape taxidermy viral yuccie succulents. Keytar poke bicycle rights, crucifix street art neutra air plant PBR&B hoodie plaid venmo. Tilde swag art party fanny pack vinyl letterpress venmo jean shorts offal mumblecore. Vice blog gentrify mlkshk tattooed occupy snackwave, hoodie craft beer next level migas 8-bit chartreuse. Trust fund food truck drinking vinegar gochujang.</p>' }, { id: 3, title: 'Cupcake Ipsum', content: '<p>Icing dessert soufflé lollipop chocolate bar sweet tart cake chupa chups. Soufflé marzipan jelly beans croissant toffee marzipan cupcake icing fruitcake. Muffin cake pudding soufflé wafer jelly bear claw sesame snaps marshmallow. Marzipan soufflé croissant lemon drops gingerbread sugar plum lemon drops apple pie gummies. Sweet roll donut oat cake toffee cake. Liquorice candy macaroon toffee cookie marzipan.</p>' } ], } }}</script>複製代碼
B.vuevue-router
<template><div> This is component of B</div></template><script>export default { name:'B',}</script>複製代碼
當咱們切換到A,點擊某條文章後,切換至B,再切換回A時,並不會展現選中狀態,操做步驟見圖:api
使用<component v-bind:is="currentTabComponent"></component>,切換的時候每次都會觸發生命週期,從新建立動態組件。接下來,咱們爲組件A和B加上生命週期,而且斷點測試一下:promise
刷新頁面,只觸發了組件A的生命週期緩存
切換至B(爲了看起來方便,我先清除了console),觸發了組件B的生命週期
再切換至A(先清除console),再次觸發了組件A的生命週期
能夠看到,初始化只掛載了組件A,每次切換都會從新觸發組件的生命週期,切換時並無保留以前的狀態(組件A的選中),可是在在某些狀況下,咱們更但願那些標籤的組件實例可以被在它們第一次被建立的時候緩存下來。爲了解決這個問題,咱們能夠用一個 <keep-alive>
元素將其動態組件包裹起來。
vue生命週期圖貼一下:
TestComponents.vue,其餘代碼不變,只用<keep-alive>
元素將 <component :is="currentTabComponent"></component>包裹起來
<!-- 動態切換顯隱,組件 --> <keep-alive> <component :is="currentTabComponent"></component> </keep-alive>複製代碼
如今,咱們再次刷新頁面,而且選中一條文章
切換至Tab B,能夠看到並無銷燬組件A
這時候,咱們再次切回至組件A,能夠看到,沒有從新觸發組件A的生命週期,而且保留了組件A文章的選中狀態。
深刻了解:https://cn.vuejs.org/v2/guide/components-dynamic-async.html
關於keep-alive的詳細api參考:https://cn.vuejs.org/v2/api/#keep-alive
<!-- 失活的組件將會被緩存!-->
<keep-alive>
<component v-bind:is="currentTabComponent"></component></keep-alive>複製代碼
注意這個<keep-alive>
要求被切換到的組件都有本身的名字,不管是經過組件的name
選項仍是局部/全局註冊。
v-show
demo到這裏,咱們來講一說爲何不用 v-show
來切換呢, V-show
會第一時間加載兩個組件, 兩個組件的生命週期都會觸發,會形成沒必要要的性能浪費,代碼改一下:
<!-- 動態切換顯隱,組件 --> <!-- <keep-alive> <component :is="currentTabComponent"></component> </keep-alive> --> <A v-show="currentTabComponent == 'A'"></A> <B v-show="currentTabComponent == 'B'"></B>複製代碼
刷新頁面,能夠看到,初始化,組件A和B的生命週期都被觸發了,而在切換時並不會從新觸發組件的生命,會保留組件A的文章選中狀態,若是咱們但願切換時從新觸發組件的生命週期,好比從新發送異步請求獲取數據等,則沒法實現。
相信你們都想到了v-if
,若是是v-if
的話 確實不會形成同時加載兩個組件,不過v-if
切換的話, 每次都會從新掛載一次,若是沒有從新渲染的須要的話 ,會形成性能浪費,代碼改一下,看一下效果:
<!-- 動態切換顯隱,組件 --> <!-- <keep-alive> <component :is="currentTabComponent"></component> </keep-alive> --> <A v-if="currentTabComponent == 'A'"></A> <B v-if="currentTabComponent == 'B'"></B>複製代碼
好了,到此 動態組件:is,v-if和v-show的優劣和使用場景大概就這樣了~關於:is的具體使用能夠結合官網,搜索一下網上的例子,就熟悉了~
在大型應用中,咱們可能須要將應用分割成小一些的代碼塊,而且只在須要的時候才從服務器加載一個模塊。爲了簡化,Vue 容許你以一個工廠函數的方式定義你的組件,這個工廠函數會異步解析你的組件定義。Vue 只有在這個組件須要被渲染的時候纔會觸發該工廠函數,且會把結果緩存起來供將來重渲染,參考:https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6
1. vue異步組件技術
vue-router配置路由,使用vue的異步組件技術,能夠實現按需加載。 可是,這種狀況下一個組件生成一個js文件。 舉例以下:
{
path: '/promisedemo',
name: 'PromiseDemo',
component: resolve => require(['../components/PromiseDemo'], resolve)
}複製代碼
2. es提案的import()
推薦使用這種方式(須要webpack > 2.4)
參考:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html
vue-router配置路由,代碼以下:
// 下面2行代碼,沒有指定webpackChunkName,每一個組件打包成一個js文件。
const ImportFuncDemo1 = () => import('../components/ImportFuncDemo1')
const ImportFuncDemo2 = () => import('../components/ImportFuncDemo2')
// 下面2行代碼,指定了相同的webpackChunkName,會合並打包成一個js文件。
// const ImportFuncDemo = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo')
// const ImportFuncDemo2 = () => import(/* webpackChunkName: 'ImportFuncDemo' */ '../components/ImportFuncDemo2')
export default new Router({
routes: [
{
path: '/importfuncdemo1',
name: 'ImportFuncDemo1',
component: ImportFuncDemo1
},
{
path: '/importfuncdemo2',
name: 'ImportFuncDemo2',
component: ImportFuncDemo2
}
]
})複製代碼
3. webpack提供的require.ensure()
vue-router配置路由,使用webpack的[require.ensure](Module API - Methods)技術,也能夠實現按需加載。 這種狀況下,多個路由指定相同的chunkName,會合並打包成一個js文件。 舉例以下:
{
path: '/promisedemo',
name: 'PromiseDemo',
component: r => require.ensure([], () => r(require('../components/PromiseDemo')), 'demo')
},
{
path: '/hello',
name: 'Hello',
component: r => require.ensure([], () => r(require('../components/Hello')), 'demo')
}複製代碼