Измените исходный код vue для достижения динамической маршрутизации кеша динамической маршрутизации.

внешний интерфейс Vue.js

Измените исходный код vue для реализации кеша динамической маршрутизации.

динамическая маршрутизация

Официальная интерпретация сайта: нам часто нужно сопоставить все маршруты, соответствующие определенному шаблону, с одним и тем же компонентом. Например, у нас есть компонент User, который используется для отображения всех пользователей с разными идентификаторами. Затем мы можем использовать «динамический сегмент» в пути маршрутизации vue-router для достижения этого эффекта.

то есть если у вас естьИнвентаризациямаршрутизация, но вы хотите передать разныеIDзагрузитьCheckInputInfoЭтот компонент, если используетсяparamsметод, вам нужно толькоpathзадняя конфигурация/:taskIdможет быть достигнутCheckInputInfo/1а такжеCheckInputInfo/2Такой маршрут также можно пройти черезthis.$route.params.taskIdчтобы получить текущий маршрутtaskId.

{
    path: 'CheckInputInfo/:taskId',
    meta: {
      title: '盘点录入单'
    },
    name: 'CheckInputInfo',
    component: () => import('@/view/Check/CheckInputInfo.vue')
  }

Точно так же вы также можете использоватьqueryметод, вам нужно толькоpathзадняя конфигурация:taskIdможет быть достигнутCheckInputInfo?taskId=1а такжеCheckInputInfo?taskId=2Такой маршрут также можно пройти черезthis.$route.query.taskIdчтобы получить текущий маршрутtaskId.

{
    path: 'CheckInputInfo:taskId',
    meta: {
      title: '盘点录入单'
    },
    name: 'CheckInputInfo',
    component: () => import('@/view/Check/CheckInputInfo.vue')
  }

vue-routerпо конфигурацииparamsа такжеqueryдля достижения динамической маршрутизации и может бытьэто.$route.xxчтобы получить текущийparamsилиquery, устраняя необходимость прямого манипулирования или обработкиwindow.location, это довольно удобно.

Уведомление: при использовании параметров маршрута, таких как переход от /user/foo к /user/bar, будет повторно использоваться исходный экземпляр компонента. Поскольку оба маршрута отображают один и тот же компонент, повторное использование более эффективно, чем уничтожение и повторное создание. Однако это также означает, что перехватчики жизненного цикла компонента больше не будут вызываться.

интерпретация: без использованияkeep-aliveВ случае, каждый раз, когда мы загружаем маршрут, он будет перезагружатьсяrenderТекущий маршрут смонтированcomponent, но если два маршрута являются динамическими маршрутами, настроенными одним и тем же компонентом маршрутизации,vueСоздан для производительности и не подлежит переработкеrender.

Это явно не соответствует нашим ожиданиям, так как же обеспечить полный жизненный цикл при динамической маршрутизации? ответkeep-alive.

keep-alive

Официальная интерпретация сайта:keep-alive При обертывании динамических компонентов экземпляры неактивных компонентов кэшируются, а не уничтожаются. Как и переходы, keep-alive является абстрактным компонентом: он не отображает элемент DOM сам по себе и не появляется в цепочке родительских компонентов компонента. В 2.2.0 и более поздних версиях активированные и деактивированные будут срабатывать во всех вложенных компонентах в дереве. Когда компонент включен, две его функции хука жизненного цикла, активированные и деактивированные, будут выполнены соответственно.

keep-alive решает самую критическую проблему производительности SPA за счет кэширования Vnodes. Ниже я проанализирую следующие шаги:

1. Проблема, заключающаяся в том, что маршрутизация вызывает повторный рендеринг компонента маршрутизации.

1. Режим без кэша:
<router-view></router-view>

Каждый раз, когда вы переключаетесьrender, выполнять весь жизненный цикл, при каждом переключении перезапускатьrender, повторный запрос, обязательно не соответствует требованию.

2. Режим кэша:
<keep-alive>
  <router-view></router-view>
</keep-alive>

Просто ввод текущего маршрута в первый разrender, переключение туда и обратно не приведет к повторному выполнению жизненного цикла, и его можно кэшироватьrouter-viewДанные.

2. Проблема кэширования данных просмотра маршрутизатора

keep-aliveиспользоватьrenderфункция для созданияVnode, разvue v2.5.10изkeep-alive.jsизrender():

