Узнайте, как оптимизировать получение данных и сократить время загрузки сайта Wix
В Velo мы используем метод $w.onReady()
в качестве начальной точки для взаимодействия со страницей. Этот метод гарантирует, что все элементы страницы закончили загрузку и мы можем с ними взаимодействовать. Жизненный цикл сайта Velo включает два запуска метода $w.onReady()
.
Первый запуск обратного вызова $w.onReady()
происходит на стороне сервера, когда сервер создает HTML-страницу. Сервер выполняет код Velo и помещает результат в HTML (если это возможно).
Второй запуск происходит на стороне клиента в браузере, когда страница сайта загрузилась.
Справочник по API Velo: Рендеринг
Когда это возможно, процесс рендеринга разделяется на две части для повышения производительности. Первый цикл процесса происходит в коде на стороне сервера, а второй цикл — в коде на стороне клиента. Если это невозможно на стороне сервера, весь процесс рендеринга происходит на стороне клиента.
Давайте поиграем с SSR, чтобы понять, как это работает.
Например, у нас есть следующий код:
$w.onReady(function () {
$w('#text1').text = 'Hello!';
});
Код будет выполнен на стороне сервера, затем результат будет добавлен в HTML страницу. И страница будет отправлена на сторону клиента со вставленными данными.
API рендеринга
Мы можем управлять этапами цикла рендеринга с помощью API wixWindow.rendering.env
.
Свойство env
возвращает backend
при рендеринге на стороне сервера и browser
при рендеринге на стороне клиента.
Давайте обновим код, чтобы увидеть это. Это строка со значением env и меткой времени.
import { rendering } from 'wix-window';
$w.onReady(function () {
$w('#text1').text = `${rendering.env}: ${Date.now()}`;
});
Теперь, когда мы перезагружаем страницу, мы видим, что HTML-контент имеет значение backend
. Когда страница закончила загрузку, мы видим значение browser
, это второй запуск $w.onReady()
на стороне клиента обновляет значение текста.
SSR & время выполнения браузера
Это выглядит просто.
Асинхронная операция
А как насчет асинхронных операций?
Если мы хотим дополнить SSR какой-либо асинхронной операцией, мы должны дождаться выполнения обещания.
Давайте посмотрим на пример. Создает запрос для получения элементов из базы данных и печатает их в виде строки.
import wixData from 'wix-data';
$w.onReady(function () {
// Async request to database
wixData.query('goods').find()
.then((data) => {
$w('#text1').text = JSON.stringify(data.items);
});
});
Как мы видим, SSR не работает ни с какими асинхронными операциями. Когда мы перезагружаем страницу, мы видим в редакторе текст по умолчанию, который содержит элемент Text. А через некоторое время мы видим элементы базы данных. Это второй запуск обратного вызова $w.onReady()
на стороне клиента.
Сайт без рендеринга на стороне сервера с динамическими данными
Я использую дросселирование сети в Chrome DevTools для снижения скорости интернета. Это может быть полезно для отладки.
Это произошло потому, что $w.onReady()
не ждет выполнения обещания на стороне сервера. Сервер не дожидается результата запроса и отправляет HTML-страницу с содержимым по умолчанию.
Исправить это очень просто, нужно дождаться результата обещания. $w.onReady()
поддерживает функции обратного вызова async. Давайте обновим код с помощью операторов async/await
.
import wixData from 'wix-data';
$w.onReady(async function () {
const data = await wixData.query('goods').find();
$w('#text1').text = JSON.stringify(data.items);
});
Теперь мы видим, что SSR начинает работать. И сервер отрисовал HTML-страницу с элементами базы данных.
Сайт с рендерингом на стороне сервера для динамических данных
❗ не забудьте отключить дросселирование сети после тестирования 😉
Длинные асинхронные вызовы замедляют производительность сайта
Мы должны быть осторожны, используя $w.onReady()
с асинхронным обратным вызовом. Длинные асинхронные задачи замедляют рендеринг страницы.
Например, мы добавляем в обратный вызов обещание с задержкой в 5 секунд.
$w.onReady(async function () {
// a delay for 5 seconds
await new Promise((r) => setTimeout(r, 5000));
$w('#text1').text = Date.now().toString();
});
Если мы запустим этот код, то увидим, что сервер будет ждать 5 секунд. А после загрузки HTML-страницы на клиенте мы снова ждем 5 секунд, прежде чем увидим результат.
Мы дважды ждем выполнения обещания на сервере и на клиенте.
Инспектор сети, время загрузки HTML-страницы с сервера с задержкой в 5 секунд
API Warmup Data
Используя Warmup Data API, мы можем передавать данные с кодом страницы с сервера и читать эти данные на клиенте.
Справочник по API Velo:
API Warmup Data используется для оптимизации загрузки данных для сайтов, которые отображаются как на сервере, так и в браузере, что позволяет выполнять дорогостоящие операции по выборке данных только один раз.
Velo: Пример API Warmup Data
import { warmupData, rendering } from 'wix-window';
// Set data on the server-side
if (rendering.env === 'backend') {
warmupData.set('my-key', 'server data');
}
// Get data on the client-side
if (rendering.env === 'browser') {
const data = warmupData.get('my-key');
console.log(data); // -> "server data"
}
Мы можем использовать Warmup Data для сокращения запросов к базе данных. Там мы сохраняем ответ запроса в warmupData
на сервере и читаем его на клиенте без дополнительного запроса к базе данных.
Реализация функции использования Warmup Data
Мы реализуем функцию, которая будет включать рендеринг на стороне сервера и использовать Warmup Data для предотвращения повторного запроса данных на стороне клиента, это сокращает время ожидания.
Создайте файл для функции util.
Добавьте файл в раздел public на боковой панели
public
└── warmupUtil.js
Это функция-обертка. Она имеет два аргумента:
- Первый аргумент — key: Это уникальный ключ, соответствующий данным для Warmup Data.
- Второй аргумент — func: Это асинхронная функция, результат которой мы хотим использовать с данными Warmup Data.
public/warmupUtil.js
import { warmupData, rendering } from 'wix-window';
export const warmupUtil = async (key, func) => {
// On the server-side
if (rendering.env === 'backend') {
// Get data
const data = await func();
// Set the warmup data on the server-side
warmupData.set(key, data);
return data;
}
// On the client-side
// Get the warmup data on the client-side
const data = warmupData.get(key);
// Checking a cached data exist
if (data) {
return data;
}
// If we don't have cache data from the server,
// then we do a backup call on the client
return func();
};
На сервере он ожидает результат асинхронной функции и устанавливает его в Warmup Data.
На клиенте он использует данные из Warmup Data. Если у него нет данных (какой-то сбой на сервере), он вызовет func на клиенте.
Параллельное выполнение для нескольких асинхронных задач
Мы должны помнить эффект $w.onReady()
при загрузке страницы. Если мы хотим использовать несколько async-функций в $w.onReady()
callback, то мы должны избегать использования их в очереди по очереди.
Например, если каждая из этих async-функций выполняется в течение 100 миллисекунд, то $w.onReady()
придется ждать 300 миллисекунд для полного выполнения всех этих функций.
// ❌ wrong approach!!
$w.onReady(async function () {
const one = await warmupUtil('one-async-func', oneAsyncFunc); // ⏳ 100 ms
const two = await warmupUtil('two-async-func', twoAsyncFunc); // ⏳ 100 ms
const three = await warmupUtil('three-async-func', threeAsyncFunc); // ⏳ 100 ms
// ⏳ wait one by one (100 ms * 3) = 300 ms
$w('#text1').text = JSON.stringify({ one, two, three });
});
Мы можем объединить кучу обещаний с помощью Promise.all()
, выполнять их параллельно и ждать, пока все они будут готовы.
// ✅ parallel asynchronous execution
$w.onReady(async function () {
const [one, two, three] = await Promise.all([
warmupUtil('one-async-func', oneAsyncFunc),
warmupUtil('two-async-func', twoAsyncFunc),
warmupUtil('three-async-func', threeAsyncFunc),
]);
// ⏳ wait 100 ms. Parallel execution of all promises
$w('#text1').text = JSON.stringify({ one, two, three });
});
Фрагменты кода
Вот фрагмент кода с аннотацией JSDoc. И пример использования.
public/warmupUtil.js
import { warmupData, rendering } from 'wix-window';
/**
* @template T
* @param {string} key
* @param {() => Promise<T>} func
* @returns {Promise<T>}
*/
export const warmupUtil = async (key, func) => {
if (rendering.env === 'backend') {
const data = await func();
warmupData.set(key, data);
return data;
}
const data = warmupData.get(key);
if (data) {
return data;
}
return func();
};
Вкладка Код страницы
import { warmupUtil } from 'public/warmupUtil';
const getGoods = async () => {
const { items } = await wixData.query('goods').find();
return items;
};
$w.onReady(async function () {
const items = await warmupUtil('goods-items', getGoods);
$w('#text1').text = JSON.stringify(items);
});
Ресурсы
- API рендеринга
env
- API данных разминки
- Velo: О процессе рендеринга страницы
Посты
- Добавление горячих клавиш на сайт Wix
- Глобальные определения типов
- Типовая безопасность вашего кода с помощью JSDoc
- Повторяющиеся обработчики событий элементов v2.0