Существует ли какая-либо техника оптимизации пакетов, которая позволит избежать включения полифиллового кода, если браузеру он не нужен?

Вопрос или проблема

В качестве примера, вот некоторые полифиллы, которые у меня есть для CookieChangeEvent, который в данный момент не поддерживается Firefox и Safari.

import Cookies, {CookieAttributes} from "js-cookie"


export function listenForCookieChange(cookieName: string, onChange: (newValue: string | null) => void) : (() => void) {

  // Если cookieStore доступен, мы можем использовать слушатель события изменения
  if("cookieStore" in window) {
    const changeListener = (event) => {
      console.log(event)

      const foundCookie = event.changed.find((cookie) => cookie.name === cookieName);
      if (foundCookie) {
        onChange(foundCookie.value);
        return
      }

      const deletedCookie = event.deleted.find((cookie) => cookie.name === cookieName);
      if(deletedCookie) {
        onChange(null);
      }
    };

    cookieStore.addEventListener("change", changeListener);

    // Мы возвращаем функцию очистки для эффекта
    return () => {
      cookieStore.removeEventListener("change", changeListener);
    };
  }

  // Если cookieStore недоступен, мы опрашиваем на изменения. 
  const interval = setInterval(() => {
    const cookie = Cookies.get(cookieName);
    if(cookie) {
      onChange(cookie);
    } else {
      onChange(null);
    }
  }, 1000);


  // Мы все равно возвращаем функцию очистки для эффекта
  return () => {
    clearInterval(interval);
  }
}

Если браузер не поддерживает CookieStore, тогда я использую js-cookie для удобных методов работы с cookies.

Признаю, что js-cookie занимает всего 800 байт в сжатом виде, так что в этом конкретном случае размер пакета является пустяком.

Но мне приходит в голову, что для приложения с множеством этих полифиллов, пользователи браузеров, которые поддерживают эти API, получат пакет с кодом, который они никогда не используют.

Существуют ли какие-либо движения в современных сборщиках, которые будут разбивать библиотеки полифиллов на части на основе user agent?

Динамический импорт решил бы эту проблему:

export async function listenForCookieChange(cookieName: string, onChange: (newValue: string | null) => void) : Promise<(() => void)> {

  // Если cookieStore доступен, мы можем использовать слушатель события изменения
  if("cookieStore" in window) {
    //snip
  } 
  const Cookies = await import("js-cookie"); 
  // логика полифилла здесь
}

Но это требует преобразования синхронных функций в асинхронные, и если мы не загрузим модули предварительно где-либо, это приведет к более медленному опыту для пользователей полифиллов.

Ответ или решение

Вопрос оптимизации бандлов и исключения полифиллов из сборки в зависимости от поддержки браузером определённых API становится всё более актуальным в современных веб-разработках. Давайте рассмотрим несколько подходов, которые можно использовать для достижения этой цели.

1. Динамический Импорт

Как вы уже отметили, динамический импорт является одним из эффективных способов загружать полифиллы только в тех случаях, когда это необходимо. Преобразование функции в асинхронную может быть не идеальным решением для всех случаев, но это отличный способ исключения ненужного кода из бандла.

export async function listenForCookieChange(cookieName: string, onChange: (newValue: string | null) => void): Promise<() => void> {
  if ("cookieStore" in window) {
    // Логика для cookieStore
  }
  const Cookies = await import("js-cookie"); 
  // Логика полифилла на основе js-cookie
}

2. Использование Babel и Полифиллов

Инструменты, такие как Babel, предоставляют возможность полифилла только для тех функций, которые действительно необходимы. С использованием плагина @babel/preset-env и его опции useBuiltIns, можно автоматически включать полифиллы только для тех браузеров, которые их требуют, что позволяет сократить размер итогового бандла.

Для этого в вашем babel.config.js вы можете настроить:

module.exports = {
  presets: [
    [
      '@babel/preset-env',
      {
        useBuiltIns: 'entry',
        corejs: 3,
      },
    ],
  ],
};

Это позволит Babel автоматически подключать необходимые полифиллы на основе целевых версий браузеров, определённых в browserslist.

3. Split Chunks с помощью Webpack

Если вы используете Webpack, обратите внимание на функционал оптимизации разделения кода (code splitting). Вы можете настроить Webpack для создания отдельных чанков для полифиллов.

В вашем webpack.config.js вы можете добавить:

optimization: {
  splitChunks: {
    cacheGroups: {
      polyfills: {
        test: /[\\/]node_modules[\\/]js-cookie[\\/]/,
        name: 'polyfills',
        chunks: 'all',
      },
    },
  },
},

Таким образом, Webpack будет собирать полифиллы в отдельный бандл, который будет загружаться только при необходимости.

4. Пользовательская Логика и Условия

Если вы хотите полностью избежать загрузки обременительных полифиллов, рассматривайте возможность разработки логики проверки возможностей браузера. Например, вы можете проверять наличие определённых функций или API перед загрузкой полифилла:

if (!('cookieStore' in window)) {
  const Cookies = await import('js-cookie');
  // Логика полифилла
}

Заключение

Существует множество способов оптимизации работы с полифиллами и сокращения размера бандлов в зависимости от возможностей браузера. Динамический импорт, настройка Babel и использования Webpack для разделения кода — это практичные решения, позволяющие избежать включения ненужного кода в бандл.

Важно следить за производительностью и удобством использования кода, чтобы избежать негативного влияния на пользовательский опыт, особенно для пользователей старых браузеров.

Оцените материал
Добавить комментарий

Капча загружается...