Якщо ви коли-небудь розробляли, чи зараз розробляєте достатньо велику веб аплікацію, у якій більшість логіки та функціоналу закладена на стороні клієнтській (у бравзері), тоді є шанс, що у певний момент ви почали зауважувати певні проблеми з продуктвністю, швидкодією вашої програми. Особливо це може бути відчутно у довготермінових проектах, коли з кожним новим тижнем додається ціла купа нового функціоналу і відповідно веб сторінка стає все повільнішою і повільнішою.
Мені неодноразово приходилось оптимізувати повільні Javascript веб аплікації і в даній статті хочу поділитися кількома речима, що особливо запам’яталися.
Отже, якщо ваша веб сторінка буквально “замерзає” на 3, 5, 10 секунд одразу після завантаження чи в процесі роботи з нею, і ви вже спробували різноманітні швидкі способи її пришвидшення. Як от застосування порад від Google чи Yahoo, або можливо спробували автоматизовані сервіси для фронт-енд оптимізації сайтів як:
і тим не менше, відчутного покращення не має. Тоді саме час закатати рукава і трохи глибше копнути у власний код. Глянути чи можна щось пришвидшити у ваших стилях, html коді, ну і звичайно найважливіше – Javascript коді.
Давайте пройдемось по кожній із складових веб аплікації (веб сторінки) і я спробую надати ті поради та техніки, які зазвичай спрацьовували для мене. Думаю і вам також можуть стати у великій пригоді. Загалом мабуть нового нічого не розкажу, але підсумую найбільш ефективні речі, що добре працюють у багатій Javascript сторінці, з великою кількістю динамічних елементів та їх обробки.
Увага, в цій статті я не розглядаю оптимізації серверної сторони. Тут ми спробуємо розібратися і розглянути варіанти пришвидшення виключно клієнтської сторони веб аплікації.
Оптимізація Стилів – CSS
Одними з найбільших пожирачів ресурсів системи (зокрема бравзера) є стилі, що стосуються
- меж (border)
- тіней (shadow)
- градієнтів
- та фону (background)
Якщо у вас на сторінці велика кількість елементів, до яких застосовані вище перечислені правила – рекомендую їх позбуватися. Або хоча б значно зменшити їхню кількість на сторінці. Звичайно після цього наша сторінка може втратити свій суперський вигляд та вже не бути такою привабливою як раніше, тому прийдеться знаходити компроміси. Досить часто простіший дизайн може мати кращий вигляд, якщо постаратись.
В кінці кінців – краще швидка, але не надто гарна програма, ніж із супер дизайном, яким ніхто не користується через її тормознутість 😉
І ще один швидкий хак, який може стати у пригоді, якщо у вас є велика кількість зображень на сторінці. Додавши властивість:
1 2 3 |
img { translateZ: 0; } |
до ваших зображень на сторінці, ви трішки обдурите бравзер і заставите його скористатися графічним процесором комп’ютера користувача (GPU) для їх відмальовування. Таким чином звільняючи центральний процесор машини (CPU) для того, щоб займатися іншими важливими процесами для відтворення вашої веб сторінки.
Оптимізація розмітки – HTML
Звичайно чим більше HTML елементів (тегів) на сторінці, тим більше потрібно нашому товаришу-бравзеру попрацювати, щоб відрендирити HTML текст (розмітку) у DOM – ієрархічне дерево об’єктів. Зрозуміло, щоб оптимізувати тут швидкість потрібно зменшити кількість тегів на сторінці. Але ми чудово знаємо, що це значно важче завдання, ніж наприклад спростити дизайн через оптимізацію стилів, адже HTML це вміст і функціонал нашої програми!
Проте навіть тут можемо щось спробувати. Найефективніший підхід – це завантажувати не все одразу із сервера, а по-трохи, по мірі необхідності. Але про це поговоримо пізніше в секції Javascript. А поки-що можу лише порадити три фішки, яких слід притримуватися у вашому HTML коді, щоб неускладнювати і так складне життя бравзера 🙂 :
- заберіть з вашої аплікації будь-який HTML код, що не несе ніякої контентної нагрузки, а лише слугує покращення та фіксам по візуальній презентації вашої сторінки. Як от непотрібні вкладені div теги, таблиці, і інші зайві елементи. Все це вже давно можна зробити з допомогою одного лише CSS. HTML – це контент вашої сторінки, а не її форма, вигляд і дизайн;
- тримайте ваш HMTL код чистим, простим і валідним. Поламані, незакриті теги, неправильне використання вкладених тегій, недозволені атрибути, чи відсутність обов’язкових атрибутів – все це примушує наш бравзер працювати довше і закривати за нами наші ж дірки;
- по можливості уникайте використання тегу iframe. Навіть коли порожній, він блокує завантаження сторінки. Та й і не є він семантичним тегом.
Javascript
Як не старайтесь, що не робіть, а одними лише оптимізаціями HTML та CSS коду у багатій на Javascript код аплікації – не обійтись. Зазвичай винувантцем номер один повільної “замерзлої” (завислої) сторінки є наш Javascript код.
Що не працює
Почну із штуки, яку пробував не раз, але зрозумів для себе, що таки не допомагає. В більшості коду дотепер використовую бібліотеку jQuery (думаю всі веб прогери знайомі з нею 😉 і колись був впевнений, що скористатись рідними функціями мови Javascript (напр. innerHTML і getElementsByClassName) буде значно швидше для бравзера, аніж використовувати подібний функціонал з бібліотеки jQuery (відповідно функції html/append, і jQuery селектори + find). Проте, кілька раз спробувавши, навіть на величезній кількості елементів, переконався, що ефект мізерний. Що ще раз підтверджує те, що jQuery не дураки писали :-)))
Тепер, що працює:
Мінімум Маніпуляцій з DOM-ом
Одне з найважливішим правил. Перед тим як вставляти новий елемент на сторінку з вашого Javascript коду, проробіть з ним усі необхідні зміни, і аж потім у самому кінці вставте його в структуру документу (DOM). Кожен раз, коли ви робите будь-яку мінімальну зміну над існуючим на сторінці HTML тегом (який на цей момент вже є живим DOM об’єктом), ви заставляєте бравзер переоглянути і пере-рендерити велику (а деколи і всю) частину сторінки. Тому будь-яка зміна існуючих на сторінці елементів є досить важкою операцією для бравзера.
Приклад поганого коду, коли ми вставляємо кожен новий елемент окремо на сторінку, тобто багато разів оновлюємо структуру документу, тим самим заставляючи працювати бравзер більше:
1 2 3 4 5 6 |
var body = jQuery('body'); var friends = ['John', 'Merry', 'Michael', 'Nick']; for (var name, i=0; name=friends[i]; i++) { body.append('<div>' + name + '</div>'); } |
і тепер це й же ж код, але з меншою кількістю DOM змін (лише з одною маніпуляцією з DOM-ом):
1 2 3 4 5 6 7 8 9 10 |
var body = jQuery('body'); var friends = ['John', 'Merry', 'Michael', 'Nick']; // збираємо і готуємо html код в масив, і в кінці лише додаємо в // існуючий код на сторінці одним махом var html = []; for (var name, i=0; name=friends[i]; i++) { html.push('<div>' + name + '</div>'); } body.append(html.join('')) |
Якщо у масиві friends буде не 4 елементи, а сотні, тисячі, тоді ефект буде дуже і дуже помітний. Тобто, мова Javascript – швика, а от механізм оновлення DOM дерева – значно повільніший. Десь так.
Прогресивне Завантеження Елементів
Навіть вище наведена порада по зменшенню кількості модифікацій дерева елементів на сторінці далеко не завжди вирішить усі проблеми. Часто логіки дійсно багато на сторінці, часто елементів дуже багато на сторінці, ну і звичайно найбільш ймовірно – обробників різноманітних подій буде ще більше! Це і кліки мишкою, і натискання клавіш, і скролінг, і просто рух курсором мишки над елементами на сторінці.
Для того, щоб зменшити час, що бравзер затрачає на обробку і встановлення усіх цих елементів та їхній подій, дуже доцільно розглянути техніку Прогресивного Завантеження Елементів та їхньої обробки.
Прогресивно (частково, порціями, по необхідності) можна робити як мінімум дві речі:
- завантажувати елементи початково на сторінку. Погодьтесь, навіщо завантажувати одразу список з 1000 елементів, якщо на сторінці (без скролу) видно лише перші 20? Тобто ми завантажуємо лише ці 20 елементів, а вже решту довантажуємо із сервера, чи вставляємо Javascript кодом, по потребі. Наприклад, при скролінгу вниз сторінки. Приклад: twitter лєнта, facebook лєнта новин.
- другим важливим сповільнювачем, якому можемо дати відстрочку – навішування обробників подій на елементи на сторінці. Думаєте при початковому завантаженні сторінки користувач одразу захоче все переклікати? Звісно ні! Добре продумайте, що є найважливішим і найчастіше використовуванішим функціоналом на вашій сторінці? Туди і привішайте ваші початкові обробники подій, кліки, курсори, скролінг якщо потрібно, і т.д. А все решта почекає…
Отже, розібралися що можемо завантажувати, і вставляти на сторінку з відстрочкою. Тепер давайте коротенько розглянемо як це можна робити і що використовувати:
Одним із популярних інструментів є бібліотка, що надає ряд утиліт для асинхронного Javascript коду. Однією з таких утиліт у цій бібліотеці є Черга (queue), яка і дозволяє ставити в чергу той ваш код, що може почекати кілька секунд допоки першочергові елементи сторінки ще тільки запускаються.
Ось коротенький приклад використання утиліти queue для асинхронного додавання елементів на сторінку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var body = jQuery('body'); // створюємо нашу асинхронну чергу, працюватиме у 2 паралельних потоки var q = async.queue(function (task, callback) { console.log('initialized ' + task.name); body.append('<div>' + task.name + '</div>'); callback(); }, 2); // закидуємо два завдання в чергу q.push({name: 'first element'}, function (err) { console.log('finished processing foo'); }); q.push({name: 'second element'}, function (err) { console.log('finished processing bar'); }); |
Що такий підхід дає? А те, що бравзер не формує перший елемент і другий, а аж тоді відображає усі зміни на сторінки, коли другий доданий. Він додає перший елемент у body, рендерить, тоді готує другий елемент, вставляє і рендерить. Тобто допоки готуються і обробляються наступні елементи, людина уже може користуватися першим елементом!
Якщо ж влом розбиратися з новими бібліотеками та інструментами, можна скористатися старими добрими setTimeout та setInterval рідними бравзерними функціями. Все залежить від складності задачі, та ваших конкретних потреб та вимог проекту.
Отак коротенько ніби і усе. Є маса інших фішок і технік. Описав лише те, що найперше згадалось, і те, що найбільше пригодилось у моїх проектах. Надіюсь вище наведені підказки стануть вам також у пригоді!
А які техніки і підходи ви знаєте? Доводилось вам пришвидшувати вашу повільну веб сторінку? Що найкраще і у яких ситуаціях спрацювало у ваших проектах, а що не працює?
Будьмо дружніми!