Демистификация девяти советов по оптимизации производительности для Vue.js

внешний интерфейс оптимизация производительности Vue.js

Эта статья сослалась на основной член Vue.jsGuillaume ChauВ теме, которой поделилась Vue conf в США в 2019 году: раскрыто 9 секретов производительности, были упомянуты девять навыков оптимизации производительности Vue.js.

Я читал его обменPPTПосле этого также прочитайте соответствующиеИсходный код проекта, после глубокого понимания принципа его оптимизации, я также применил некоторые из этих методов оптимизации к своей обычной работе и добился неплохих результатов.

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

Эта статья в основном для версии Vue.js 2.x, в конце концов, в следующий раз Vue.js 2.x по-прежнему является основной версией нашей работы.

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

Functional components

Первый совет, функциональные компоненты, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template>
  <div class="cell">
    <div v-if="value" class="on"></div>
    <section v-else class="off"></section>
  </div>
</template>

<script>
export default {
  props: ['value'],
}
</script>

Код оптимизированного компонента выглядит следующим образом:

<template functional>
  <div class="cell">
    <div v-if="props.value" class="on"></div>
    <section v-else class="off"></section>
  </div>
</template>

Затем мы визуализируем 800 компонентов до и после оптимизации в родительском компоненте и запускаем обновление компонентов, изменяя данные в каждом кадре, открываем панель производительности Chrome, чтобы записать их производительность, и получаем следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что выполнение до оптимизацииscriptЭто занимает больше времени, чем оптимизированный, и мы знаем, что движок JS является однопоточным операционным механизмом, и поток JS будет блокировать поток пользовательского интерфейса, поэтому, когда время выполнения скрипта слишком велико, он будет блокировать рендеринг и привести к зависанию страницы. и оптимизированныйscriptВремя выполнения короткое, поэтому работает лучше.

Итак, почему сокращается время выполнения JS с функциональными компонентами? Это начинается с принципа реализации функциональных компонентов.Вы можете понимать это как функцию, которая может отображать и генерировать часть DOM в соответствии с данными контекста, которые вы передаете.

В отличие от обычных компонентов объектного типа, функциональные компоненты не рассматриваются как реальные компоненты.Мы знаем, что вpatchВ процессе, если вы столкнетесь с узлом, это компонентvnode, рекурсивно выполнит процесс инициализации подкомпонента, в то время как функциональный компонентrenderсгенерированный является общимvnode, не будет рекурсивного процесса дочернего компонента, поэтому накладные расходы на рендеринг будут намного ниже.

Таким образом, функциональные компоненты не имеют состояния, у них нет реактивных данных, у них нет ловушек жизненного цикла. Вы можете думать об этом как об удалении части DOM в обычном шаблоне компонента и отображении его с помощью функции, что является своего рода повторным использованием на уровне DOM.

Child component splitting

Второй трюк, разделение подкомпонентов, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template>
  <div :style="{ opacity: number / 300 }">
    <div>{{ heavy() }}</div>
  </div>
</template>

<script>
export default {
  props: ['number'],
  methods: {
    heavy () {
      const n = 100000
      let result = 0
      for (let i = 0; i < n; i++) {
        result += Math.sqrt(Math.cos(Math.sin(42)))
      }
      return result
    }
  }
}
</script>

Код оптимизированного компонента выглядит следующим образом:

<template>
  <div :style="{ opacity: number / 300 }">
    <ChildComp/>
  </div>
</template>

<script>
export default {
  components: {
    ChildComp: {
      methods: {
        heavy () {
          const n = 100000
          let result = 0
          for (let i = 0; i < n; i++) {
            result += Math.sqrt(Math.cos(Math.sin(42)))
          }
          return result
        },
      },
      render (h) {
        return h('div', this.heavy())
      }
    }
  },
  props: ['number']
}
</script>

Затем мы визуализируем 300 компонентов до и после оптимизации в родительском компоненте и запускаем обновление компонентов, изменяя данные в каждом кадре, открываем панель производительности Chrome, чтобы записать их производительность, и получаем следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что оптимизированное выполнениеscriptВремя значительно меньше, чем до оптимизации, поэтому производительность лучше.

