Vue 全站緩存二:如何設計全站緩存

在前文Vue 全站緩存之 keep-alive : 動態移除緩存中,咱們實現了在路由離開時動態移除緩存這一功能,以此爲基礎,vue 全站使用緩存成爲可能。javascript

本文長篇大論囉裏囉嗦巴拉巴拉,請跳着讀、反覆讀、隨機讀、躺着讀……css

系列篇1:Vue 全站緩存之 keep-alive : 動態移除緩存html

本篇爲系列篇2:Vue 全站緩存二:如何設計全站緩存前端

系列篇3:Vue 全站緩存之 vue-router-then :先後頁數據傳遞vue

系列篇4:Vue 全站緩存之 vue-router-then :實現原理java

前言

從早期粗暴得將 css、js 資源設定瀏覽器本地緩存,到後來小圖標合併成大圖節省請求資源,還有動態請求304狀態判斷,而後 ajax 開啓 web2.0 時代, pjax 大放光彩,到現在 vue.js 等前端框架的繁榮盛世,全部的這一系列發展,我認爲,提速是一個核心驅動力。node

keep-alive

在 vue 裏,支持 keep-alive 特性,經過 keep-alive ,再也不銷燬舊組件爲新組件讓路,而是緩存起來以待未來複用,當複用緩存組件時,若是數據沒有變化甚至能夠直接原樣恢復。連接web

keep-alive 和 vue-router

將 router-view 放置到 keep-alive 中,便可粗暴的實現全部路由頁的緩存功能。ajax

<!-- App.vue -->
<keep-alive><router-view class="transit-view"></router-view></keep-alive>
複製代碼

爲何要使用緩存

最多見的一個場景是新建訂單時選擇地址,新建訂單是一個路由頁面,去選擇用戶現有的地址又是一個路由頁面,因此理所固然的,咱們但願用戶選擇完地址回到訂單頁面的時候,訂單裏的其餘數據好比選擇的優惠券啊收件日期啊都能繼續保持。vue-router

在大量相似的場景裏,不斷的出現數據保留和複用的需求,因此 vuex 出現了,經過第三方的一個公共組件來保存和中轉數據,這是一個解決方案,並且仍是主流的解決方案哦。

然而換個角度,若是訂單頁在選擇地址的時候被緩存了,回到訂單頁後直接複用前面的訂單組件,其餘數據都保留此時只要更新下地址數據,讓全部的代碼邏輯都集中在訂單組件之中,這樣的開發體驗是否是會更直觀更友好?

這是見仁見智的思路,各有想法,很差說誰好誰壞,咱們就先繼續討論緩存組件的方案吧。

出現了一點小問題

若是全部的路由頁都被緩存了,那麼當你不想使用緩存的時候怎麼辦?好比又建了一個新訂單,進入不一樣文章的編輯組件,好比進入不一樣的用戶中心,緩存當然提了速,有時咱們也會不想要緩存功能,特別是一些表單場景,咱們既但願填寫一半進入下一頁面時能保留填寫的數據,咱們又但願新進入的表單是一個全新的表單頁。

魚和熊掌能夠兼得

咱們既但願填寫一半進入下一頁面時能保留填寫的數據,咱們又但願新進入的表單是一個全新的表單頁。

換句話說,回到上一個頁面時使用緩存,進入下一個頁面時不使用緩存

再換句話說,全部頁面都用緩存,只在後退(回到上一頁)時移除當前頁緩存,這樣下一次前進(進入當前頁)時由於沒有緩存就天然使用全新頁面

也就是說,只要實現後退(回到上一頁)時移除當前頁緩存這個功能,就能夠了。

在路由中定義位置

這是一種緩存複用的思路,爲了實現後退(回到上一頁)時移除當前頁緩存,由於想要實現動態肯定用戶的前進後退行爲比較麻煩,因此,咱們有個傻瓜式的方案:預測使用場景約定各路由頁面的層級關係。

好比,在 routes 定義裏,咱們能夠這麼定義各路由頁:

// 僅供參考,此處缺乏路由組件定義
// router/index.js
routes: [
        {   path: '/', redirect:'/yingshou', },
        {   path: '/yingshou',                meta:{rank:1.5,isShowFooter:true},          },
        {   path: '/contract_list',           meta:{rank:1.5,isShowFooter:true},          },
        {   path: '/customer',                meta:{rank:1.5,isShowFooter:true},          },
        {   path: '/wode',                    meta:{rank:1.5,isShowFooter:true},          },
        {   path: '/yingfu',                  meta:{rank:1.5,isShowFooter:true},          },
        {   path: '/yingfu/pact_list',        meta:{rank:2.5},                            },
        {   path: '/yingfu/pact_detail',      meta:{rank:3.5},                            },
        {   path: '/yingfu/expend_view',      meta:{rank:4.5},                            },
        {   path: '/yingfu/jizhichu',         meta:{rank:5.5},                            },
        {   path: '/yingfu/select_pact',      meta:{rank:6.5},                            },
        {   path: '/yingfu/jiyingfu',         meta:{rank:7.5},                            },
    ]
複製代碼

