本篇繼續介紹vue-router,我們需要要完成這樣個demo:《分頁顯示文章列表》;這里我們以博客園首頁列表為例簡化處理:
按照上圖框選所示,簡單分為藍色部分文章組件(ArticleItemComponent),橙色框選部分列表組件(ArticleListComponent);分頁部分我們就簡單通過router-link指令構建滿足演示即可,我們的代碼實現邏輯:
1、列表組件初始化數據,傳遞給文章組件進行渲染
2、路由改變時重新初始化列表組件,更新數據
請看我們的第一版代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo2</title> <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script> <script src="https://cdn.bootcss.com/vue-router/2.7.0/vue-router.js"></script> <style> .active{ color: red; } </style> </head> <body> <div id="app"> <router-link v-for="n in [1,2,3,4,5,6,7,8]" :to="{name:'page',params:{num:n}}" :key="n"> {{n}} </router-link> <div> <router-view> </router-view> </div> </div> <script> var fakeData = []; (function () { for (var i = 0; i < 45; i++) { fakeData.push({ id: i + 1, title: `一步一步學習Vue (${i})`, desc: `一步一步學習Vue---正文部分 (${i})---正文結束`, readcount: parseInt(Math.random() * 1000) }) } })(); var ArticleItemComponent = { template: ` <ul class="article-item"> <li>{{item.readcount}}</li> <li>{{item.title}}</li> <li>{{item.desc}}</li> </ul> `, props: ['item'] } var ArticleListComponent = { template: ` <div class="article-list"> <article-item v-for="item in articleList" :item="item" :key="item.id"></article-item> </div> `, data: function () { return { articleList: [] } }, created: function () { this.activePagedData(); }, methods: { activePagedData: function () { var index = this.$route.params.num; //假定每頁五條數據 var start = (index - 1) * 5, end = index * 5; this.articleList = fakeData.slice(start, end); } }, components: { 'article-item': ArticleItemComponent } } var router = new VueRouter({ linkActiveClass:'active', routes: [ { name: 'page', path: '/page/:num', component: ArticleListComponent } ] }); var app = new Vue({ router: router }).$mount("#app"); </script> </body> </html>
路由的配置方式上一篇文章中已經做過介紹,這里陌生的部分在於ArticleListComponent中的created的使用,一個vue實例被生成后調用這個函數。vue實例被生成后還要綁定到某個html元素上,之后還要進行編譯,然后再插入到document中。每一個階段都會有一個鈎子函數,方便開發者在不同階段處理不同邏輯;其它鈎子函數我們在使用的時候會繼續介紹。一般可以在created函數中調用ajax等來獲取頁面初始化所需的數據。
雙擊運行,效果如下圖所示:
我們發現,就在第一次初始化的時候,文章列表正常顯示出來了,但是后面路由的切換操作,並沒有引起組件的更新或者說數據的更新?這是什么原因呢?其實在vue-router會最大的限度的重用組件,也就是說當page/1 and page/2這兩個路徑之間切換的時候,vue-router發現映射的是同一個組件,那么vue-router就會重用該組件,而不會重新創建它,因為比起銷毀再創建,復用則顯得更加高效,所以組件的created鈎子函數就不會被執行,導致數據不會更新,從而出現上圖的結果;對於上述現象vue-router也給我們提供了解決方式,上文提到了在vue-router啟用之后,$route會被注入到任何組件,那么我們就可以監聽$roue組件的變化來實現組件更新,基於此,請看我們的第二版代碼(請看綠色部分)
//....省略其它代碼 var ArticleListComponent = { template: ` <div class="article-list"> <article-item v-for="item in articleList" :item="item" :key="item.id"></article-item> </div> `, data: function () { return { articleList: [] } }, created: function () { this.activePagedData(); }, methods: { activePagedData: function () { var index = this.$route.params.num; //假定每頁五條數據 var start = (index - 1) * 5, end = index * 5; this.articleList = fakeData.slice(start, end); } }, watch: { '$route': function () { this.activePagedData(); } }, components: { 'article-item': ArticleItemComponent } } //......省略其它
保存后刷新,效果應該是這樣的:
這個是一個不大不小的坑,而且這種使用場景我們在實際生產中也很常見,當你發現路由變化的時候,組件數據未更新,可以考慮由於組件重用導致的生命周期鈎子未執行的情況,當然解決方式也不止這一種,在vue-router 2.2+中也增加了鈎子函數同樣能解決問題。而且通過watch方式有點侵入,感覺和在angular中使用watch類似,個人不推薦這種寫法;我們簡單修改下我們程序,作為本篇代碼的最后一版本:
var ArticleListComponent = { template: ` <div class="article-list"> <article-item v-for="item in articleList" :item="item" :key="item.id"></article-item> </div> `, data: function () { return { articleList: [] } }, created: function () {
var index=this.$route.params.num; this.activePagedData(index); }, methods: { activePagedData: function (index) { ////假定每頁五條數據 var start = (index - 1) * 5, end = index * 5; this.articleList = fakeData.slice(start, end); } }, // watch: { // '$route': function () { // this.activePagedData(); // } // }, beforeRouteUpdate: function (to, from, next) {
var index=to.params.num; this.activePagedData(index); next(); }, components: { 'article-item': ArticleItemComponent } }
保存后刷新頁面,可以發現效果和上圖一致。
小結:今天就到這里吧,主要講了一個知識點,如何處理vue'-router導致的組件重用問題,組件重用很多場景下提供了很好的性能指標,畢竟操作dom的代價是很大的,但是總要解決類似上述場景的問題,雖然是一個很小的知識點,但值得單獨放一篇着重說明一下,下一篇會講述生命周期(牽扯到組件生命周期和路由生命周期),敬請期待。
本篇完整代碼:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>demo2</title> <script src="https://cdn.bootcss.com/vue/2.4.1/vue.js"></script> <script src="https://cdn.bootcss.com/vue-router/2.7.0/vue-router.js"></script> <style> .active { color: red; } </style> </head> <body> <div id="app"> <router-link v-for="n in [1,2,3,4,5,6,7,8]" :to="{name:'page',params:{num:n}}" :key="n"> {{n}} </router-link> <div> <router-view> </router-view> </div> </div> <script> var fakeData = []; (function () { for (var i = 0; i < 45; i++) { fakeData.push({ id: i + 1, title: `一步一步學習Vue (${i})`, desc: `一步一步學習Vue---正文部分 (${i})---正文結束`, readcount: parseInt(Math.random() * 1000) }) } })(); var ArticleItemComponent = { template: ` <ul class="article-item"> <li>{{item.readcount}}</li> <li>{{item.title}}</li> <li>{{item.desc}}</li> </ul> `, props: ['item'] } var ArticleListComponent = { template: ` <div class="article-list"> <article-item v-for="item in articleList" :item="item" :key="item.id"></article-item> </div> `, data: function () { return { articleList: [] } }, created: function () { var index=this.$route.params.num; this.activePagedData(index); }, methods: { activePagedData: function (index) { //假定每頁五條數據 var start = (index - 1) * 5, end = index * 5; this.articleList = fakeData.slice(start, end); } }, // watch: { // '$route': function () { // this.activePagedData(); // } // }, beforeRouteUpdate: function (to, from, next) { var index=to.params.num; this.activePagedData(index); next(); }, components: { 'article-item': ArticleItemComponent } } var router = new VueRouter({ linkActiveClass: 'active', routes: [ { name: 'page', path: '/page/:num', component: ArticleListComponent } ] }); var app = new Vue({ router: router }).$mount("#app"); </script> </body> </html>