1. 程式人生 > >keep-alive 組件級緩存

keep-alive 組件級緩存

表示 com 緩存 cti path outer 操作 ati head

前言

在Vue構建的單頁面應用(SPA)中,路由模塊一般使用vue-routervue-router不保存被切換組件的狀態,

它進行push或者replace時,舊組件會被銷毀,而新組件會被新建,走一遍完整的生命周期。

但有時候,我們有一些需求,比如跳轉到詳情頁面時,需要保持列表頁的滾動條的深度,等返回的時候依然在這個位置,這樣可以提高用戶體驗。

在Vue中,對於這種“頁面緩存”的需求,我們可以使用keep-alive組件來解決這個需求。

keep-alive

keep-alive是個抽象組件(或稱為功能型組件),實際上不會被渲染在DOM樹中。 它的作用是在內存中緩存組件(不讓組件銷毀),等到下次再渲染的時候,還會保持其中的所有狀態,並且會觸發activated鉤子函數。 因為緩存的需要通常出現在頁面切換時,所以常與router-view一起出現:
<keep-alive>
    <router-view />
</keep-alive>
可以使用keep-alive組件的include/exclude屬性。 include屬性表示要緩存的組件名(即組件定義時的name屬性), 接收的類型為stringRegExpstring數組; exclude屬性有著相反的作用,匹配到的組件不會被緩存。 假如可能出現在同一router-view的N個頁面中,我只想緩存列表頁和詳情頁,那麽可以這樣寫:
<keep-alive :include="[‘Home‘, ‘User‘]">
  <router-view />
</keep-alive>

vue實現前進刷新,後退不刷新

希望實現前進刷新、後退不刷新的效果。即加載過的界面能緩存起來(返回不用重新加載),關閉的界面能被銷毀掉(再進入時重新加載)。

例如對a->b->c 前進(b,c)刷新,c->b->a 後退(b,a)不刷新

知道路由是前進還是後退就好了,

這樣的話我就能在後退的時候讓from路由的keepAlive置為false

to路由的keepAlive置為ture,就能在再次前進時,重新加載之前這個keepAlive被置為false的路由了

但是這個需要集合鈎子函數來是實現

// App.vue

<div class="app">
    <keep-alive>
      <router-view v-if="$route.meta.keepAlive"></router-view>
    </keep-alive>
    <router-view v-if
="!$route.meta.keepAlive"></router-view> </div>

這個還是有爭議的。。。。

然後在一篇博客中看到用vux 來寫的,所以這邊也自己demo了下:

就是下面的代碼了:

實現條件緩存:全局的include數組

只需要將B動態地從include數組中增加/刪除就行了

在Vuex中定義一個全局的緩存數組,待傳給include:

export default {
  namespaced: true,
  state: {
    keepAliveComponents: [] // 緩存數組
  },
  mutations: {
    keepAlive (state, component) {
      // 註:防止重復添加(當然也可以使用Set)
      !state.keepAliveComponents.includes(component) && 
        state.keepAliveComponents.push(component)
    },
    noKeepAlive (state, component) {
      const index = state.keepAliveComponents.indexOf(component)
      index !== -1 &&
        state.keepAliveComponents.splice(index, 1)
    }
  }
}

在父頁面中定義keep-alive,並傳入全局的緩存數組:

// App.vue

<div class="app">
  <!--傳入include數組-->
  <keep-alive :include="keepAliveComponents">
    <router-view></router-view>
  </keep-alive>
</div>

export default {
  computed: {
    ...mapState({
      keepAliveComponents: state => state.global.keepAliveComponents
    })
  }
}

緩存:在路由配置頁中,約定使用meta屬性keepAlive,值為true表示組件需要緩存。

在全局路由鉤子beforeEach中對該屬性進行處理,這樣一來,每次進入該組件,都進行緩存:

const router = new Router({
  routes: [
    {
      path: ‘/A/B‘,
      name: ‘B‘,
      component: B,
      meta: {
        title: ‘B頁面‘,
        keepAlive: true // 這裏指定B組件的緩存性
      }
    }
  ]
})

router.beforeEach((to, from, next) => {
  // 在路由全局鉤子beforeEach中,根據keepAlive屬性,統一設置頁面的緩存性
  // 作用是每次進入該組件,就將它緩存
  if (to.meta.keepAlive) {
    store.commit(‘global/keepAlive‘, to.name)
  }
})

取消緩存的時機:對緩存組件使用路由的組件層鉤子beforeRouteLeave

因為B->A->B時不需要緩存B,所以可以認為:當B的下一個頁面不是C時取消B的緩存,那麽下次進入B組件時B就是全新的:

export default {
  name: ‘B‘,
  created () {
      // ...設置滾動條在最頂部
  },
  beforeRouteLeave (to, from, next) {
    // 如果下一個頁面不是詳情頁(C),則取消列表頁(B)的緩存
    if (to.name !== ‘C‘) {
        this.$store.commit(‘global/noKeepAlive‘, from.name)
    }
    next()
  }
}

因為B的條件緩存,是B自己的職責,所以最好把該業務邏輯寫在B的內部,而不是A中,這樣不至於讓組件之間的跳轉關系變得混亂。

一個需要註意的細節:因為keep-alive組件的include數組操作的對象是組件名、而不是路由名,

因此我們定義每一個組件時,都要顯式聲明name屬性,否則緩存不起作用。而且,一個顯式的name對Vue devtools有提示作用。

keep-alive 組件級緩存