[Перевод] ES6-модули в браузере: готовы они уже или нет?

Слышали об использовании ES6-модулей в браузере? Собственно — это обычные ES6-модули. Только применяются они в коде, предназначенном для браузеров.

ylq1wlx8jeaaomnc9kwintirrma.jpeg
Если кто не знает — вот как это выглядит.

Имеется страница index.html:



  
    
               


Есть файл index.mjs, представляющий собой модуль, подключённый к странице:

import { doSomething } from './utils.mjs';

doSomething('Make me a sandwich');


Этот модуль, в свою очередь, импортирует функцию doSomething из модуля utils.mjs:

export const doSomething = message => {
  console.log('No.');
};


ES6-модули существуют уже много лет. И, возможно, вы подумываете о том, чтобы их попробовать. Я пользовался ими что-то около месяца в собственном проекте. При этом я пришёл к выводу о том, что…

Саспенс!

О поддержке модулей браузерами


Прежде чем я расскажу о том, что я понял, предлагаю взглянуть на поддержку модулей браузерами. Этот материал я пишу в начале 2020 года, поэтому уровень поддержки модулей составляет довольно внушительные 90%.

484cfd9e669b731f483cfdd99a638777.png


Поддержка модулей браузерами по сведениям caniuse.com

Меня вполне устраивают эти 90% (я не принимаю во внимание нужды 10% пользователей), хотя вы, возможно, хотите быть более ответственным. Но, даже учитывая это, если ваш проект не рассчитан на IE, или UC, или Opera Mini, такой уровень поддержки означает, что практически 100% вашей целевой аудитории сможет без проблем работать с вашим проектом.

Правда, поддержка модулей браузерами — это только начало. Здесь я хочу найти ответ на три вопроса, которые возникли у меня в самом начале пути в сторону модулей:

  • Есть ли у использования модулей в браузерах какие-нибудь плюсы?
  • А минусы?
  • Уверен, у меня был и третий вопрос, но сейчас я его вспомнить не могу.


Давайте с этим разберёмся…

Каковы плюсы использования модулей в браузерах?


Это — чистый JavaScript! Никакого конвейера сборки проекта, никакого 400-строчного конфигурационного файла Webpack, никакого Babel, никаких плагинов и пресетов, и никаких дополнительных 45 npm-модулей. Только вы и ваш код.

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

Итак. Плюсы ES6-модулей — это чистый JavaScript, это отсутствие сборки, конфигурирования проекта, это отказ от ставших ненужными зависимостей. А что ещё? Есть же что-то ещё?

Нет, больше ничего.

Каковы минусы использования модулей в браузерах?


▍Сравнение модулей и бандлеров


Размышляя о том, стоит ли использовать ES6-модули в браузере, на самом деле, приходится выбирать между использованием модулей и бандлеров наподобие Webpack, Parcel или Rollup.

Можно (вероятно) использовать и то и другое, но в реальности, если планируется пропускать код через бандлер, нет причины применять конструкцию . Хэш в имени файла при этом не используется. Как в такой ситуации предполагается сообщать браузеру о том, откуда ему загружать index.mjs — из кэша или из сети?

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

  • Надо установить заголовок всех ответов cache-control в значение no-cache. Невероятно, но no-cache — это не значит «не кэшировать». Это означает, что файл кэшировать надо, но файл не должен «использоваться для удовлетворения последующего запроса без успешной проверки на исходном сервере».
  • Надо использовать служебный заголовок ETag. Если не знаете — это (обычно) хэш содержимого файла, который, в виде заголовка, отправляют вместе с файлом. Как правило, 38fd9e перемещают из имени файла в заголовок.
  • Нужно пользоваться хорошим CDN-сервисом с кэшем, которым удобно управлять. Браузер будет проверять заголовки ETag для каждого файла при каждой загрузке сайта. Таких проверок будет очень много. Поэтому используемый CDN-сервис должен быть быстрым. И кэш его нужно будет обновлять (записывая в него новые заголовки ETag) при каждом выпуске новой версии сайта. (Это, например, делается автоматически на хостинге Firebase).
  • Нужно настроить сервис-воркер, действующий в роли кэша, находящегося «на шаг позади» реальной ситуации. Он будет перехватывать все запросы и отдавать то, что уже есть в кэше, а потом, в фоне, обновлять кэш из сети.