Так почему же есть разница? Давайте посмотрим на компоненты перед оптимизацией. Пример передаетheavyФункция имитирует трудоемкую задачу, и эта функция выполняется один раз при каждом рендеринге, поэтому каждый рендеринг компонента будет занимать много времени для выполнения JavaScript.

Оптимизированный способ состоит в том, чтобы поставить эту трудоемкую задачуheavyПодкомпонент для логики выполнения функцииChildCompИнкапсулированный, потому что обновление Vue — это гранулярность компонентов, хотя каждый кадр вызывает повторный рендеринг родительского компонента посредством модификации данных, ноChildCompНо он не будет перерисовываться, потому что внутри него также нет никаких реагирующих изменений данных. Поэтому оптимизированный компонент не будет выполнять трудоемкие задачи в каждом рендеринге, а естественное время выполнения JavaScript будет сокращено.

Тем не менее, я выдвинул несколько разных точек зрения на этот метод оптимизации.issue, я думаю, что в этом сценарии лучше использовать вычисляемые свойства для оптимизации, чем разбивать подкомпоненты. Благодаря функции кэширования самого вычисляемого свойства трудоемкая логика будет выполняться только при первом рендеринге, и нет дополнительных затрат на рендеринг дочерних компонентов с использованием вычисляемых свойств.

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

Local variables

Третий трюк, локальные переменные, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template>
  <div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>

<script>
export default {
  props: ['start'],
  computed: {
    base () {
      return 42
    },
    result () {
      let result = this.start
      for (let i = 0; i < 1000; i++) {
        result += Math.sqrt(Math.cos(Math.sin(this.base))) + this.base * this.base + this.base + this.base * 2 + this.base * 3
      }
      return result
    },
  },
}
</script>

Код оптимизированного компонента выглядит следующим образом:

<template>
  <div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>

<script>
export default {
  props: ['start'],
  computed: {
    base () {
      return 42
    },
    result ({ base, start }) {
      let result = start
      for (let i = 0; i < 1000; i++) {
        result += Math.sqrt(Math.cos(Math.sin(base))) + base * base + base + base * 2 + base * 3
      }
      return result
    },
  },
}
</script>

Затем мы визуализируем 300 компонентов до и после оптимизации в родительском компоненте и запускаем обновление компонентов, изменяя данные в каждом кадре, открываем панель производительности Chrome, чтобы записать их производительность, и получаем следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что оптимизированное выполнениеscriptВремя значительно меньше, чем до оптимизации, поэтому производительность лучше.

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

Итак, почему эта разница вызывает разницу в производительности, причина в том, что каждый раз, когда вы посещаетеthis.baseкогда, потому чтоthis.baseявляется реактивным объектом, поэтому вызовет егоgetter, а затем выполните логический код, связанный со сбором зависимостей. Подобная логика выполняется больше, как в примере, сотни циклов обновляют сотни компонентов, каждый компонент срабатываетcomputedПри пересчете и многократном выполнении логики, связанной со сбором зависимостей, производительность, естественно, упадет.

С точки зрения спроса,this.baseДостаточно один раз выполнить сбор зависимостей, поместить егоgetterРезультат оценки возвращается в локальную переменнуюbase, зайдите позжеbaseне сработает, когдаgetter, и не будет следовать логике сбора зависимостей, а производительность, естественно, улучшится.

Это очень полезный трюк оптимизации производительности. Потому что, когда многие люди разрабатывают проекты Vue.js, они обычно пишут напрямую всякий раз, когда берут переменные.this.xxx, потому что большинство людей не замечают посещенияthis.xxxвещи за кадром. Когда количество посещений невелико, проблема с производительностью не становится заметной, но как только количество посещений увеличивается, например, несколько посещений в большом цикле, например, возникают проблемы с производительностью.