核心的思路是,在定義路由時,在 meta 中定義一個 rank 字段來聲明該路由的頁面優先級, 好比 1.5 標識第 1 層如首頁,2.5 表示第 2 層如商品列表頁, 3.5標識第 3 層商品詳情頁,以此類推。

若是你們同在一層,也能夠經過 1.4 和 1.5 這樣小數位來約定前後層級。

總之,咱們指望的是,從第1層進入第2層是前進,從第3層回到第2層是後退。

在路由跳轉裏動態判斷移除緩存

使用Vue.mixin的方法攔截了路由離開事件,並在該攔截方法中實現後退時銷燬頁面緩存

// main.js
Vue.mixin({
    beforeRouteLeave:function(to, from, next){
        if (from && from.meta.rank && to.meta.rank && from.meta.rank>to.meta.rank)
        {//此處判斷是若是返回上一層,你能夠根據本身的業務更改此處的判斷邏輯,酌情決定是否摧毀本層緩存。
            if (this.$vnode && this.$vnode.data.keepAlive)
            {
                if (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache)
                {
                    if (this.$vnode.componentOptions)
                    {
                        var key = this.$vnode.key == null
                                    ? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '')
                                    : this.$vnode.key;
                        var cache = this.$vnode.parent.componentInstance.cache;
                        var keys  = this.$vnode.parent.componentInstance.keys;
                        if (cache[key])
                        {
                            if (keys.length) {
                                var index = keys.indexOf(key);
                                if (index > -1) {
                                    keys.splice(index, 1);
                                }
                            }
                            delete cache[key];
                        }
                    }
                }
            }
            this.$destroy();
        }
        next();
    },
});
複製代碼

這樣就好了嗎

不行,這樣只解決了動態移除緩存的狀況,讓用戶既能夠進入新頁面,也能夠回到舊頁面。前者進新頁面問題不大,後者回到舊頁面這件事咱還沒討論過呢。

緩存是說用就用的嗎

即便是路由頁面複用了緩存,也只是複用了緩存的組件和數據,在實際場景中,從列表 A 進入詳情 B 再進入列表 C ,請問 列表 C 和列表 A 是同一個路由頁,但他們的數據會同樣嗎?應該同樣嗎?

因此就算是緩存了也要更新數據?

看起來,咱們獲得了一個新結論,緩存頁的數據也不可靠啊,摔,這日子快無法過了。

應該有辦法的。

緩存的組件被複用時會觸發 activated 事件,非緩存組件則會在建立時觸發 created mounted 等一大堆事件,而同一個頁面列表 A 進列表 B,由於 url 參數不一樣,也會觸發beforeRouteUpdate事件。連接1 連接2 連接3

看起來,咱們可以經過捕捉這些事件乾點啥。

不對不對

第一直覺是,咱們在捕捉到頁面載入的事件後去拉取數據更新頁面。仔細一想,咱們上面羅裏吧嗦廢了老半天勁是爲了進入緩存頁面再也不看那 loading 條的啊。摔,怎麼又繞回來了。

笨辦法

這裏提供一個笨辦法,事件該捕捉咱仍是要捕捉,只是這是否去作拉取數據這個動做,咱能夠有待商榷。

  • 在全部的路由頁統一註冊一個方法,就叫pageenter吧,無論從啥狀況進來的,咱都去觸發這個方法。
// list.vue
    data(){
        return {
            params        : null,
        }
    },
    methods:{
        pageenter:function(){
            this.params = {
                        'uuid'                 : this.$route.query.uuid   ,
                    };
        },
    }
複製代碼
  • 若是直接 watch 這個 params ,這事仍是無法玩(請想下爲何?),這裏能夠用一個笨辦法,咱將它轉化成字符串再 watch,捕捉到字符串變化再去從新拉取數據,若是字符串沒有變化,則啥也不作。
// list.vue
    computed:{
        paramsToString(){
            return JSON.stringify(this.params);
        },
    },
    watch:{
        paramsToString:function(){
            this.loadContent();
        },
    },
    methods:{
        loadContent:function(){
            //此處拉取數據
            //this.$http.get(....)
        },
    }
複製代碼
  • 在main.js裏,能夠用Vue.mixin攔截上文提到的三種事件,來觸發 pageenter 方法。
//main.js
Vue.mixin({
    /*初始化組件時,觸發pageenter方法*/
    mounted:function(){
       if (this.pageenter)
       {
           this.pageenter();
       }
    },
    // /*從其餘組件返回激活當前組件時*/
    activated:function(){
       if (this.pageenter)
       {
           this.pageenter();
       }
    },
    /*在同一組件中,切換路由(參數變化)時*/
    beforeRouteUpdate:function(to, from, next){
        if (this.pageenter)
        {
            this.$nextTick(()=>{
                this.pageenter();
            });
        }
        next();
    },
});
複製代碼

後語

至此,全站緩存的框架算是基本搭好了

嗎?

請繼續閱讀-系列篇3:Vue 全站緩存之 vue-router-then :先後頁數據傳遞

原文來自阿星的博客:wanyaxing.com/blog/201807…

相關文章
相關標籤/搜索