keep-alive: кеш на уровне компонентов

внешний интерфейс GitHub Vuex

title: keep-alive: кеш на уровне компонентов теги:

  • keep-alive
  • Vue
  • vue-router

кеш страницы

В одностраничном приложении (SPA), созданном Vue, модуль маршрутизации обычно использует vue-router. vue-router не сохраняет состояние переключаемых компонентов, при пуше или замене старые компоненты уничтожаются, а новые создаются заново и проходят полный жизненный цикл.

Но иногда у нас есть некоторые требования, например, при переходе на страницу сведений нам нужно сохранить глубину полосы прокрутки страницы списка, и она все еще будет в этом положении при возврате, что может улучшить взаимодействие с пользователем. . В Vue для этого требования «кэширования страницы» мы можем использовать компонент поддержания активности, чтобы решить это требование.

Как использовать

keep-alive — это абстрактный компонент (или функциональный компонент), который фактически не отображается в дереве DOM. Его функция заключается в кэшировании компонента в памяти (чтобы не дать компоненту быть уничтоженным), и при следующем рендеринге он сохранит в нем все состояния и вызовет активированную функцию хука. Поскольку потребность в кэшировании обычно возникает при переключении страниц, это часто происходит с router-view:

<keep-alive>
	<router-view />
</keep-alive>

Таким образом, каждый компонент, отображаемый в Router-View, будет кэшироваться.

Если вы хотите отображать только определенные страницы/компоненты, вы можете использовать свойства include/exclude компонента keep-alive. Атрибут include указывает имя кэшируемого компонента (то есть атрибут name, когда компонент определен), а полученный тип — строка, RegExp или массив строк; атрибут exclude имеет противоположный эффект, и совпадающие компоненты не будет кэшироваться. Если он может появиться на N страницах одного и того же представления маршрутизатора, и я хочу кэшировать только страницу списка и страницу сведений, я могу написать:

<keep-alive :include="['ListView', 'DetailView']">
  <router-view />
</keep-alive>

Реализовать условное кэширование: глобальный массив включения

Написание вышеуказанного включения обычно не используется, потому что оно фиксирует, какие страницы кэшируются, а какие нет, если возникает следующий сценарий:

  • Существующие страницы: домашняя страница (A), страница списка (B), страница сведений (C), обычно из: A->B->C;
  • Когда B переходит к C, а затем возвращается к B, B должен сохранить расстояние прокрутки списка;
  • Когда B возвращается к A, а затем входит в B, B не нужно поддерживать состояние, оно совершенно новое.

Очевидно, что в этом примере B «условно кэшируется», сохраняя кеш, когда C->B, и отказываясь от кеша, когда A->B. На самом деле решение не сложное, просто нужно динамически добавлять/удалять B из включаемого массива. Конкретные шаги:

  1. Определите глобальный массив кеша в Vuex, который будет передан, чтобы включить:
// global.js

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)
    }
  }
}
  1. Определите поддержку активности на родительской странице и передайте глобальный массив кеша:
// 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
    })
  }
}
  1. Кэш: на странице конфигурации маршрутизации принято использовать метаатрибут 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)
  }
})
  1. Когда отменять кеширование: используйте хук слоя компонента маршрута beforeRouteLeave для кешированного компонента. Поскольку B->A->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, чтобы не загромождать отношения перехода между компонентами.

  1. Деталь, требующая внимания: поскольку объектом операции включения массива компонента keep-alive является имя компонента, а не имя маршрута, поэтому, когда мы определяем каждый компонент, мы должны явно объявлять атрибут имени, иначе кеш не будет работай. Кроме того, явное имя имеет подсказку для Vue devtools.

другой путь?

Другой способ реализации условного кэширования, который я видел в Интернете, очень похож, но он находится в родительском компоненте, используя два представления маршрутизатора и условный рендеринг:

// 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>

В проблеме GitHub есть некоторые споры (см. Ссылку), и я не проверял ее всесторонне, поэтому пока не знаю эффекта.

Q&A

В: Имя компонента было добавлено в глобальный массив include, почему страница до сих пор не кэшируется?

A: Нет проблем, если вы будете следовать изложенным выше идеям, обратите внимание на пункт 5, то есть очень вероятно, что вы забыли объявить атрибут name для компонента.

Q: Компонент может кэшироваться, но полоса прокрутки не кэшируется, например, она вернется наверх?

О: Проблема с полосой прокрутки связана со структурой HTML компонента. В двух словах, функция поддержания активности кэширует scrollTop родительского элемента относительно компонента, поэтому, если для вашего компонента/страницы установлено значение height: 100%, а полоса прокрутки находится внутри компонента, полоса прокрутки, которую вы видите, не кэшировано. Конечно, относительно этого нам все равно нужно зайти в исходный код, чтобы копнуть глубже, и оставить дырку для себя.

Ссылаться на

  • Документация

Используйте -keep-alive для динамических компонентов

API: поддержка активности

  • заявление

поддержка vue-router

Vue реализует прямое обновление, обратное не обновляется

  • Обсуждать

https://github.com/vuejs/vue-router/issues/811