render () {
    const slot = this.$slots.default
    const vnode: VNode = getFirstComponentChild(slot)
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      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
      const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

существуетrenderполучаетсяVnode,подобноcache[key]существует, то:

vnode.componentInstance = cache[key].componentInstance

В противном случае будетVnodeСохранить какcacheвнутри:

cache[key] = vnode

Тогда используйтеkeep-alive, мы можем сохранить каждыйroute-viewДанные.

Проблема кэширования динамического маршрута и как ее реализовать

Во-первых, появление багов

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

Удельная производительность кэша динамической маршрутизации следующая:

  • Маршруты, настроенные с помощью динамической маршрутизации, могут кэшировать только одну копию данных.
  • keep-aliveТолько первый динамический маршрут будет иметь полный жизненный цикл, а последующие маршруты будут запускать толькоactivedа такжеdeactivatedэти два крючка.
  • После изменения определенных данных маршрутизации динамического маршрута все данные динамической маршрутизации этого же маршрута будут обновлены синхронно.

Наше ожидание на самом деле используетkeep-aliveВ случае динамическая маршрутизация может иметь нединамическую производительность, т. е. иметьполный жизненный цикл,соответствующий кэш данных.

2. Найдите ключ к проблеме

Началоkeep-aliveОбнаружение исходного кода, на самом деле проблема возникает на этом этапе:

if (
  // not included
  (include && (!name || !matches(include, name))) ||
  // excluded
  (exclude && name && matches(exclude, name))
) {
  return vnode
}

Из представленного выше представления можно найти, чтоrouter-viewНа самом деле он уже закеширован, а динамический маршрутrouter-viewвсе прошлоifРешение возвращеноVnode. Тогда посмотри на это сноваnameчто:

function getComponentName (opts: ?VNodeComponentOptions): ?string {
  return opts && (opts.Ctor.options.name || opts.tag)
}
const name: ?string = getComponentName(componentOptions)

здесьoptsНа самом деле он соответствуетVueComponentиз$опции,а такжеэто.$options.nameэто не соответствует.vueобъявлено в файлеnameАтрибуты. Потом подумал, не зря это требуется предусмотреть при настройке маршрутизацииnameСвойство должно быть таким же, как и внутри компонента.nameЗначение остается прежним.

Видя это, проблема была решена, потому что компоненты конфигурации динамической маршрутизации одинаковы,getComponentNameвозвращает одно и то же каждый разname,Потомrender()кэшировать то же самоеVnode, и кэшируется только одна копия. В этом случае, пока правильный кешVnodeи вынутьVnode, в случае динамической маршрутизации,keep-aliveДо сих пор нормально работает.

Изменить исходный код Vue

Вышеупомянутое происходит из-за проблемы с именами компонентов динамической маршрутизации, если кешированныйkeyРазве недостаточно сделать его единственным?

Итак, вrouter-viewНастройте ключ, как указано выше, и ключ получит путь деления, который всегда уникален:

<keep-alive :include="cacheList">
  <router-view :key="$route.path"></router-view>
</keep-alive>

затем изменитьkeep-alive.jsИсходный код выглядит следующим образом (поскольку отношения между праздниками не детализированы, просто вставьте исходный код напрямую, человек, который его реализовал, это я, и первый, эта ошибка на github все еще открыта):

/* 
*@flow
*modify by LK 20190624
*/

import { isRegExp, remove } from 'shared/util'
import { getFirstComponentChild } from 'core/vdom/helpers/index'

type VNodeCache = { [key: string]: ?VNode };

function getComponentName (opts: ?VNodeComponentOptions): ?string {
  return opts && (opts.Ctor.options.name || opts.tag)
}

function matches (pattern: string | RegExp | Array<string>, key: string | Number): boolean {
  if (Array.isArray(pattern)) {
    return pattern.indexOf(key) > -1
  } else if (typeof pattern === 'string') {
    return pattern.split(',').indexOf(key) > -1
  } else if (isRegExp(pattern)) {
    return pattern.test(key)
  }
  /* istanbul ignore next */
  return false
}

function pruneCache (keepAliveInstance: any, filter: Function) {
  const { cache, keys, _vnode } = keepAliveInstance
  for (const key in cache) {
    const cachedNode: ?VNode = cache[key]
    if (cachedNode) {
      // const name: ?string = getComponentName(cachedNode.componentOptions)
      if (key && !filter(key)) {
        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)
}

const patternTypes: Array<Function> = [String, RegExp, Array]

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, key => matches(val, key))
    })
    this.$watch('exclude', val => {
      pruneCache(this, key => !matches(val, key))
    })
  },

  render () {
    const slot = this.$slots.default
    const vnode: VNode = getFirstComponentChild(slot)
    const key: ?string = vnode.key == null
        // same constructor may get registered as different local components
        // so cid alone is not enough (#3269)
        ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
        : vnode.key
    const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
    if (componentOptions) {
      // check pattern
      const name: ?string = getComponentName(componentOptions)
      const { include, exclude } = this
      if (
        // not included
        (include && (!key || !matches(include, key))) ||
        // excluded
        (exclude && key && matches(exclude, key))
      ) {
        return vnode
      }

      const { cache, keys } = this

      if (cache[key]) {
        vnode.componentInstance = cache[key].componentInstance
        // make current key freshest
        remove(keys, key)
        keys.push(key)
      } else {
        cache[key] = vnode
        keys.push(key)
        // prune oldest entry
        if (this.max && keys.length > parseInt(this.max)) {
          pruneCacheEntry(cache, keys[0], keys, this._vnode)
        }
      }

      vnode.data.keepAlive = true
    }
    return vnode || (slot && slot[0])
  }
}

Как интегрировать

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

Сначала измените package.json:

npm installникогда не скачивайтеvue,Исправлятьpackjson.jsизменить на местныйvue: "vue": "файл:./vue2.5.0/"

"dependencies": {
  "axios": "^0.18.0",
  "clipboard": "^2.0.0",
  "codemirror": "^5.38.0",
  "countup": "^1.8.2",
  "cropperjs": "^1.2.2",
  "dayjs": "^1.7.7",
  "echarts": "^4.0.4",
  "html2canvas": "^1.0.0-alpha.12",
  "iview": "^3.2.2",
  "iview-area": "^1.5.17",
  "js-cookie": "^2.2.0",
  "simplemde": "^1.11.2",
  "sortablejs": "^1.7.0",
  "tree-table-vue": "^1.1.0",
  "v-org-tree": "^1.0.6",
  "vue": "file:./vue2.5.0/",
  "vue-i18n": "^7.8.0",
  "vue-router": "^3.0.1",
  "vuedraggable": "^2.16.0",
  "vuex": "^3.0.1",
  "wangeditor": "^3.1.1",
  "xlsx": "^0.13.3"
},

2. Измените все локальныеimport vueДля локальных файлов:

// import Vue from 'vue'
import Vue from '../vue-2.5.10/src/core/index'