Когда я оптимизировал производительность компонента таблицы ZoomUI ранее, вrender table bodyЯ использовал метод оптимизации локальных переменных и написал тест для сравнения производительности: при рендеринге таблицы 1000 * 10 производительность повторного рендеринга обновленных данных таблицы ZoomUI почти вдвое выше, чем у таблицы ElementUI.

Reuse DOM with v-show

Четвертый трюк, используйтеv-showЧтобы повторно использовать DOM, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template functional>
  <div class="cell">
    <div v-if="props.value" class="on">
      <Heavy :n="10000"/>
    </div>
    <section v-else class="off">
      <Heavy :n="10000"/>
    </section>
  </div>
</template>

Код оптимизированного компонента выглядит следующим образом:

<template functional>
  <div class="cell">
    <div v-show="props.value" class="on">
      <Heavy :n="10000"/>
    </div>
    <section v-show="!props.value" class="off">
      <Heavy :n="10000"/>
    </section>
  </div>
</template>

Затем мы визуализируем 200 компонентов до и после оптимизации в родительском компоненте и запускаем обновление компонентов, изменяя данные в каждом кадре, и открываем панель производительности Chrome, чтобы записать их производительность, и получаем следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что оптимизированное выполнениеscriptВремя значительно меньше, чем до оптимизации, поэтому производительность лучше.

Основное различие между оптимизацией до и после заключается в использованииv-showкоманда замененаv-ifИнструкции по замене явных и неявных компонентов, хотя с точки зрения производительностиv-showа такжеv-ifТочно так же оба управляют отображением и сокрытием компонентов, но внутренний разрыв в реализации все еще очень велик.

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

function render() {
  with(this) {
    return _c('div', {
      staticClass: "cell"
    }, [(props.value) ? _c('div', {
      staticClass: "on"
    }, [_c('Heavy', {
      attrs: {
        "n": 10000
      }
    })], 1) : _c('section', {
      staticClass: "off"
    }, [_c('Heavy', {
      attrs: {
        "n": 10000
      }
    })], 1)])
  }
}

когда условиеprops.valueКогда значение , вызовет обновление соответствующего компонента, дляv-ifВизуализированные узлы из-за старых и новых узловvnodeНепоследовательно, в процессе сравнения основных алгоритмов сравнения старые будут удалены.vnodeузел, создать новыйvnodeузел, затем новыйHeavyкомпоненты, будут испытыватьHeavyСам компонент инициализируется и рендеритсяvnode,patchи так далее.

Так что используйтеv-ifКаждый раз, когда компонент обновляется, создается новыйHeavyПодкомпоненты, когда есть много обновленных компонентов, естественно, будут снижать производительность.

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

function render() {
  with(this) {
    return _c('div', {
      staticClass: "cell"
    }, [_c('div', {
      directives: [{
        name: "show",
        rawName: "v-show",
        value: (props.value),
        expression: "props.value"
      }],
      staticClass: "on"
    }, [_c('Heavy', {
      attrs: {
        "n": 10000
      }
    })], 1), _c('section', {
      directives: [{
        name: "show",
        rawName: "v-show",
        value: (!props.value),
        expression: "!props.value"
      }],
      staticClass: "off"
    }, [_c('Heavy', {
      attrs: {
        "n": 10000
      }
    })], 1)])
  }
}

когда условиеprops.valueКогда значение , вызовет обновление соответствующего компонента, дляv-showОтрисованные узлы из-за старых и новыхvnodeпоследовательны, им просто нужно сохранитьpatchVnodeВот и все, так как он заставляет узлы DOM показывать и скрывать?

это былоpatchVnodeВ процессе внутреннийv-showФункция хука, соответствующая инструкцииupdate, то он будет основан наv-showЗначение, связанное с директивой для установки значения элемента DOM, на который она действует.style.displayЗначение контролирует отображение и скрытие.

Поэтому по сравнению сv-ifпродолжайте удалять и создавать новый DOM с функциями,v-showпросто обновляет явные и неявные значения существующей модели DOM, поэтомуv-showстоит больше, чемv-ifМеньше, чем сложнее внутренняя структура DOM, тем больше разница в производительности.

