Код должен не просто запуститься, а просто сказать одну ерунду: кодовое слово найти не просто 👍, 🙇🙇🙇.
Демонстрационный код написан на Vue3 + ts + Vite, но также содержит советы по оптимизации, применимые к Vue2. Если оптимизация применима только к Vue3 или Vue2, я отмечу это в заголовке.
Оптимизация кода
Используйте ключ в v-для
использоватьv-for
При обновлении отображаемого списка элементов по умолчанию используется стратегия повторного использования на месте; при изменении данных списка она будет основываться наkey
значение, чтобы определить, изменено ли значение, если изменено, повторно визуализировать этот элемент, в противном случае повторно использовать предыдущий элемент;
Примечания по использованию ключей:
- Не используйте возможные дубликаты или возможные варианты
key
значение (консоль также выдаст напоминание) - Если данные в массиве имеют состояние, которое необходимо поддерживать (например, поле ввода), не используйте свойства массива.
index
так какkey
значение, потому что если элемент вставлен или удален из массива, индекс элемента за ним изменится, что является неправильным состоянием привязки, когда Vue повторно используется на месте. - Если в массиве нет доступного уникального значения ключа, и массив не обновляется полностью, но при вставке или удалении данных с помощью push и splice, вы можете рассмотреть возможность добавления в него ключевого поля.Значение Symbol() может обеспечить уникальность .
Когда использовать какой ключ?
Это очень деликатный вопрос, в первую очередь нужно знать原地复用
(наверное虚拟dom
изменить, два虚拟dom节点
изkey
Если это то же самое, узел не будет воссоздан, но будет изменен исходный узел)
Когда данные, которые мы отображаем, не должны поддерживать состояние, например, обычная простая постраничная визуализация таблицы (не включает ввод, только отображение), дополнительная загрузка раскрывающегося списка и другие сценарии, тогда используйтеindex
так какkey
Это даже лучше, потому что при входе на следующую или предыдущую страницу предыдущий узел будет повторно использоваться на месте, а не создаваться заново.id
так какkey
Вместо этого DOM будет воссоздан, а производительность будет относительно низкой.
В дополнение к использованиюindex
так какkey
Я также должен стараться избегать таких операций, как добавление/удаление в середине массива, которые повлияют на ключевые изменения следующих элементов. Это заставит Vue думать, что все следующие элементы изменились, что приводит к избыточному контрасту и повторному использованию на месте.
Таким образом, использование индекса в качестве ключа должно удовлетворять:
- Данные не имеют независимого состояния
- Данные не будут добавлены/удалены и другие операции, которые повлияют на ключевые изменения следующих элементов
Когда использовать id в качестве ключа?
для большинства данныхid
единственный, этот несомненно одинkey
предпочтительный ответ. Использование в большинстве случаевid
так какkey
не будет отображаться вышеbug
. Но если вам нужно подумать о производительности, вам нужно подумать о том, следует ли вам использовать повторное использование на месте.
Это то же самое, что и отображение данных пейджинга выше, если вы используетеid
так какkey
, можно предположить, что каждая часть данных на каждой страницеid
не совпадают, поэтому при изменении страницы два虚拟DOM树
узлаkey
совершенно непоследовательно,vue
Исходный узел удаляется и создается новый узел. Не исключено, что эффективность будет еще ниже. Но есть у него и свои преимущества. Толькоkey
может помочьdiff
Он более точно связывает для нас состояние, что особенно подходит для сценариев, в которых данные имеют независимые состояния, например данные списка с полями ввода или переключателями.
Итак, когда использоватьid
так какkey
? Всего один момент:
- Недоступно
index
так какkey
когда
Использовать ключ в v-if/v-else-if/v-else
Многие люди могут игнорировать этот пункт
Причина: по умолчанию Vue максимально эффективно обновляет DOM. Это означает, что когда он переключается между элементами одного и того же типа, он будет исправлять существующие элементы вместо того, чтобы удалять старый и добавлять новый в то же место. Неожиданные побочные эффекты могут возникнуть, если неидентичные элементы идентифицируются как идентичные.
Если есть только один v-if и нет v-else или v-if-else, ключ добавлять не нужно.
По сравнению с ключом v-for, ключ в v-if/v-else-if/v-else относительно прост, мы можем напрямую написать фиксированную строку или массив
<transition>
<button
v-if="isEditing"
v-on:click="isEditing = false"
>
Save
</button>
<button
v-else
v-on:click="isEditing = true"
>
Edit
</button>
</transition>
.v-enter-active, .v-leave-active {
transition: all 1s;
}
.v-enter, .v-leave-to {
opacity: 0;
transform: translateY(30px);
}
.v-leave-active {
position: absolute;
}
Например, для приведенного выше кода вы обнаружите, что, хотя эффект перехода добавлен к кнопке, переход не может быть запущен, если не добавлен клавишный переключатель.
Не используйте v-for и v-if вместе (Vue2)
Этот метод оптимизации ограничен Vue2, приоритет v-for и v-if был скорректирован в Vue3.
Все это знают
никогда не ставь
v-if
а такжеv-for
Используется на одном и том же элементе в одно и то же время.Привести кРуководство по стилю Vue2.x
Причина в том,v-for
Приоритет выше, чемv-if
, поэтому, когда они используются на одной и той же этикетке, каждый рендеринг сначала будет зацикливаться, а затем будет выполняться условное суждение.
Уведомление:в vue3
v-if
приоритет надv-for
, так когдаv-for
а такжеv-if
Эффект похож наVue2
средняя ручкаv-if
Эффект от повышения
Например, следующий код находится вVue2
не рекомендуется,Vue
Также будет вынесено соответствующее предупреждение.
<ul>
<li v-for="user in users" v-if="user.active">
{{ user.name }}
</li>
</ul>
Мы должны изо всех сил старатьсяv-if
Перейдите на более высокий уровень или используйте вычисленные свойства для обработки данных
<ul v-if="active">
<li v-for="user in users">
{{ user.name }}
</li>
</ul>
Если вы не хотите еще один цикл содержимого контейнера без более высокого уровня, вы можете использоватьtemplate
быть его родительским элементом,template
не будет отображаться браузером какDOM
узел
Если я хочу оценить содержимое каждого элемента в объекте обхода, чтобы выбрать визуализированные данные, я могу использоватьcomputed
фильтровать пройденные объекты
// js
let usersActive = computed(()=>users.filter(user => user.active))
// template
<ul>
<li v-for="user in usersActive">
{{ user.name }}
</li>
</ul>
Разумный выбор v-if и v-show
v-if
а такжеv-show
Разница всем знакома;v-if
Контролируйте отображение и скрытие элементов, напрямую манипулируя удалением и добавлением DOM;v-show
управляя DOMdisplay
Знакомство с CSS для управления отображением и скрытием элементов
Поскольку производительность операций добавления/удаления в DOM намного ниже, чем у свойств CSS, управляющих DOM
Поэтому, когда элемент нуждается в частых изменениях отображения/скрытия, мы используемv-show
для повышения производительности.
Когда элемент не нуждается в частых изменениях отображения/скрытия, мы передаемv-if
удаление DOM может сэкономить браузеру ресурсы, необходимые для рендеринга части DOM.
Используйте простые вычисляемые свойства
Сложные вычисляемые свойства должны быть разбиты на как можно больше простых свойств.
легко проверить
Когда каждое вычисляемое свойство состоит из очень простого выражения с небольшим количеством зависимостей, проще написать тесты, чтобы убедиться, что оно работает правильно.
легко читать
Упрощение вычисляемых свойств требует, чтобы вы давали каждому значению описательное имя, даже если оно не может использоваться повторно. Это позволяет другим разработчикам (и вам в будущем) сосредоточиться на коде, который им небезразличен, и понять, что происходит.
Лучше «Принять перемены»
Любое значение, которое может быть названо, может быть использовано в представлении. Например, мы можем захотеть отобразить сообщение, сообщающее пользователям, сколько они сэкономили, или мы можем захотеть рассчитать налоги, но они могут быть представлены отдельно, а не как часть общей цены.
Небольшие целенаправленные вычисляемые свойства уменьшают гипотетические ограничения на использование информации, поэтому при изменении требований требуется меньше рефакторинга.
Привести кРуководство по стилю Vue2
вычисленный всем знаком, он будет пересчитан при изменении реактивных данных, отправленных в его выражении. Если мы напишем более сложное выражение в вычисляемом свойстве, реактивные данные, от которых оно зависит, станут сколь угодно большими. Все выражение необходимо переоценивать при изменении любой из зависимостей.
let price = computed(()=>{
let basePrice = manufactureCost / (1 - profitMargin)
return (
basePrice -
basePrice * (discountPercent || 0)
)
})
При изменении любого значения из поля ManufacturingCost, profitMargin, DiscountPercent будет пересчитана вся цена.
Но если мы изменим его на следующее
let basePrice = computed(() => manufactureCost / (1 - profitMargin))
let discount = computed(() => basePrice * (discountPercent || 0))
let finalPrice = computed(() => basePrice - discount)
Если при изменении DiscountPercent будут пересчитаны только Discount и finalPrice, т.к.computed
из缓存特性
, не будет пересчитывать basePrice
функциональные функциональные компоненты (Vue2)
Обратите внимание, что это только в качестве средства оптимизации в Vue2, 3.x, существует разница в производительности между сборкой, а функциональное состояние компонентов было значительно уменьшено, и в большинстве случаев применение незначительно. Поэтому на SFCS
functional
Миграционный путь для разработчиков - это удалить этот атрибут и заменитьprops
Все ссылки на переименованы в$props
,Будуattrs
переименовать в$attrs
.
До оптимизации
<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>
<script>
export default {
props: ['value'],
}
</script>
- Нет этого (нет экземпляра)
- нет ответных данных
Разделить компоненты
Какой? Написанный вами файл vue содержит более тысячи строк кода? 🤔
Разумное разделение компонентов может не только оптимизировать производительность, но и сделать код более читабельным. принцип единой функции
полученный изslides.com/AK Let yum/v UE from…
До оптимизации
<template>
<div :style="{ opacity: number / 300 }">
<div>{{ heavy() }}</div>
</div>
</template>
<script>
export default {
props: ['number'],
methods: {
heavy () { /* HEAVY TASK */ }
}
}
</script>
Оптимизировано
<template>
<div :style="{ opacity: number / 300 }">
<ChildComp/>
</div>
</template>
<script>
export default {
props: ['number'],
components: {
ChildComp: {
methods: {
heavy () { /* HEAVY TASK */ }
},
render (h) {
return h('div', this.heavy())
}
}
}
}
</script>
Поскольку обновление Vue — это гранулярность компонентов, хотя каждый кадр вызывает повторный рендеринг родительского компонента посредством модификации данных, ноChildComp
Но он не будет перерисовываться, потому что внутри него нет реагирующих изменений данных. Таким образом, оптимизированный компонент не будет выполнять трудоемкие задачи при каждом рендере.
использовать локальные переменные
До оптимизации
<template>
<div :style="{ opacity: start / 300 }">{{ result }}</div>
</template>
<script>
import { heavy } from '@/utils'
export default {
props: ['start'],
computed: {
base () { return 42 },
result () {
let result = this.start
for (let i = 0; i < 1000; i++) {
result += heavy(this.base)
}
return result
}
}
}
</script>
Оптимизировано
<template>
<div :style="{ opacity: start / 300 }">
{{ result }}</div>
</template>
<script>
import { heavy } from '@/utils'
export default {
props: ['start'],
computed: {
base () { return 42 },
result () {
const base = this.base
let result = this.start
for (let i = 0; i < 1000; i++) {
result += heavy(base)
}
return result
}
}
}
</script>
Здесь в основном представлены рассчитанные свойства компонентов до и после оптимизации.
result
различия в реализации, к компонентам до оптимизации обращаются несколько раз в процессе расчетаthis.base
, тогда как оптимизированный компонент будет использовать локальные переменные перед вычислениемbase
, кешthis.base
, затем прямой доступbase
.Итак, почему эта разница вызывает разницу в производительности, причина в том, что каждый раз, когда вы посещаете
this.base
когда, потому чтоthis.base
является реактивным объектом, поэтому вызовет егоgetter
, а затем выполните логический код, связанный со сбором зависимостей. Подобная логика выполняется больше, как в примере, сотни циклов обновляют сотни компонентов, каждый компонент срабатываетcomputed
При пересчете и многократном выполнении логики, связанной со сбором зависимостей, производительность, естественно, упадет.С точки зрения спроса,
this.base
Достаточно один раз выполнить сбор зависимостей, поместить егоgetter
Результат оценки возвращается в локальную переменнуюbase
, зайдите позжеbase
не сработает, когдаgetter
, и не будет следовать логике сбора зависимостей, а производительность, естественно, улучшится.Привести кДемистификация девяти советов по оптимизации производительности для Vue.js
Используйте KeepAlive
Когда некоторые компоненты с высокой стоимостью рендеринга необходимо часто переключать, его можно использоватьkeep-alive
кэшировать этот компонент
В использованииkeep-alive
после того, какkeep-alive
После того, как обернутый компонент визуализируется в первый раз,vnode
И DOM будет кешироваться, и тогда при следующем повторном рендеринге компонента он получит соответствующие данные прямо из кешаvnode
и DOM, а затем рендерить, нет необходимости снова проходить инициализацию компонента,render
а такжеpatch
Подождите, пока ряд процессов уменьшитсяscript
Время выполнения, лучшая производительность.
Уведомление:Злоупотребление keep-alive только сделает ваше приложение более медленным, потому что оно будет занимать много памяти в течение длительного времени.
уничтожение событий
Когда компонент уничтожается, мы должны очистить глобальные события и таймеры, добавленные в компонент, чтобы предотвратить утечку памяти.
HOOK Vue3 позволяет нам писать объявление события и уничтожение вместе, что делает его более читабельным.
function scrollFun(){ /* ... */}
document.addEventListener("scroll", scrollFun)
onBeforeUnmount(()=>{
document.removeEventListener("scroll", scrollFun)
})
Vue2 все еще может пройти$once
Добиться такого эффекта, конечно, можно и вoptionsAPI
ПЕРЕД УНИЧТОЖЕНИЕМ SINICR, но я рекомендую написать первый, потому что последний сделает код одной и той же функции более рассредоточенным.
function scrollFun(){ /* ... */}
document.addEventListener("scroll", scrollFun)
this.$once('hook:beforeDestroy', ()=>{
document.removeEventListener("scroll", scrollFun)
})
function scrollFun(){ /* ... */}
export default {
created() {
document.addEventListener("scroll", scrollFun)
},
beforeDestroy(){
document.removeEventListener("scroll", scrollFun)
}
}
загрузка изображения
Отложенная загрузка изображений: подходит для ситуаций, когда на странице много изображений и не все изображения отображаются на одном экране, плагин vue-lazyload предоставляет нам очень удобную инструкцию отложенной загрузки изображений v-lazy
Однако не все изображения подходят для отложенной загрузки, такие как баннеры, фотоальбомы и т. д. Более рекомендуется использовать технологию предварительной загрузки изображений для загрузки предыдущего и следующего изображений отображаемого в данный момент изображения.
Используйте подходящий тип изображения
Использовать веб-формат: Об этом и говорить нечего.Все мы знаем, что преимущества WebP заключаются в том, что он имеет лучший алгоритм сжатия данных изображения, который может привести к уменьшению размера изображения, и имеет качество изображения, неразличимое невооруженным глазом; качество изображения как без потерь, так и с потерями.Режим сжатия, альфа-прозрачность и характеристики анимации, эффект преобразования в JPEG и PNG достаточно превосходны, стабильны и однородны.
Используйте Interleaved GIF или Progressive JPEG: Еще один способ оптимизировать взаимодействие с пользователем — использовать изображения в формате GIF с чересстрочной разверткой или прогрессивные (Progressive Encoding) изображения JPEG. Прогрессивные файлы JPEG являются первымиразмыто, а потом постепенно стало понятно.
Разница между базовым JPEG и прогрессивным JPEG:
Есть два способа сохранить формат файла JPEG. Это базовый JPEG и прогрессивный JPEG.
Оба формата имеют одинаковый размер и данные изображения, и их расширение одинаково, единственная разница заключается в том, как они отображаются.
- Baseline JPEG
- Progressive JPEG
Прогрессивные преимущества JPEG:
- Пользовательский опыт Файл jpeg, закодированный прогрессивным способом, рендеринг в браузере размыт до четкости. Пользователь может получить обратную связь о желаемой информации в градиентном изображении. Если содержимое не соответствует ожиданиям пользователя, пользователь может перейти на новую страницу раньше времени.
- Размер файла Эксперименты показали, что при размере файла JPEG менее 10 КБ файл JPEG, использующий стандартную кодировку (оптимизирована таблица Хаффмана), меньше, чем файл JPEG, использующий кодировку градиента (вероятность появления 75%). Для файлов размером более 10 КБ файлы JPEG с градиентным кодированием с вероятностью 94% будут меньше, чем файлы со стандартным кодированием.
Сократите ненужные ответные данные
Всем известно, что отзывчивые данные в vue требуют дополнительной привязки к ним функций get и get обработки, если какие-то ваши данные не будут меняться или вы не хотите, чтобы их изменения вызывали какие-то побочные эффекты (обновление представлений или другое). Обычно я бы определил его так.
export default {
data() {
this.version = '10'; // 不会被做响应式处理
return {
/* ... */
}
}
}
Советы: на самом деле этот метод не самый лучший, потому что он будет привязывать данные напрямую к экземпляру vue, а vue предпочитает, чтобы данные можно было унифицировать в данных, а затем получить доступ через проксирование к экземпляру vue. Поэтому лучшим способом должно быть замораживание данных в данных.
В Vue3 это не может быть решено описанным выше методом, потому что гранулярность Proxy proxy — это весь объект, а не определенное свойство.
Используйте разумные алгоритмы обработки данных
Это относительно сравнивает силу структур данных и алгоритмов
Например, метод, преобразующий массив в многоуровневую структуру.
/**
* 数组转树形结构,时间复杂度O(n)
* @param list 数组
* @param idKey 元素id键
* @param parIdKey 元素父id键
* @param parId 第一级根节点的父id值
* @return {[]}
*/
function listToTree (list,idKey,parIdKey,parId) {
let map = {};
let result = [];
let len = list.length;
// 构建map
for (let i = 0; i < len; i++) {
//将数组中数据转为键值对结构 (这里的数组和obj会相互引用,这是算法实现的重点)
map[list[i][idKey]] = list[i];
}
// 构建树形数组
for(let i=0; i < len; i++) {
let itemParId = list[i][parIdKey];
// 顶级节点
if(itemParId === parId) {
result.push(list[i]);
continue;
}
// 孤儿节点,舍弃(不存在其父节点)
if(!map[itemParId]){
continue;
}
// 将当前节点插入到父节点的children中(由于是引用数据类型,obj中对于节点变化,result中对应节点会跟着变化)
if(map[itemParId].children) {
map[itemParId].children.push(list[i]);
} else {
map[itemParId].children = [list[i]];
}
}
return result;
}
разное
Помимо вышеперечисленных методов, есть еще множество техник оптимизации, но я не очень часто их использую в проектах🤣
- Заморозить объекты (чтобы данные, которые не должны быть реактивными, не стали реактивными)
- Рендеринг длинного списка — пакетный рендеринг
- Рендеринг длинного списка — Динамический рендеринг (vue-virtual-scroller)
- ...
Над оптимизацией сгиба/объема
В моем проекте у меня в основном есть следующие направления оптимизации для оптимизации первого экрана
- объем
- разделение кода
- Интернет
оптимизация объема
-
Код сжатой упаковки:
webpack
а такжеvite
Упаковка производственной среды сжимает ваш код по умолчанию, что обычно не требует специальной обработки.webpack
Его также можно реализовать вручную через соответствующий плагин сжатия. -
Отмена
source-map
:Вы можете проверить, есть ли файл .map в упакованном продукте, и если да, то можетеsource-map
Значение установлено значение false для отключения или пустого отображения кода (это настоящий объем, занятый большим) -
пакет включен
gizp
сжатие:Это требует, чтобы сервер также включил разрешениеgizp
трансмиссию, иначе бесполезно включать(webpack
соответствующийgzip
Плагин сжатия, менее версияwebpack
Плагин сжатия может быть другим, рекомендуется сначала проверить на официальном сайте)
разделение кода
Роль сегментации кода заключается в разделении упакованного продукта на небольшие продукты, которые зависят отesModule
. поэтому, когда вы используетеimport()
для импорта файла или зависимости, то файл или зависимость будут упакованы отдельно как небольшой продукт.路由懒加载
а также异步组件
Все используют этот принцип.
- Отложенная загрузка маршрута
- Асинхронные компоненты
Для библиотек пользовательского интерфейса я обычно не использую компоненты загрузки по запросу, а предпочитаю метод оптимизации, представленный CDN.
Интернет
CDN:Первый — введение упомянутой выше CDN, локальная библиотека используется на этапе разработки, а конфигурация外部扩展(Externals)
Исключите эти зависимости при упаковке. Затем импортируйте их через CDN в html файл
Пуш сервера:HTTP2 является относительно зрелым; после введения CDN выше мы можем использовать функцию HTTP2 Server Push для веб-сайтов, чтобы позволить браузерам загружать эти CDN и другие файлы заранее.
Включите gzip:Это было сказано выше, принцип заключается в том, что когда и клиент, и сервер поддерживают передачу gzip, сервер сначала отправит сжатый файл gzip, а затем клиент получит его и распаковает.
Включить кеширование:Как правило, я использую кеш согласования, но это применимо не ко всем ситуациям, например, для файлов, использующих Server Push, имя файла не может быть произвольно изменено. Поэтому я обычно также исправляю имя файла основного файла производства
Оптимизация пользовательского опыта
Мы можем улучшить взаимодействие с пользователем до загрузки основного файла. Это может сократить время белого экрана.
Однако следует отметить, что существует много ресурсов, которые необходимо загрузить при первой загрузке страницы.Если ресурсы, связанные с загрузкой, размещены в DOM, ресурсы загрузки могут быть заблокированы другими ресурсами.
Поэтому рекомендуется, чтобы код css или js, связанный с загрузкой, был встроен в заголовок html, чтобы обеспечить загрузку соответствующих css и js при отображении загрузки. И не рекомендуется использовать высокопроизводительную или высокопроизводительную логику переваривания сети при загрузке, что впоследствии продлит время парсинга или загрузки других ресурсов.