Эта статья участвует в "Боевой рекорд оптимизации производительности"Тема заявки на доклады
Оптимизация производительности — это проблема, с которой столкнется каждый разработчик.Особенно сейчас, когда все больше и больше внимания уделяется опыту, а конкуренция становится все более и более жесткой, для наших разработчиков это далеко не полная итерация и хорошая функция. далеко не достаточно.Самое главное сделать продукт хорошо,чтобы больше людей захотело его использовать,а пользователям было удобнее им пользоваться.Не в этом ли тоже воплощение ценности и способностей нашего разработчика. Уделение внимания проблемам с производительностью и оптимизация работы с продуктом гораздо ценнее, чем исправление нескольких безобидных ошибок.
В этой статье описаны некоторые из моих небольших навыков ежедневной разработки проектов Vue.Без лишних слов, давайте начнем.
1. Оптимизация производительности лонг-листа
1. Не реактивный
Например, списки участников, списки продуктов и т. д. — это чисто отображение данных.В сценариях, где не будет динамических изменений, нет необходимости реагировать на данные, что может значительно повысить скорость рендеринга.
такие как использованиеObject.freeze()
заморозить объект,Описание MDNОбъект, замороженный этим методом, не может быть изменен, то есть нельзя добавлять к объекту новые свойства, нельзя удалять существующие свойства, нельзя изменять перечисляемость, конфигурируемость и возможность записи существующих свойств объекта, а существующие свойства нельзя изменить. изменить значение свойства, а также прототип объекта не могут быть изменены.
export default {
data: () => ({
userList: []
}),
async created() {
const users = await axios.get("/api/users");
this.userList = Object.freeze(users);
}
};
Отзывчивый исходный адрес Vue2:src/core/observer/index.js - 144行
такое, что
export function defineReactive (...){
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {
return
}
...
}
Видно с самого начала судитьconfigurable
дляfalse
Прямой возврат не выполняет реактивную обработку
configurable
дляfalse
Указывает, что это свойство нельзя изменить, а замороженный объектconfigurable
Просто дляfalse
Vue3 добавляет отзывчивостиflag
, используемый для обозначения типа целевого объекта, подробные сведения об отзывчивости Vue3 можно найти в другой моей статье.
2. Виртуальная прокрутка
Если это длинный список больших данных, будет очень сложно создать слишком много DOM за один раз, если все они отрисовываются.В настоящее время виртуальная прокрутка может использоваться для отображения только небольшой части содержимого (включая видимую область), а затем при прокрутке постоянно заменять содержимое видимой области, чтобы имитировать эффект прокрутки
<recycle-scroller
class="items"
:items="items"
:item-size="24"
>
<template v-slot="{ item }">
<FetchItemView
:item="item"
@vote="voteItem(item)"
/>
</template>
</recycle-scroller>
Ссылаться наvue-virtual-scroller,vue-virtual-scroll-list
Принцип заключается в отслеживании событий прокрутки, динамическом обновлении DOM, который необходимо отобразить, и расчете смещения в представлении.Это также означает, что в процессе прокрутки требуется расчет в реальном времени, и есть определенные затраты.Поэтому если объем данных не большой, используйте обычную просто прокрутку
2. v-для обхода избегайте одновременного использования v-if
Почему вам следует избегать использования обоихv-for
а такжеv-if
в Vue2v-for
Приоритет выше, поэтому в процессе компиляции будут пройдены все элементы списка для генерации виртуального DOM, а затем через v-if будут отрисованы только те, которые удовлетворяют условиям, что приведет к пустой трате производительности, т.к. мы надеемся, что виртуальный DOM, не соответствующий условиям, будет отрендерен.
в Vue3v-if
Приоритет выше, что означает, что когда условием оценки является атрибут в списке, пройденном v-for, v-if не может его получить.
Таким образом, в некоторых сценариях, которые необходимо использовать одновременно, вы можете отфильтровать список, вычислив атрибут следующим образом.
<template>
<ul>
<li v-for="item in activeList" :key="item.id">
{{ item.title }}
</li>
</ul>
</template>
<script>
// Vue2.x
export default {
computed: {
activeList() {
return this.list.filter( item => {
return item.isActive
})
}
}
}
// Vue3
import { computed } from "vue";
const activeList = computed(() => {
return list.filter( item => {
return item.isActive
})
})
</script>
3. В списке используется уникальный ключ
Например, есть список, нам нужно вставить элемент в середину, что будет, если мы не используем ключ или используем индекс в качестве ключа? первый взгляд на картинку
как показаноli1
а такжеli2
Перерендеринга не будет, это не спорно. а такжеli3、li4、li5
будет повторно отображать
потому что не пользуюсьkey
или списокindex
так какkey
Когда отношение положения каждого элемента является индексом, результат на приведенном выше рисунке напрямую приводит к тому, что элемент, который мы вставили во все следующие элементы, и соответствующее отношение положения изменилось, поэтому все они будут выполняться в процессе исправления. затем повторно визуализировать. Это не то, что мы хотим, мы хотим отрендерить добавленный элемент, а остальные четыре элемента не вносят никаких изменений, поэтому не перерисовываем
при использовании толькоkey
В случае позиционное отношение, соответствующее каждому элементу, равноkey
, взгляните на использование уникальныхkey
стоимость
Как на картинкеli3
а такжеli4
Он не будет перерисовываться, потому что содержимое элемента не изменилось, и соответствующее позиционное отношение не изменилось.
Вот почему v-for должен писать ключ, и не рекомендуется использовать индекс массива в качестве ключа в разработке.
4. Повторно используйте DOM с v-show
v-show
: это визуализация компонента, а затем изменение отображения компонента на блокировку или отсутствие
v-if
: отображать или не отображать компонент
Таким образом, для сценариев, где условия могут часто изменяться, используйте v-show для экономии производительности, особенно чем сложнее структура DOM, тем больше преимуществ.
Однако у него есть и недостатки, то есть в начале v-show будут отрисовываться все компоненты внутри ветки, и будут выполняться соответствующие функции хука жизненного цикла, в то время как v-if будет загружать только те компоненты, которые попали по условию суждения, поэтому оно должно быть основано на разных сценариях.Используйте соответствующую команду
Например, используйте следующееv-show
Повторно используйте DOM, чемv-if/v-else
хороший эффект
<template>
<div>
<div v-show="status" class="on">
<my-components />
</div>
<section v-show="!status" class="off">
<my-components >
</section>
</div>
</template>
Принцип заключается в использовании v-if для запуска обновлений diff при изменении условий, и если старый и новый vnode окажутся несовместимыми, весь старый vnode будет удален.vnode
, а затем воссоздать новыйvnode
, затем создайте новыйmy-components
компонента, пройдет инициализацию самого компонента,render
,patch
и так далее, покаv-show
Когда условия меняются, старые и новыеvnode
Он непротиворечив, и ряд процессов, таких как удаление и создание, выполняться не будут.
5. Используйте функциональные компоненты для компонентов без состояния
Для некоторого чистого отображения, без реагирующих данных, без управления состоянием и без компонентов функций привязки жизненного цикла, мы можем установить их как функциональные компоненты для повышения производительности рендеринга, поскольку они будут рассматриваться как функция, поэтому накладные расходы очень низкие.
Принципpatch
процесс для функциональных компонентовrender
Сгенерированный виртуальный DOM не будет иметь рекурсивного процесса инициализации подкомпонента, поэтому накладные расходы на рендеринг будут намного ниже.
это приемлемоprops
, но не может использоваться внутри, так как экземпляр не будет созданthis.xx
Получить свойства компонента, записанные следующим образом
<template functional>
<div>
<div class="content">{{ value }}</div>
</div>
</template>
<script>
export default {
props: ['value']
}
</script>
// 或者
Vue.component('my-component', {
functional: true, // 表示该组件为函数式组件
props: { ... }, // 可选
// 第二个参数为上下文,没有 this
render: function (createElement, context) {
// ...
}
})
6. Сегментация подкомпонентов
Давайте посмотрим на пример
<template>
<div :style="{ opacity: number / 100 }">
<div>{{ someThing() }}</div>
</div>
</template>
<script>
export default {
props:['number'],
methods: {
someThing () { /* 耗时任务 */ }
}
}
</script>
В приведенном выше коде каждый раз, когда передается родительский компонентnumber
Каждый раз, когда происходит изменение, оно будет повторно отображаться и выполняться повторно.someThing
это трудоемкая задача
Таким образом, одна из оптимизаций заключается в использовании вычисляемых свойств, поскольку сами вычисляемые свойства имеют функцию кэширования вычисляемых результатов.
Второй — разделить на подкомпоненты, потому что обновление Vue — это компонентная гранулярность.Хотя первое изменение данных вызовет повторный рендеринг родительского компонента, подкомпонент не будет повторно рендериться, потому что изменений нет. в его внутренности, что занимает время. Естественно, задача не будет повторно выполняться, поэтому производительность лучше. Оптимизированный код выглядит следующим образом
<template>
<div>
<my-child />
</div>
</template>
<script>
export default {
components: {
MyChild: {
methods: {
someThing () { /* 耗时任务 */ }
},
render (h) {
return h('div', this.someThing())
}
}
}
}
</script>
7. Вариабельная локализация
Проще говоря, это сохранение переменных, на которые будут ссылаться несколько раз, потому что каждый доступthis.xx
, так как это отзывчивый объект, он будет срабатывать каждый разgetter
, а затем выполнить соответствующий код, который зависит от коллекции.Если вы используете переменную больше раз, производительность, естественно, ухудшится
По требованиям достаточно выполнить сбор зависимостей на переменную в функции, но многие по привычке пишут в проекте многоthis.xx
, игнорируяthis.xx
То, что вы делаете за кулисами, вызовет проблемы с производительностью
Например следующий пример
<template>
<div :style="{ opacity: number / 100 }"> {{ result }}</div>
</template>
<script>
import { someThing } from '@/utils'
export default {
props: ['number'],
computed: {
base () { return 100 },
result () {
let base = this.base, number = this.number // 保存起来
for (let i = 0; i < 1000; i++) {
number += someThing(base) // 避免频繁引用 this.xx
}
return number
}
}
}
</script>
8. Внедряйте сторонние плагины по запросу
НапримерElement-UI
Такую стороннюю библиотеку компонентов можно импортировать по запросу, чтобы она не была слишком большой, особенно когда проект небольшой, нет необходимости полностью вводить библиотеку компонентов
// main.js
import Element3 from "plugins/element3";
Vue.use(Element3)
// element3.js
// 完整引入
import element3 from "element3";
import "element3/lib/theme-chalk/index.css";
// 按需引入
// import "element3/lib/theme-chalk/button.css";
// ...
// import {
// ElButton,
// ElRow,
// ElCol,
// ElMain,
// .....
// } from "element3";
export default function (app) {
// 完整引入
app.use(element3)
// 按需引入
// app.use(ElButton);
}
9. Отложенная загрузка маршрута
Мы знаем, что Vue — это одностраничное приложение, поэтому, если вы не используете ленивую загрузку, это приведет к загрузке слишком большого количества контента при входе на домашнюю страницу.Если время слишком велико, появится длинный белый экран. , что не способствует взаимодействию с пользователем и SEO.
Таким образом, вы можете использовать ленивую загрузку, чтобы разделить страницы и загрузить соответствующие страницы, когда это необходимо, чтобы распределить давление загрузки домашней страницы и сократить время загрузки домашней страницы.
Ленивая загрузка без маршрутизации:
import Home from '@/components/Home'
const router = new VueRouter({
routes: [
{ path: '/home', component: Home }
]
})
Ленивая загрузка с маршрутизацией:
const router = new VueRouter({
routes: [
{ path: '/home', component: () => import('@/components/Home') },
{ path: '/login', component: require('@/components/Home').default }
]
})
При входе на этот маршрут он будет принимать соответствующиеcomponent
, затем запуститеimport
Скомпилируйте и загрузите компоненты, которые можно понимать какPromise
изresolve
механизм
-
import
: Спецификация синтаксиса Es6, вызов во время компиляции, процесс деструктурирования, не поддерживает функции переменных и т. д. -
require
: Спецификация AMD, вызов во время выполнения, представляет собой процесс присваивания, поддерживает функции вычисления переменных и т. д.
Для получения дополнительной информации о модульности внешнего интерфейса см. мою другую статьюПодробное резюме модульной спецификации внешнего интерфейса
10. сохранить кэшированные страницы
Например, после ввода следующего шага на странице ввода формы и последующего возврата к предыдущему шагу на странице формы содержимое ввода формы должно быть сохранено, например, в列表页>详情页>列表页
, такие сцены, которые прыгают туда-сюда и т.д.
Мы все можем использовать встроенные компоненты<keep-alive></keep-alive>
Кешировать компонент и не выгружать его при переключении компонента, чтобы при повторном возврате его можно было быстро отрендерить из кеша вместо повторного рендеринга для сохранения производительности
Просто оберните компоненты, которые вы хотите кэшировать
<template>
<div id="app">
<keep-alive>
<router-view/>
</keep-alive>
</div>
</template>
- также можно использовать
include/exclude
кэшировать/декэшировать указанные компоненты - Доступен в течение двух жизненных циклов
activated/deactivated
чтобы получить текущее состояние компонента
11. Разрушение событий
Когда компонент Vue уничтожается, он автоматически отвязывает все свои инструкции и прослушиватели событий, но только события самого компонента.
И для定时器
,addEventListener
Зарегистрированные слушатели и т. д. должны быть вручную уничтожены или развязаны в хуке жизненного цикла уничтожения компонента, чтобы избежать утечек памяти.
<script>
export default {
created() {
this.timer = setInterval(this.refresh, 2000)
addEventListener('touchmove', this.touchmove, false)
},
beforeDestroy() {
clearInterval(this.timer)
this.timer = null
removeEventListener('touchmove', this.touchmove, false)
}
}
</script>
12. Ленивая загрузка изображения
Ленивая загрузка изображений означает, что для страниц с большим количеством изображений для повышения скорости загрузки страницы загружаются только изображения в видимой области, а изображения за пределами видимой области загружаются после прокрутки до видимой области.
Эта функция поставляется с некоторыми фреймворками пользовательского интерфейса, если нет?
Порекомендуйте сторонний плагинvue-lazyload
npm i vue-lazyload -S
// main.js
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
// 接着就可以在页面中使用 v-lazy 懒加载图片了
<img v-lazy="/static/images/1.png">
Или создайте свои собственные колеса и вручную инкапсулируйте пользовательскую инструкцию.Здесь инкапсулируется версия, совместимая с каждым браузером, в основном для того, чтобы определить, поддерживает ли браузер или нет.IntersectionObserver
API, используйте его для реализации ленивой загрузки, если нет, используйте событие прокрутки мониторинга + дроссель для реализации
const LazyLoad = {
// install方法
install(Vue, options) {
const defaultSrc = options.default
Vue.directive('lazy', {
bind(el, binding) {
LazyLoad.init(el, binding.value, defaultSrc)
},
inserted(el) {
if (IntersectionObserver) {
LazyLoad.observe(el)
} else {
LazyLoad.listenerScroll(el)
}
},
})
},
// 初始化
init(el, val, def) {
el.setAttribute('data-src', val)
el.setAttribute('src', def)
},
// 利用IntersectionObserver监听el
observe(el) {
var io = new IntersectionObserver((entries) => {
const realSrc = el.dataset.src
if (entries[0].isIntersecting) {
if (realSrc) {
el.src = realSrc
el.removeAttribute('data-src')
}
}
})
io.observe(el)
},
// 监听scroll事件
listenerScroll(el) {
const handler = LazyLoad.throttle(LazyLoad.load, 300)
LazyLoad.load(el)
window.addEventListener('scroll', () => {
handler(el)
})
},
// 加载真实图片
load(el) {
const windowHeight = document.documentElement.clientHeight
const elTop = el.getBoundingClientRect().top
const elBtm = el.getBoundingClientRect().bottom
const realSrc = el.dataset.src
if (elTop - windowHeight < 0 && elBtm > 0) {
if (realSrc) {
el.src = realSrc
el.removeAttribute('data-src')
}
}
},
// 节流
throttle(fn, delay) {
let timer
let prevTime
return function (...args) {
const currTime = Date.now()
const context = this
if (!prevTime) prevTime = currTime
clearTimeout(timer)
if (currTime - prevTime > delay) {
prevTime = currTime
fn.apply(context, args)
clearTimeout(timer)
return
}
timer = setTimeout(function () {
prevTime = Date.now()
timer = null
fn.apply(context, args)
}, delay)
}
},
}
export default LazyLoad
Он используется так, сv-LazyLoad
заменятьsrc
<img v-LazyLoad="xxx.jpg" />
13. SSR
Я не практиковал это в проекте, поэтому не знаю, что делать.По оптимизации SSR можно прочитать эту статью:Подробное описание схемы оптимизации Vue-SSR, запишите сюда, сюда не переместится
Прекрасное прошлое
- Анализ исходного кода принципа адаптивности Vue3.2 и разница с адаптивностью Vue2.x
- Объясните алгоритм виртуального DOM и Diff простыми словами, а также разницу между Vue2 и Vue3.
- 7 видов Vue3 и 12 видов компонентной связи Vue2, достойные коллекции
- Что было обновлено в последней версии Vue3.2?
- Очки продвинутых знаний JavaScript
- Внешний мониторинг исключений и аварийное восстановление
- 20 минут, чтобы помочь вам победить HTTP и HTTPS и укрепить свою систему знаний HTTP
Эпилог
Если эта статья поможет вам немного, пожалуйста, поставьте лайк и поддержите ее, спасибо