ноv-showв сравнении сv-ifПреимущество в производительности на этапе обновления компонента, если только на этапе инициализации,v-ifпроизводительность выше, чемv-show, причина в том, что он отображает только одну ветвь, аv-showОбе ветки отображаются черезstyle.displayДля управления отображением и скрытием соответствующего DOM.

В использованииv-show, будут отображены все компоненты внутри ветки, будут выполнены соответствующие функции ловушки жизненного цикла, а использованиеv-if, компоненты внутри ветки, которые не были затронуты, не будут отображаться, и соответствующие функции обработчика жизненного цикла не будут выполняться.

Поэтому нужно понимать их принципы и отличия, чтобы можно было использовать подходящие команды в разных сценариях.

KeepAlive

Совет пятый, используйтеKeepAliveКомпонент кэширует DOM, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template>
  <div id="app">
    <router-view/>
  </div>
</template>

Код оптимизированного компонента выглядит следующим образом:

<template>
  <div id="app">
    <keep-alive>
      <router-view/>
    </keep-alive>
  </div>
</template>

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

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что оптимизированное выполнениеscriptВремя значительно меньше, чем до оптимизации, поэтому производительность лучше.

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

при использованииKeepAliveпосле того, какKeepAliveПосле того, как обернутый компонент визуализируется в первый раз,vnodeИ DOM будет кешироваться, и тогда при следующем повторном рендеринге компонента он получит соответствующие данные прямо из кешаvnodeи DOM, а затем рендерить, нет необходимости снова проходить инициализацию компонента,renderа такжеpatchПодождите, пока ряд процессов уменьшитсяscriptвремя выполнения и лучшая производительность.

но использоватьKeepAliveКомпонент стоит недешево, потому что он будет занимать больше памяти для кэширования, что является типичным применением оптимизации пространства-времени.

Deferred features

Совет шестой, используйтеDeferredКомпонент задерживает рендеринг компонентов в пакетах, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<template>
  <div class="deferred-off">
    <VueIcon icon="fitness_center" class="gigantic"/>

    <h2>I'm an heavy page</h2>

    <Heavy v-for="n in 8" :key="n"/>

    <Heavy class="super-heavy" :n="9999999"/>
  </div>
</template>

Код оптимизированного компонента выглядит следующим образом:

<template>
  <div class="deferred-on">
    <VueIcon icon="fitness_center" class="gigantic"/>

    <h2>I'm an heavy page</h2>

    <template v-if="defer(2)">
      <Heavy v-for="n in 8" :key="n"/>
    </template>

    <Heavy v-if="defer(3)" class="super-heavy" :n="9999999"/>
  </div>
</template>

<script>
import Defer from '@/mixins/Defer'

export default {
  mixins: [
    Defer(),
  ],
}
</script>

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

До оптимизации:

Оптимизировано:

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

Разница между оптимизацией до и после в основном заключается в том, что последняя используетDeferэтоmixin, то как конкретно это работает, давайте разбираться:

export default function (count = 10) {
  return {
    data () {
      return {
        displayPriority: 0
      }
    },

    mounted () {
      this.runDisplayPriority()
    },

    methods: {
      runDisplayPriority () {
        const step = () => {
          requestAnimationFrame(() => {
            this.displayPriority++
            if (this.displayPriority < count) {
              step()
            }
          })
        }
        step()
      },

      defer (priority) {
        return this.displayPriority >= priority
      }
    }
  }
}

DeferОсновная идея состоит в том, чтобы разделить один рендеринг компонента на несколько раз, который поддерживается внутри.displayPriorityпеременная, а затем передатьrequestAnimationFrameУвеличивайте рендеринг каждого кадра доcount. затем используйтеDefer mixinвнутри компонента может проходитьv-if="defer(xxx)"Способ управленияdisplayPriorityувеличить доxxxпри рендеринге некоторых блоков.

Если у вас есть компоненты, для рендеринга которых требуется время, используйтеDeferredРекомендуется выполнять прогрессивный рендеринг, чтобы избежать однократногоrenderРендеринг завис из-за того, что время выполнения JS слишком велико.

