Velo: Рендеринг на стороне сервера и API данных Warmup

Узнайте, как оптимизировать получение данных и сократить время загрузки сайта 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

Оцените статью
devanswers.ru
Добавить комментарий