一步一步學Vue(六)


本篇繼續介紹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">&nbsp;{{n}}&nbsp;</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">&nbsp;{{n}}&nbsp;</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>
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM