Что это? Первый взгляддобиться эффекта,Исходный код здесь
Большое спасибоXGHeavenиjeff_oneДва брата, этот эффект должен быть стеком страниц, который в основном имитирует навигацию нативных приложений APP, а затем то, что я реализую, является лишь частью функции этого эффекта, и полная функция также должна иметь "Может быть несколько экземпляров одной и той же страницы с разными состояниями.", заинтересованные друзья могут пойти и посмотретьvue-navigation,vue-page-stack,vue-nav
Сцены
Во-первых, я не могу назвать это взаимодействие профессиональным названием., моя общая сцена в то время такова:
- Теперь есть небольшой торговый центр. С главной страницы (Index) вы можете войти на страницу со списком продуктов (List). Этот список представляет собой бесконечный список. Теперь пользователь прокручивает вниз и видит, что определенный продукт кажется любимым, поэтому нажмите, чтобы перейти на страницу сведений о продукте (Информация), я очень доволен после прочтения. Что ж, но ходить по магазинам, чтобы пользователь вернулся на страницу со списком продуктов, готовый продолжить просмотр, что, если вы обновите предыдущий список пользователя? Если я очень недоволен, это означает, что мне нужно повторно действовать, и, найдя продукт, который мне только что понравился, я продолжаю ходить по магазинам.
SomePage -> List: List重新加载
List -> Info -> List: List使用缓存
- Когда пользователь подтверждает продукт и решает купить его, он входит на страницу заказа (Форма). Страница заказа по умолчанию представляет собой введенный пользователем исторический адрес доставки. Похоже, что у пользователя есть продавец, который хочет напомнить о доставке, поэтому он заполняет примечания, а затем проверяет.Если в заказе есть ошибка, адрес может быть изменен снова, поэтому нажмите на адрес доставки, чтобы войти на страницу списка адресов доставки (Адрес).После выбора мы автоматически вернемся к страницу заказа для пользователя и обновить адрес доставки.Если в это время нет примечания, что произойдет?
SomePage -> Form: Form重新加载
Form -> Address -> Form: Form使用缓存
Выше приведен простой пример.По сути, это похоже на проблему, с которой я столкнулся, когда моя предыдущая компания занималась мобильным терминалом (SPA), за исключением того, что мы являемся списком продавцов, но взаимодействие аналогично.В начале, Я решил это напрямую путем маршрутизации, аналогично использованию информации в качестве подстраницы списка и адреса в качестве подстраницы формы. Внедрение достигнуто, но слишком хлопотно, сопровождение хлопотно и не очень дружелюбно по отношению к другим партнерам в группе. До определенного времени я решил его рефакторить, до рефакторинга у меня не было хороших идей, а тут гугл накосячил: используйте включаемую реализацию keep-alive, я знал эту идею на тот момент, я не посмотри вниз, и сделал это сам Еда и одежда 😁.
Унифицировать словарный запас, следующее类列表页Список,类详情页информация
Ступай на яму
В начале я тоже наступил на яму, в основном из-за того, что keep-alive Vue в то время редко трогали, все было Copy...
Первое использование как раньше (копия)keep-aliveРутина такова:
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive"/>
Затем я преобразовал его, чтобы он выглядел так
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<keep-alive :include="include">
<router-view v-if="!$route.meta.keepAlive"/>
</keep-alive>
это контролировать этоinclude, особый подход заключается в том, чтобы сначала перейти к параметрам маршрутизации на странице списка классов.metaдобавить один кcacheToполе, указывающее, что указанная страница кэшируется, значение представляет собой массив, а затем:
router.beforeEach((to, from, next) => {
if (isPageLikeList(from)) {
// 如果from是类列表页面
const fromCacheTo = from.meta.cacheTo
if (fromCacheTo.indexOf(to.name) > -1) {
// 如果to是类详情页面
// 将from对应的组件设置缓存
} else {
// 移除from缓存
}
}
// ...
})
Но мысль очень красивая.После написания обнаружил,что баг более серьезный,и кеш страницы списка классов всегда недействителен.Почему? Это начинается с включения механизма keep-alive, поэтому я посмотрел исходный код keep-alive, в исходниках есть такой абзац
// ...
mounted () {
this.$watch('include', val => {
pruneCache(this, name => matches(val, name))
})
this.$watch('exclude', val => {
pruneCache(this, name => !matches(val, name))
})
},
// ...
Затем продолжайте смотреть вниз, чтобы слушатьincludeлогика обратного вызова
function pruneCache (keepAliveInstance: any, filter: Function) {
// 对于include,filter逻辑为:include包含组件名时返回true,否则返回false
const { cache, keys, _vnode } = keepAliveInstance
for (const key in cache) {
const cachedNode: ?VNode = cache[key]
if (cachedNode) {
const name: ?string = getComponentName(cachedNode.componentOptions)
// 若组件不在include范围中
if (name && !filter(name)) {
pruneCacheEntry(cache, key, keys, _vnode)
}
}
}
}
function pruneCacheEntry (
cache: VNodeCache,
key: string,
keys: Array<string>,
current?: VNode
) {
const cached = cache[key]
// 销毁组件
if (cached && (!current || cached.tag !== current.tag)) {
cached.componentInstance.$destroy()
}
// 清除组件的缓存
cache[key] = null
remove(keys, key)
}
Общий процесс таков,includeПосле изменения (добавленного или удаленного), если компонент неincludeв и текущийcache[组件name]Если есть кеш, выполните pruneCacheEntry, чтобы уничтожить компонент и очистить кеш компонента.
Итак, как это влияет на мою логику выше? Согласно моему предыдущему процессу, динамическое управлениеinclude, нет проблем с удалением кеша компонентов таким образом, но будут проблемы с его добавлением, потому что новая операция происходит до выхода со страницы списка классов и входа на страницу сведений о классе.В это время страница списка классов уже существует, а затем обнаруживается исходный код.includeВ процессе есть только операция очистки кеша.
Зная проблему, как решить очень ясно
выполнить
анализировать
мы можемincludeПеред входом на страницу со списком классов (router.beforeEach) операция страницы со списком классов может быть эффективно кэширована.На самом деле, это так просто, несмотря ни на что, пока это страница со списком классов, она будет хранится перед входом.Его кэш при выходе определяет, является ли новый маршрут страницей сведений о классе, заданной страницей списка классов, и если нет, очищает кэш. Поэтому, прежде чем это осознать, давайте сначала уточним сцену, раньше это было больше, чем игрушка...
SomePage -> List: List重新加载
// 第一种
List -> Info -> List: List使用缓存
// 第二种
List -> Info -> SomePage -> List: List重新加载
// 第三种,不限层级
List -> Info -> OtherList -> OtherInfo -> ... -> List: List、Info、OtherList...使用缓存
其中第三种,从OtherInfo返回OtherList时,OtherInfo应该是要被清除缓存的,依次类推。也就是说,返回时不保留之前的缓存
Теперь определим общую логику на примере сценария. Один момент здесь очень ясен, а именно: нам нужно заботиться только о коммутаторе маршрутизации.toиfrom, начните с этих двух объектов маршрутизации, чтобы решить. Итак, вот 4 примера:
-
toиfromТакже не является страницей со списком классов -
toиfromОбе страницы со списком классов -
toэто страница со списком классов -
fromэто страница со списком классов
Теперь уточним его по этим 4 случаям
- В первом случае
toиfromТакже не является страницей со списком классов- Кэш не требуется, и все предыдущие кеши можно очистить
- Во втором случае
toиfromОбе страницы со списком классов- как
toне вfromВ настройках очистите кеш и добавьте новыеtoкеш; - В противном случае держите
fromкеш, добавленоtoтайник
- как
- В третьем случае
toэто страница со списком классов- как
fromне вtoнастроить, очистить кеш, добавитьtoтайник - В противном случае никаких действий не требуется
- как
- четвертый
- как
toне вfromнастроить, очистить кеш
- как
Как определить, является ли это страницей со списком классов?
[
{
path: '/list',
name: 'list',
meta: {
cacheTo: ['info']
}
// ...
},
{
path: '/info',
name: 'info',
// ...
}
]
Как и выше, поддерживайте поле в маршруте, напримерcacheTo, если имя компонента настроено, оно считается страницей списка классов
Выполнение
Логика понятна, далее мы сделаем его более общим (подключаемым), и постараемся сделать так, чтобы он был менее навязчив к исходному проекту, я назову его какVKeepAliveChain
Во-первых, если вы будете поддерживать его, как будто наступите на ямуinclude, не очень подключаемый, я должен сделать одинstore, то этоincludeможно использоватьVue.observableиметь дело с
// VKeepAliveChain.js
const state = Vue.observable({
caches: []
})
const clearCache = () => {
if (state.caches.length > 0) {
state.caches = []
}
}
const addCache = name => state.caches.push(name)
Чтобы не использовать его напрямую, как наступить на яму<keep-alive :include="include">, реализуем функциональный компонент для решения
// VKeepAliveChain.js
export const VKeepAliveChain = {
install (Vue, options = { key: 'cacheTo' }) {
const { key } = options
// 支持一下自定义key
if (key) {
cacheKey = key
}
// 直接透传children,所以会像keep-alive一样,只拿第一个组件节点
const component = {
name: 'VKeepAliveChain',
functional: true,
render (h, { children }) {
return h(
'keep-alive',
{ props: { include: state.caches } },
children
)
}
}
Vue.component('VKeepAliveChain', component)
}
}
Теперь реализуем основную логику управления кешем, ведь нам нужно использоватьrouter.beforeEach, согласился быть как можно менее инвазивным, здесь вы можете слить это.
это
mergeBeforeEachHookУ некоторых студентов есть вопросы по методу, я просто хотел, чтобы люди, которые поддерживают продолжение, знали, что здесь есть крючок (на самом деле я не придал этому значения позже), vue-router'sbeforeEachХуки можно многократно прописывать и выполнять в порядке регистрации, здесь можно передать роутер на Vue.use, а потом прописать прямо в модуле.beforeEachкрюк
// VKeepAliveChain.js
const defaultHook = (to, from, next) => next()
export const mergeBeforeEachHook = (hook = defaultHook) => {
return (to, from, next) => {
// 缓存控制逻辑
// 1. 都不是类列表页
// 清空缓存
// 2. 都是类列表页
// 若`to`不在`from`的配置中,清空缓存,同时要新增`to`缓存
// 保留`from`的缓存,新增`to`缓存
// 3. 新路由是类列表页
// 若`from`不在`to`的配置中,清空缓存,新增`to`缓存
// 否则,无需处理
// 4. 旧路由是类列表页
// 若`to`不在`from`的配置中,清空缓存
const toName = to.name
const toCacheTo = (to.meta || {})[cacheKey]
const isToPageLikeList = toCacheTo && toCacheTo.length > 0
const fromName = from.name
const fromCacheTo = (from.meta || {})[cacheKey]
const isFromPageLikeList = fromCacheTo && fromCacheTo.length > 0
if (!isToPageLikeList && !isFromPageLikeList) {
clearCache()
} else if (isToPageLikeList && isFromPageLikeList) {
if (fromCacheTo.indexOf(toName) === -1) {
clearCache()
}
addCache(toName)
} else if (isToPageLikeList) {
if (toCacheTo.indexOf(fromName) === -1) {
clearCache()
addCache(toName)
}
} else if (isFromPageLikeList) {
if (fromCacheTo.indexOf(toName) === -1) {
clearCache()
}
}
return hook(to, from, next)
}
}
Тогда реализуется вся функция, и я не могу ее отправитьnpm v-keep-alive-chainначальство.
способ есть
Сначала импортируйте и зарегистрируйте его
// main.js
import { mergeBeforeEachHook, VKeepAliveChain } from 'v-keep-alive-chain'
Vue.use(VKeepAliveChain, {
key: 'cacheTo' // 可选的 默认为cacheTo
})
// 如果你没有注册过beforeEach
router.beforeEach(mergeBeforeEachHook())
// 如果有注册beforeEach
router.beforeEach(mergeBeforeEachHook((to, from, next) => {
next()
}))
Затем в App.vue (в зависимости от вашего случая)
<keep-alive>
<router-view v-if="$route.meta.keepAlive"/>
</keep-alive>
<VKeepAliveChain>
<router-view v-if="!$route.meta.keepAlive"/>
</VKeepAliveChain>
Затем настройте свои потребности в маршрутизаторе
[
{
path: '/list',
name: 'list',
meta: {
cacheTo: ['info']
}
// ...
},
{
path: '/info',
name: 'info',
// ...
}
]
Тогда вы можете повеселиться
Меры предосторожности
- Настройка маршрутизации обязательна
nameсвойства, и этоnameпотребности и компонентыnameТакой же -
cacheToприоритет меньше, чемkeepAlive, поэтому не устанавливайте страницу, которая обрабатывает это требованиеkeepAlive - Можно настроить кэширование 2-х страниц только при переключении между собой, но я не нашел подходящего сценария.
- Я обычно использую webpack и vue-cli из коробки.Упакованный скаффолд — vue-cli4.0.Я редко изучаю эти вещи глубоко.Потом я посмотрел исходный код, опубликованный в npm-пакете, и обнаружил, что много бесполезных полифилов было Я ввел его, в результате Gzip-пакеты больше 4Кб.Решения пока не нашел.Если знаете, сообщите пожалуйста 😂
Статья пишется быстрее, если есть ошибки, можете оставить сообщение ниже
Друзья, увидев это, я надеюсь, что статья может вас вдохновить. Я очень приветствую технические обмены. Если вы считаете, что статья вам полезна, пожалуйста, поставьте мне 👍. Обычно я не забочусь об этом, но, поскольку я буду Скоро отправлю резюме, мне нужно что-то для поддержки фасада, нет возможности, резюме очень плохое, спасибо, и пусть жизнь принесет вам счастье! ! !