feb-alive
github地址
體驗鏈接
使用理由
開發者無需因為動態路由或者普通路由的差異而將數據初始化邏輯寫在不同的鉤子里beforeRouteUpdate或者activated 開發者無需手動緩存頁面狀態,例如通過localStorage或者sessionStorage緩存當前頁面的數據 feb-alive會幫你處理路由meta信息的存儲與恢復為什么開發feb-laive?
當我們通過Vue開發項目時候,是否會有以下場景需求?
/a跳轉到/b 后退到/a時候,希望從緩存中恢復頁面 再次跳轉到/b時,分兩種情況 情況一: 通過鏈接或者push跳轉,則希望重新創建/b頁面,而不是從緩存中讀取 情況二: 如果點擊瀏覽器自帶前進按鈕,則還是從緩存中讀取頁面。這個場景需求著重強調了緩存,緩存帶來的好處是,我上次頁面的數據及狀態都被保留,無需在從服務器拉取數據,使用戶體驗大大提高。
嘗試用keep-alive實現頁面緩存
<keep-alive> <router-view></router-view></keep-alive>
so easy但是理想很完美,現實很殘酷
存在問題
-/a跳到/b,再跳轉到/a 的時候,頁面中的數據是第一次訪問的/a頁面,明明是鏈接跳轉,確出現了緩存的效果,而我們期望的是像app一樣開啟一個新的頁面。
同理動態路由跳轉/page/1->/page/2因為兩個頁面引用的是同一個組件,所以跳轉時頁面就不會有任何改變,因為keep-alive的緩存的key是根據組件來生成的(當然Vue提供了beforeRouteUpdate鉤子供我們刷新數據) 總結:keep-alive的緩存是==組件級別==的,而不是==頁面級別==的。舉個應用場景
例如瀏覽文章頁面,依次訪問3篇文章
/artical/1 /artical/2 /artical/3當我從/artical/3后退到/artical/2時候,由于組件緩存,此時頁面還是文章3的內容,所以必須通過beforeRouteUpdate來重新拉取頁面2的數據。(注意此處后退不會觸發組件的activated鉤子,因為兩個路由都渲染同個組件,所以實例會被復用,不會執行reactivateComponent)
如果你想從/artical/3后退到/artical/2時,同時想恢復之前在/artical/2中的一些狀態,那么你還需要自己針對/artical/2中的所有狀態數據進行存儲和恢復。
綜上:keep-alive實現的組件級別的緩存和我們想象中的緩存還是有差距的,keep-alive并不能滿足我們的需求。
==針對這些問題,所以feb-alive插件誕生了==
由于feb-alive是基于keep-alive實現的,所以我們先簡單分析一下keep-alive是如何實現緩存的
export default { name: 'keep-alive', abstract: true, props: { include: patternTypes, exclude: patternTypes, max: [String, Number] }, created () { this.cache = Object.create(null) this.keys = [] }, destroyed () { for (const key in this.cache) { pruneCacheEntry(this.cache, key, this.keys) } }, mounted () { this.$watch('include', val => { pruneCache(this, name => matches(val, name)) }) this.$watch('exclude', val => { pruneCache(this, name => !matches(val, name)) }) }, render () { // 獲取默認插槽 const slot = this.$slots.default // 獲取第一個組件,也就和官方說明的一樣,keep-alive要求同時只有一個子元素被渲染,如果你在其中有 v-for 則不會工作。 const vnode: VNode = getFirstComponentChild(slot) // 判斷是否存在組件選項,也就是說只對組件有效,對于普通的元素則直接返回對應的vnode const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions if (componentOptions) { // 檢測include和exclude const name: ?string = getComponentName(componentOptions) const { include, exclude } = this if ( // not included (include && (!name || !matches(include, name))) || // excluded (exclude && name && matches(exclude, name)) ) { return vnode } const { cache, keys } = this // 如果指定了子組件的key則使用,否則通過cid+tag生成一個key const key: ?string = vnode.key == null ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.key // 判斷是否存在緩存 if (cache[key]) { // 直接復用組件實例,并更新key的位置 vnode.componentInstance = cache[key].componentInstance remove(keys, key) keys.push(key) } else { // 此處存儲的vnode還沒有實例,在之后的流程中通過在createComponent中會生成實例 cache[key] = vnode keys.push(key) // 當緩存數量大于閾值時,刪除最早的key if (this.max && keys.length > parseInt(this.max)) { pruneCacheEntry(cache, keys[0], keys, this._vnode) } } // 設置keepAlive屬性,createComponent中會判斷是否已經生成組件實例,如果是且keepAlive為true則會觸發actived鉤子。 vnode.data.keepAlive = true } return vnode || (slot && slot[0]) }}
新聞熱點
疑難解答
圖片精選