Time slicing

Седьмой совет, используйтеTime slicingТехника резки временных отрезков, вы можете проверить этоОнлайн-пример.

Код до оптимизации выглядит следующим образом:

fetchItems ({ commit }, { items }) {
  commit('clearItems')
  commit('addItems', items)
}

Оптимизированный код выглядит следующим образом:

fetchItems ({ commit }, { items, splitCount }) {
  commit('clearItems')
  const queue = new JobQueue()
  splitArray(items, splitCount).forEach(
    chunk => queue.addJob(done => {
      // 分时间片提交数据
      requestAnimationFrame(() => {
        commit('addItems', chunk)
        done()
      })
    })
  )
  await queue.start()
}

Сначала мы нажимаемGenterate itemsкнопку, чтобы создать 10 000 поддельных данных, а затем включить и выключитьTime-slicingв случае щелчкаCommit itemsкнопку для отправки данных, откройте панель производительности Chrome, чтобы записать их производительность, вы получите следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти две цифры, мы можем обнаружить, что сумма до оптимизацииscriptВремя выполнения меньше, чем после оптимизации, но, судя по внешнему виду, страница будет зависать примерно на 1,2 секунды после нажатия кнопки отправки перед оптимизацией.После оптимизации страница не будет полностью зависать, но все равно будет оказывать чувство Катона.

Так почему же страница зависает до оптимизации? Поскольку данных, отправленных за один раз, слишком много, внутреннее время выполнения JS слишком велико, что блокирует поток пользовательского интерфейса и приводит к зависанию страницы.

После оптимизации на странице по-прежнему имеет коробку, потому что мы разбиваем данные, составляет 1000 гранулярности. В этом случае компонент рендеринга по-прежнему имеет давление. Мы наблюдаем, что FPS только более чем в десятках, и будет совпадение. Обычно до тех пор, пока FPS страницы достигает 60, страница будет очень гладкой. Если мы превратим размер данных разделения частиц в 100, в основном FPS может достигать 50 или более, хотя рендеринг страницы является гладкой, но завершить 1000 данных Общее время подачи все еще растет.

использоватьTime slicing Технология позволяет избежать зависания страницы, обычно мы добавляем эффект загрузки при обработке этой трудоемкой задачи, в этом примере мы можем включитьloading animation, затем отправьте данные. Сравнение показало, что до оптимизации из-за слишком большого количества данных, отправленных за один раз, JS работал в течение длительного времени, блокируя поток пользовательского интерфейса, и эта анимация загрузки не будет отображаться.После оптимизации, потому что мы разделили на несколько временных интервалов для отправки данных один раз. Время выполнения JS сокращено, поэтому анимация загрузки может отображаться.

Здесь следует отметить одну вещь, хотя мы разделили временной интервал, чтобы использоватьrequestAnimationFrameAPI, но использоватьrequestAnimationFrameСама по себе она не может гарантировать полнокадровую работу.requestAnimationFrameГарантировано, что соответствующая функция входящего обратного вызова будет выполнена после каждого REDRAW браузера. Для обеспечения полного рама, только время работы JS в одном тике не может превышать 17 мс.

Non-reactive data

Восьмой навык, использованиеNon-reactive dataДля не отвечающих данных вы можете проверить этоОнлайн-пример.

Код до оптимизации выглядит следующим образом:

const data = items.map(
  item => ({
    id: uid++,
    data: item,
    vote: 0
  })
)

Оптимизированный код выглядит следующим образом:

const data = items.map(
  item => optimizeItem(item)
)

function optimizeItem (item) {
  const itemData = {
    id: uid++,
    vote: 0
  }
  Object.defineProperty(itemData, 'data', {
    // Mark as non-reactive
    configurable: false,
    value: item
  })
  return itemData
}

Или предыдущий пример, мы сначала нажимаемGenterate itemsкнопку, чтобы создать 10 000 поддельных данных, а затем включить и выключитьPartial reactivityв случае щелчкаCommit itemsкнопку для отправки данных, откройте панель производительности Chrome, чтобы записать их производительность, вы получите следующие результаты.

