Любой, кто использовал Vue, знает, что представления Vue, управляемые данными, основаны наgetter
а такжеsetter
Сбор зависимостей для реализации изменений данных для точного обновления представления, а затем модификации узлов DOM, но так ли это «точно»?
жизненный опыт
Во-первых, мы все знаемVueилиReactКлючом к эффективному обновлению является использованиеvirtual dom
(именуемый в дальнейшемvdom
), когда есть изменение данных, путем изменения старого и нового компонентовvdom
изdiffоперации, вычислите узлы DOM, которые необходимо фактически изменить, а затем выполните операции добавления, удаления и модификации. Отсюда видно, чтоdiffточность и производительность, является ключевым звеном в общей производительности обновления.
а такжеVueСравниватьReactПричина превосходства (не в целом, а только в разнице) заключается в том, чтоVueиспользовалgetter
а такжеsetter
Точный сбор зависимостей данных реализованных представлений, а именно:Когда данные обновляются, компоненты, использующие данные, могут быть точно инициированы для выполнения процесса обновления.
Но так ли это на самом деле?
Деловая сцена
То, что нужно современному бизнесу, — это веб-версия.Verbatim Lyric Maker, как следует из названия, то, что мы обычно видим на QQ MusicдословноС помощью этого инструмента создаются тексты песен, и выглядит это так:
Перед любой QQ музыкальная собака или мою прохладный лирику Verbatim Maker - это инструмент, встроенный в настольный клиент, хотя каждый инструмент немного отличается, но взаимодействие в основном, как показано ниже:
Проще говоря, мы **"отмечаем"** каждое слово текста с помощью клавиатуры.вверх вниз влево вправоДождитесь выполнения операции по управлению движением курсора (синее поле) и отметьте на нем словоВремя началаа такжепродолжительность.
Детали реализации этого не подробно, это интересный, но ненормальный сложный процесс. Наконец, я понял это некоторое время, пока появился некоторые песни программы, нарушая эту функцию «Мечта».
Была проблема
Это была темная и ветреная ночь с громом и молнией, и тест сообщил о вопросе в группе:
Нажмите, чтобы открыть, и найдите следующее, первая строка начинается со второго слова, все остальные застревают примерно на две секунды и переходят сразу ко второй строке.
Ясно, что это очень серьезная проблема с производительностью, которая требует срочного решения.
проблема позиционирования
Я нашла эту песню и обнаружила, что это 17-минутная рэп-песня "Реальность против мечты" (хоть и победила эта битва мечта, но я потерпел поражение от реальности ┑( ̄Д  ̄)┍).
Мы включаем Vueрежим производительности, откройте панель производительности Chrome, чтобы увидеть узкое место.
Работайте в течение 20 секунд, нажимайте «Вправо» каждую секунду в течение первых 10 секунд, а затем нажмите и удерживайте «Вправо» в течение следующих десяти секунд.
Из приведенного выше видно, что частота кадров сильно колеблется в первые десять секунд, а позднее использование10 секундРендеринг кадра полностью застрял в ритме, соответствующем эффекту двух секунд после предыдущей анимации. из нижней части панели火焰图
Видно, что,scriptingРасчеты плотные и занимают много времени.
Глядя на время обратного вызова при повторном нажатии «правильного» события, время, затрачиваемое на один обратный вызов, достигло ** 243 миллисекунд **.
Посмотрите еще раз на панель vue-dev-tool.
Вы правильно прочитали, обновление ElButton заняло более 2000 секунд. Сначала я подумал, что это статистический баг dev-tool, поэтому прочитал напрямую.lyricMaker
Этот компонент (то есть создатель текстов выше). Из флейм-графа выше видно, что большая часть времени уходит на операции скрипта.В сочетании с логикой страницы стиль отображения каждой строки и каждого слова в продуценте должен динамически рассчитываться (каждый раз вперед и назад учитываются), Таким образом, причиной проблемы с производительностью является:
Когда слово лирики очень много, каждый раз, когда вы перемещаете курсор, срабатывают слишком много разных расчетов, приводит к странице Cardon.
Решать проблему
Теперь, когда вы знаете, что проблема заключается в том, что вычисление различий, вызванное слишком большим количеством слов, занимает слишком много времени, вы можете решить наиболее распространенную проблему —DOM节点过多怎么优化?
Проблема та же:删除掉不在可视区域的节点
.
Как показано на рисунке выше, вам нужно только скрыть текст текущей строки> ± 6, а затем посмотреть на панель производительности и панель инструментов разработчика после преобразования:
Видно, что отнимающий много времени обратный вызов события был изменен с243ms -> 8ms
, на графике пламени видно, что вызовы методов значительно сократились, и кажется, что эффект оптимизации в настоящее время очевиден.
Если смотреть только на поверхность, то это конец цветка. Но меня заинтересовала небольшая деталь на скриншоте выше.
Dig Deep
Посмотрите еще раз на панель vue-del-tool, если будем внимательны, то сможем найтиElButton
Время компонента от2390310 мс было уменьшено до 15255 мс, что сократило время на 99%,вupdateRender
Занимает 99% времени.
lyricMaker
Время, затрачиваемое на компоненты, сокращается вдвое,ElButton
Это больше похоже на ключ к повышению производительности.Из этого я предполагаю, что первые несколько сотен тысяч миллисекунд - это не отображение ошибки, а реальная ситуация. Ввиду копательного духа Lanxiang, давайте получим исходный код dev-tool, чтобы выяснить статистическую логику времени отображения этой панели, в основном таким методом:код
const COMPONENT_HOOKS = [
'beforeCreate',
'created',
'beforeMount',
'mounted',
'beforeUpdate',
'updated',
'beforeDestroyed',
'destroyed'
]
const RENDER_HOOKS = {
beforeMount: { after: 'mountRender' },
mounted: { before: 'mountRender' },
beforeUpdate: { after: 'updateRender' },
updated: { before: 'updateRender' }
}
function applyHooks (vm) {
if (vm.$options.$_devtoolsPerfHooks) return
vm.$options.$_devtoolsPerfHooks = true
const renderMetrics = {}
COMPONENT_HOOKS.forEach(hook => {
const renderHook = RENDER_HOOKS[hook]
const handler = function () {
if (SharedData.recordPerf) {
// Before
const time = performance.now()
if (renderHook && renderHook.before) {
// Render hook ends before one hook
const metric = renderMetrics[renderHook.before]
if (metric) {
metric.end = time
addComponentMetric(vm.$options, renderHook.before, metric.start, metric.end)
}
}
// After
this.$once(`hook:${hook}`, () => {
const newTime = performance.now()
addComponentMetric(vm.$options, hook, time, newTime)
if (renderHook && renderHook.after) {
// Render hook starts after one hook
renderMetrics[renderHook.after] = {
start: newTime,
end: 0
}
}
})
}
}
const currentValue = vm.$options[hook]
if (Array.isArray(currentValue)) {
vm.$options[hook] = [handler, ...currentValue]
} else if (typeof currentValue === 'function') {
vm.$options[hook] = [handler, currentValue]
} else {
vm.$options[hook] = [handler]
}
})
}
Как видно из приведенного выше кода (COMPONENT_HOOKS.forEach开始
),updateRender
На этот раз изbeforeUpdate
начатьupdated
закончить этот период, дакаждыйКомпоненты будут учитываться в статистике, например, когда изменение данных имеет 1000 обновлений кнопок, это занимает в общей сложности 1 секунду, тогда время updateRender ElButton в этой статистике панели составляет 1 секунду * 1000, что составляет 10000 миллисекунд.
Так что это правда, что наш ElButton updateRender выше занял 2390 секунд, но его нужно разделить на количество компонентов, чтобы не показывать ошибку!
давайте посмотрим на одинDemo, нажмите кнопку "Нажмите на меня", чтобы обновитьi
значение, вы можете увидеть вывод консоли3 btn updated
.
Подождите~ Подумайте 3 секунды, есть что-то странное?
1 секунда.
2 секунды. .
3 секунды. . .
Почему компонент кнопки обновляется? ! Не противоречит ли это эффективному механизму обновления, упомянутому в начале статьи? Из кода видно, что кнопка не используетсяi
переменная, то вi
До и после измененияНе должен вызывать обновлениеиз. То же самое верно и для кнопки в Verbatim Maker:
<div class="tool">
<el-button type="info" @click="handleLineModify(lineIndex)">修改</el-button>
<el-button type="danger" @click="handleLineDelete(lineIndex)">删除</el-button>
</div>
Две кнопки также обновляются из-за несвязанных обновлений данных состояния. Является ли все это искажением человеческой природы или потерей морали, следите за обновлениями сегодня в 23:59. . . Блин, извините за сумбур...
When you have eliminated the impossible, whatever remains,however improbable,must be the truth.
----Sherlock·Holmes
Теперь, когда мы подошли к этому, давайте углубимся в то, на что похож процесс обновления для Vue. (Здесь опущен процесс исследования исходного кода Vue длиной в 10 000 слов)
Наконец, он может находиться вElButton
до запуска обновления, потому чтоlyricMaker
Когда компонент переходит к процессу diffElButton
Когда этот элемент применяется,ElButton
из$forceUpadte
метод, тем самым вызывая лавину производительности.
Полный исходный код находится здесь:код
Как видно из исходного кода, есть две ситуации, вызывающие срабатывание принудительного обновления:
-
первый:
hasDynamicScopedSlot
Это правда.Что касается того, когда это значение истинно, это отдельная история, которую можно написать в статью.Я пока не буду ее здесь перечислять.Сейчас я в основном рассмотрю вторую. -
ВторойКогда у компонента есть дочерние элементы (статические, динамические), каждый diff будет принудительно обновляться, что является особенностью Vue!. Давайте посмотрим, что говорит автор Vue:GitHub.com/v UE JS/v UE/боюсь…
Дикие трансляционные бактерии: компонент, который содержит статический слот, поскольку возможно, что слот изменился после обновления состояния родительского компонента, поэтому этот дочерний компонент необходимо обновить один раз.
Но как сказал PR, в Vue3.0 этой проблемы не будет, все слоты будут унифицированы какscope slot
иметь дело с. В настоящее время у нас также есть менее элегантное решение для ручной установки всего статического содержимого вscope slot
. могу видеть этоDemo
мыслительные вопросы:
- Первое Демо выше, если поставить
<!-- updateCount: {{updateCount}} -->
Что произойдет, когда этот комментарий будет опубликован?
Суммировать
-
Иногда мы решаем проблему, или это может быть просто неправильно.
-
Иногда в нашем коде есть проблема, не баг, а фича!
Заявление об авторских правах: Оригинальная статья, если вам нужно перепечатать, пожалуйста, укажите источник "Эта статья была впервые опубликована вxlaoyu.info"