В результате, когда посетитель повторно зайдёт на сайт, браузер заявит: «Мне нужно загрузить файл index.mjs. Вижу, в моём кэше этот файл уже есть, его ETag — 38fd9e. Запрошу этот файл у сервера, но скажу ему, чтобы он прислал мне его только в том случае, если его ETag — не 38fd9e». Сервис-воркер этот запрос перехватит, проигнорирует ETag и вернёт index.mjs из своего кэша (этот файл попал в кэш тогда, когда страница загружалась в прошлый раз). Затем сервис-воркер перенаправит запрос к серверу. Сервер вернёт либо сообщение о том, что файл не изменился, либо файл, который будет сохранён в кэше.

На мой взгляд — всё это очень уж хлопотно.

Вот, если кому интересно, код сервис-воркера:

self.addEventListener('fetch', e => {
  e.respondWith(
    (async () => {
      // Загрузить ресурс из кэша и обновить кэш
      const cacheResponse = await caches.match(e.request);
      
      // Обновить кэш (асинхронно), возвращая кэшированный ответ
      // (ETag-заголовки предотвратят ненужные загрузки файлов)
      fetch(e.request).then(fetchResponse => {
        caches
          .open(CACHE_NAME)
          .then(cache => cache.put(e.request, fetchResponse));
      });
      
      if (cacheResponse) return cacheResponse;
      
      return fetch(e.request);
    })()
  );
});


Я ленился, поэтому не изучил и не использовал новейшее свойство FetchEvent.navigationPreload. Дело в том, что к тому моменту я потратил больше времени на кэширование, чем на написание приложения (к вашему сведению — я потратил на эти дела, соответственно, 10 и 11 часов).

Да, хочу отметить, что предложение «карты импорта» направлено на решение некоторых из вышеописанных проблем. Оно позволяет организовать нечто вроде мэппинга index.js на index.38fd9e.mjs. Но для генерирования хэша, всё равно, понадобится некий сборочный конвейер, карты импорта придётся внедрять в HTML-файл. Это означает, что тут понадобится бандлер… Собственно говоря — при таком раскладе модули в браузере уже не нужны.

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

Но ведь, вероятно, не все используют бандлеры?


Я писал этот материал, исходя из предположения о том, что все пишут код в модулях, применяя конструкции import/export или require, а затем собирают код в продакшн-бандлы с помощью Webpack, Grunt, Gulp или чего-то такого.

Существуют ли разработчики, которые не пользуются бандлерами? Есть ли кто-то, кто размещает свой JavaScript-код во множестве файлов и отправляет их в продкшн без бандлинга? Может быть один из таких людей — вы? Если так — мне хотелось бы узнать всё о вашей жизни.

Итоги


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

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

Если завтра я начну работу над новым проектом, то в голове у меня не возникнет вопроса о том, надо ли запускать старый добрый create-react-app. Все первоначальные настройки займут секунд тридцать, и хотя начальный размер проекта в 40 Кб чуть великоват, для большинства сайтов это не сыграет никакой роли.

А вот — другая ситуация. Предположим, мне нужно было бы собрать воедино немного HTML/CSS и JavaScript для некоего эксперимента, и при этом такой эксперимент представлял бы собой проект, включающий в себя чуть больше файлов, чем «несколько». Если, работая над этим проектом, я не планировал бы тратить время на настройку системы для его сборки, тогда я, возможно, воспользовался бы модулями в браузере. И то — если бы меня не волновала производительность этого проекта.

Мне интересно было бы узнать о том, как Webpack и родственные ему инструменты поступают с ES6-модулями в браузере. Полагаю, что в будущем, когда предложение по поводу «карт импорта» получит достаточную поддержку, бандлеры, возможно, будут использовать их как механизм абстрагирования неприглядных хэшей, которые используются в наши дни.

Уважаемые читатели! Пользовались ли вы ES6-модулями в браузере?

1ba550d25e8846ce8805de564da6aa63.png

© Habrahabr.ru