До оптимизации:

Оптимизировано:

Сравнивая эти два рисунка, мы видим, что оптимизированное выполнениеscriptВремя значительно меньше, чем до оптимизации, поэтому производительность лучше.

Причина этого различия заключается в том, что когда данные отправляются внутренне, вновь отправленные данные также будут определены как отвечающие по умолчанию.Если податрибут данных находится в форме объекта, он будет рекурсивно Атрибут также отзывчивый, поэтому, когда передается много данных, этот процесс становится трудоемким.

После оптимизации заносим свойства объекта во вновь представленные данныеdataвручную сталconfigurableдляfalse, так что внутреннеwalkпройти, когдаObject.keys(obj)Получение массива свойств объекта игнорируетсяdata, этого не будетdataэто свойствоdefineReactive,из-заdataОн указывает на объект, что также уменьшит логику рекурсивного ответа, что эквивалентно уменьшению потери производительности этой части. Чем больше объем данных, тем более очевидным будет эффект от этой оптимизации.

На самом деле существует множество способов оптимизации, например, некоторые данные, которые мы определяем в компоненте, не обязательно должны быть в компоненте.dataопределено в. Мы не используем некоторые данные в шаблоне, и нам не нужно отслеживать их изменения, мы просто хотим поделиться данными в контексте компонента, в это время мы можем просто смонтировать данные в экземпляр компонента. .thisна, например:

export default {
  created() {
    this.scroll = null
  },
  mounted() {
    this.scroll = new BScroll(this.$el)
  }
}

Таким образом, мы можем поделиться в контексте компонентаscrollобъект, хотя он и не является реактивным объектом.

Virtual scrolling

Девятый трюк, используйтеVirtual scrollingКомпонент виртуальной прокрутки, вы можете проверить этоОнлайн-пример.

Код компонента до оптимизации выглядит следующим образом:

<div class="items no-v">
  <FetchItemViewFunctional
    v-for="item of items"
    :key="item.id"
    :item="item"
    @vote="voteItem(item)"
  />
</div>

Оптимизированный код выглядит следующим образом:

<recycle-scroller
  class="items"
  :items="items"
  :item-size="24"
>
  <template v-slot="{ item }">
    <FetchItemView
      :item="item"
      @vote="voteItem(item)"
    />
  </template>
</recycle-scroller>

Или предыдущий пример, нам нужно включитьView list, затем нажмитеGenterate itemsкнопка для создания 10 000 фрагментов поддельных данных (обратите внимание, что онлайн-пример может создать не более 1000 фрагментов данных, на самом деле 1000 фрагментов данных не могут хорошо отразить эффект оптимизации, поэтому я изменил ограничение исходного кода, запустив его локально , и создали 10 000 единиц данных), а затем отдельно вUnoptimizedа такжеRecycleScrollerв случае щелчкаCommit itemsкнопку для отправки данных, прокрутите страницу, откройте панель производительности Chrome, чтобы записать их производительность, и вы получите следующие результаты.

До оптимизации:

Оптимизировано:

Сравнив эти две картинки, мы обнаружили, что в случае отсутствия оптимизации частота кадров из 10 000 фрагментов данных составляет только однозначные числа в случае прокрутки, а в случае отсутствия прокрутки — всего около дюжины. Причина в том, что в неоптимизированной сцене рендерится слишком много DOM, рендеринг сам по себе оказывает большое давление. После оптимизации, даже при 10 000 кусков данных, fps в прокрутке может быть больше 30, а в непрокрутке может достигать 60 полных кадров.

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

Компонент виртуальной прокрутки такжеGuillaume ChauПисьменный, заинтересованные студенты могут изучить егореализация исходного кода. Его основной принцип заключается в прослушивании событий прокрутки, динамическом обновлении элементов DOM, которые необходимо отобразить, и вычислении их смещения в представлении.

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

Суммировать

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

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

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

Эта статья была впервые опубликована в публичном аккаунте«Фронтальная частная кухня старого Хуанга», добро пожаловать, чтобы следовать.