AssetMapper: простое и современное управление CSS и JS

Дата обновления перевода 2024-07-25

AssetMapper: простое и современное управление CSS и JS

Компонент AssetMapper позволяет писать современные JavaScript и CSS без сложностей использования бандлера. Браузеры уже поддерживают многие современные функции JavaScript вроде утверждения import и классов ES6. А протокол HTTP/2 означает, что объединение ваших ресурсов для уменьшения количества HTTP-соединений больше не является насущной необходимостью. Этот компонент является легким слоем, который помогает выдавать ваши файлы непосредственно браузеру.

Компонент AssetMapper имеет две основные функции:

  • Маппирование и версионирование ресурсов : Все файлы внутри assets/ доступны публично и являются версионированными. Например, вы можете сослаться на assets/styles/app.css в шаблоне с {{ asset('styles/app.css') }}. Финальный URL будет содержать хеш версии, вроде /assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css.
  • Importmaps : Нативная функция браузера, которая облегчает использование утверждения JavaScript import (например, import { Modal } from 'bootstrap) без системы построения. Это поддерживается во всех браузерах (благодаря shim) и является стандартом W3C.

Установка

Чтобы установить компонент AssetMapper, выполнте:

1
$ composer require symfony/asset-mapper symfony/asset symfony/twig-pack

В дополнение к symfony/asset-mapper, это также гарантирует, что у вас доступны Компонент Asset и Twig.

Если вы используете Symfony Flex , вы закончили! Рецепт только что добавил следующие файлы:

  • assets/app.js Ваш главный файл JavaScript;
  • assets/styles/app.css Ваш главный файл CSS;
  • config/packages/asset_mapper.yaml То, где вы определяете ваш ресурс "paths";
  • importmap.php Ваш файл конфигурации importmap.

Он также обновил файл templates/base.html.twig:

1
2
3
{% block javascripts %}
+    {% block importmap %}{{ importmap('app') }}{% endblock %}
{% endblock %}

Если вы не используете Flex, вам нужно будет создать и обновить эти файлы вручную. Смотрите последний рецепт asset-mapper, чтобы узнать точное содержание этих файлов.

Маппирование и ссылание на ресурсы

Компонент AssetMapper работает путём определения каталогов/путей ресурсов, которые вы хотите сделать публичными. Эти ресурсы затем версионируются и на них легко сослаться. Благодаря файлу asset_mapper.yaml, ваше приложение запускается с одним маппированым путём: каталогом assets/.

Если вы создадите файл assets/images/duck.png, вы можете сослаться на него в шаблоне с помощью:

1
<img src="{{ asset('images/duck.png') }}">

Путь - images/duck.png - относительный к вашему маппируемому каталогу (assets/). Это известно как логический путь к вашему ресурсу.

Если вы посмотрите на HTML в вашей странице, URL будет чем-то вроде: /assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png. Если вы обновите этот файл, часть версии URL изменится автоматически!

Подача ресурсов в разработке против производства

В окружении dev, URL - /assets/images/duck-3c16d9220694c0e56d8648f25e6035e9.png - обрабатывается и возвращается вашим приложением Symfony.

Для окружения prod, до развёртывания, вы должны выполнить:

1
$ php bin/console asset-map:compile

Это физически скопирует все файлы из ваших маппированных каталогов в public/assets/, чтобы они выдавались напрямую вашим веб-сервером. Смотрите Развёртывание , чтобы узнать больше.

Tip

Если вам нужно скопировать скомпилированные ресурсы в другое место (например, загрузить их в S3), создайте сервис, реализующий Symfony\Component\AssetMapper\Path\PublicAssetsFilesystemInterface и установите его идентификатор сервиса (или псевдоним) как asset_mapper.local_public_assets_filesystem (чтобы заменить встроенный сервис).

Отладка: как увидеть все маппированные ресурсы

Чтобы увидеть все маппированные ресурсы в вашем приложении, выполните:

1
$ php bin/console debug:asset-map

Это отобразит вам все маппированые пути и ресурсы внутри каждого из них:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Пути AssetMapper
----------------

--------- ------------------------------
 Путь      Префикс с пространством имён
--------- ------------------------------
assets

Маппированные ресурсы
---------------------

------------------ ----------------------------------------------------
 Логический путь    Путь файловой системы
------------------ ----------------------------------------------------
 app.js             assets/app.js
 styles/app.css     assets/styles/app.css
 images/duck.png    assets/images/duck.png

"Логический путь" - это путь, который следует использовать при ссылании на ресурс, например, из шаблона.

Importmaps и написание JavaScript

Все современные браузеры поддерживают утверждение import JavaScript и современные функции ES6, вроде классов. Поэтому этот код "просто работает":

1
2
3
4
5
// assets/app.js
import Duck from './duck.js';

const duck = new Duck('Waddles');
duck.quack();
1
2
3
4
5
6
7
8
9
// assets/duck.js
export default class {
    constructor(name) {
        this.name = name;
    }
    quack() {
        console.log(`${this.name} says: Quack!`);
    }
}

Благодаря функции Twig {{ importmap() }}, о которой вы узнаете в этом разделе, файл assets/app.js загружается и выполняется браузером.

Tip

При импорте относительных файлов обязательно добавляйте расширение .js. В отличие от Node, в окружении браузера расширение является обязательным.

Импорт сторонних пакетов JavaScript

Предположим, вы хотите использовать пакет npm, например bootstrap. Технически, это можно сделать, импортировав его полный URL-адрес, например, из CDN:

1
import { Alert } from 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/+esm';

Но ой! Необходимость добавления этого URL-адреса - это боль! Вместо этого мы можем добавить его в нашу "importmap" с помощью команды importmap:require. Эту команду можно использовать для загрузки любого пакета npm:

1
$ php bin/console importmap:require bootstrap

Это добавляет пакет bootstrap к вашему файлу importmap.php:

1
2
3
4
5
6
7
8
// importmap.php
return [
    // ...

    'bootstrap' => [
        'url' => 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/+esm',
    ],
];

Note

Иногда пакет - например, bootstrap - имеет одну или несколько зависимостей, например, @popperjs/core. Команда importmap:require добавит как основной пакет, так и его зависимости. Если пакет включает основной CSS-файл, он также будет добавлен (см. Работа со сторонним CSS ).

Note

Если вы получаете ошибку 404, возможно, возникла проблема с пакетом JavaScript, которая не позволяет ему обслуживаться CDN jsDelivr. Например, в
в пакете могут отсутствовать такие свойства, как main или module в
файле конфигурации package.json. Попробуйте связаться с сопровождающим пакета и попросить его исправить эти проблемы.

Теперь вы можете импортировать пакет bootstrap как обычно:

1
2
import { Alert } from 'bootstrap';
// ...

Все пакеты в importmap.php загружаются в каталог assets/vendor/, который должен игнорироваться git (рецепт Flex добавляет его в .gitignore для вас). Чтобы загрузить файлы на других компьютерах,
если некоторые из них отсутствуют, нужно выполнить следующую команду:

1
$ php bin/console importmap:install

Вы можете обновить сторонние пакеты до их актуальных версий, выполнив команду:

1
2
3
4
5
6
7
8
# перечисляет устаревшие пакеты и отображает их последние версии
$ php bin/console importmap:outdated
# обновляет все устаревшие пакеты
$ php bin/console importmap:update

# вы такожм можете выполнять команды только для заданного списка пакетов
$ php bin/console importmap:update bootstrap lodash
$ php bin/console importmap:outdated bootstrap lodash

Как работает importmap?

Как этот файл importmap.php позволяет вам импортировать bootstrap? Это происходит благодаря функции Twig {{ importmap() }} в base.html.twig, которая выводит importmap:

1
2
3
4
5
6
7
<script type="importmap">{
    "imports": {
        "app": "/assets/app-4e986c1a2318dd050b1d47db8d856278.js",
        "/assets/duck.js": "/assets/duck-1b7a64b3b3d31219c262cf72521a5267.js",
        "bootstrap": "https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/+esm"
    }
}</script>

Импортируемые карты - это встроенная функция браузера. Они работают во всех браузерах благодаря файлу "shim", который автоматически включается компонентом AssetMapper (все современные браузеры поддерживают их нативно).

Когда вы импортируете bootstrap из вашего JavaScript, браузер просмотрит importmap и увидит, что он должен получить пакет из URL.

Но откуда взялась запись импорта /assets/duck.js? Отличный вопрос!

Файл assets/app.js выше импортирует ./duck.js. Когда вы импортируете файл с помощью относительного пути, ваш браузер ищет этот файл относительно того, что импортирует его. Таким образом, он будет искать /assets/duck.js. Этот URL был бы правильным, за исключением того, что файл duck.js является версионированным. К счастью, компонент AssetMapper видит этот импорт и добавляет маппирование из /assets/duck.js к правильному, версионированному имени файла. Результат: импорт ./duck.js просто работает!

Функция importmap() также выводит ES module shim, чтобы более старые браузеры понимали importmaps (см. конфигурацию polyfill ).

Точка входа "app" и предварительная загрузка

"Точка входа" - это основной файл JavaScript, который загружает браузер, и ваше приложение по умолчанию начинается с одного из них:

1
2
3
4
5
6
7
8
// importmap.php
return [
    'app' => [
        'path' => './assets/app.js',
        'entrypoint' => true,
    ],
    // ...
];

В дополнение к карте импорта, { importmap('app') }} в base.html.twig выводит несколько других вещей, в том числе:

1
<script type="module">import 'app';</script>

Эта строка говорит браузеру загрузить запись карты импорта app , что приводит к выполнению кода в файле assets/app.js.

Функция importmap() также выводит набор «предварительных загрузок»:

1
2
<link rel="modulepreload" href="/assets/app-4e986c1a2318dd050b1d47db8d856278.js">
<link rel="modulepreload" href="/assets/duck-1b7a64b3b3d31219c262cf72521a5267.js">

Это оптимизация производительности, о которой вы можете узнать ниже в Производительность: Добавление предварительной загрузки .

Импорт конкретных файлов из стороннего пакета

Иногда вам потребуется импортировать конкретный файл из пакета. Например, допустим, вы интегрируете highlight.js и хотите импортировать только ядро и определенный язык:

1
2
3
4
5
import hljs from 'highlight.js/lib/core';
import javascript from 'highlight.js/lib/languages/javascript';

hljs.registerLanguage('javascript', javascript);
hljs.highlightAll();

В этом случае добавление пакета highlight.js в ваш файл importmap.php не сработает: что бы вы ни импортировали - например, highlight.js/lib/core - должно точно совпадать с записью в файле importmap.php.

Вместо этого используйте importmap:require и передайте ему точные пути, которые вам нужны. Этот пример также показывает, как можно требовать несколько пакетов одновременно:

1
$ php bin/console importmap:require highlight.js/lib/core highlight.js/lib/languages/javascript

Глобальные переменные вроде jQuery

Возможно, вы привыкли полагаться на глобальные переменные, вроде переменной $ в jQuery:

1
2
3
4
5
// assets/app.js
import 'jquery';

// app.js или любой другой файл
$('.something').hide(); // НЕ БУДЕТ РАБОТАТЬ!

Но в модульном окружении (например, в AssetMapper), когда вы импортируете библиотеку типа jquery, она не создаёт глобальную переменную. Вместо этого, вам следует импортировать её и установить как переменную в каждом файле, где она вам нужна:

1
2
import $ from 'jquery';
$('.something').hide();

Вы даже можете сделать это из тега встроенного скрипта:

1
2
3
4
<script type="module">
    import $ from 'jquery';
    $('.something').hide();
</script>

Если вам нужно сделать что-то глобальной переменной, сделайте это вручную изнутри app.js:

1
2
3
import $ from 'jquery';
// речі у "window" стають глобальними змінними
window.$ = $;

Работа с CSS

CSS можно добавить на страницу, импортировав его из файла JavaScript. По умолчанию assets/app.js уже импортирует assets/styles/app.css:

1
2
3
4
// assets/app.js
import '../styles/app.css';

// ...

Когда вы вызываете importmap('app') в base.html.twig, AssetMapper анализирует assets/app.js (и все JavaScript-файлы, которые он импортирует) в поисках утверждений import для CSS-файлов. Итоговая коллекция CSS-файлов выводится на на страницу в виде тегов link в том порядке, в котором они были импортированы.

Note

Импорт CSS-файла не поддерживается встроенными JavaScript модулями. AssetMapper делает это, добавляя специальную карту импорта для каждого CSS-файла. Эти специальные записи валидны, но ничего не делают. AssetMapper добавляет тег <link> для каждого CSS-файла, но когда JavaScript выполняет утверждение import, ничего дополнительного не происходит.

Работа со сторонним CSS

Иногда пакет JavaScript содержит один или несколько файлов CSS. Например, пакет bootstrap содержит файл dist/css/bootstrap.min.css.

Вы можете требовать CSS-файлы так же, как и файлы JavaScript:

1
$ php bin/console importmap:require bootstrap/dist/css/bootstrap.min.css

Чтобы включить его на страницу, импортируйте его из файла JavaScript:

1
2
3
4
// assets/app.js
import 'bootstrap/dist/css/bootstrap.min.css';

// ...

Tip

Некоторые пакеты, например bootstrap, рекламируют, что содержат файл CSS.
В этих случаях, когда вы importmap:require bootstrap, файл CSS также добавляется в файл importmap.php, для удобства. Если какой-то пакет не рекламирует свой CSS-файл в свойстве style файла конфигурации package.json, попробуйте связаться с сопровождающим пакета и попросить его добавить это свойство.

Пути внутри CSS-файлов

Внутри CSS вы можете ссылаться на другие файлы, используя обычную функцию CSS url().
и относительного пути к целевому файлу:

1
2
3
4
5
/* assets/styles/app.css */
.quack {
    /* file lives at assets/images/duck.png */
    background-image: url('../images/duck.png');
}

Путь в финальном файле app.css будет автоматически включать версионированный URL
для duck.png:

1
2
3
4
/* public/assets/styles/app-3c16d9220694c0e56d8648f25e6035e9.css */
.quack {
    background-image: url('../images/duck-3c16d9220694c0e56d8648f25e6035e9.png');
}

Использование Tailwind CSS

Чтобы использовать CSS-фреймворк Tailwind с компонентом AssetMapper, ознакомьтесь с
symfonycasts/tailwind-bundle

Использование Sass

Чтобы использовать Sass вместе с компонентом AssetMapper, ознакомьтесь с symfonycasts/sass-bundle.

Ленивый импорт CSS из файла JavaScript

Если у вас есть CSS, который вы хотите загрузить лениво, вы можете сделать это с помощью обычного, «динамического» синтаксиса импорта:

1
2
3
4
// assets/any-file.js
import('./lazy.css');

// ...

В этом случае lazy.css будет загружен асинхронно, а затем добавлен на страницу. Если вы используете динамический импорт для ленивой загрузки файла JavaScript и этот файл импортирует CSS-файл (используя нединамический синтаксис import'), этот CSS-файл также будет загружен асинхронно.

Проблемы и отладка

Есть несколько распространённых проблем и ошибок с которыми вы можете столкнуться.

Отсутствует запись importmap

Одна из самых распространённых ошибок будет выходить из консоли вашего браузера и будет примерно такой:

Failed to resolve module specifier " bootstrap". Relative references must start with either "/", "./", or "../".

Или:

The specifier "bootstrap" was a bare specifier, but was not remapped to anything. Relative module specifiers must start with "./", "../" or "/".

Это означает, что где-то в вашем JavaScript вы импортируете сторонний пакет, например, import 'bootstrap'. Браузер пытается найти этот пакет в вашем файле importmap, но его там нет.

Почти всегда это можно исправить, добавив его в ваш importmap:

1
$ php bin/console importmap:require bootstrap

Note

Некоторые браузеры, такие как Firefox, показывают где находится этот код "импорта", тогда как другие, например, Chrome, пока это не показывают.

404 Не найдено для файла JavaScript, CSS или изображения

Иногда файл JavaScript, который вы импортируете (например, import './duck.js') или файл CSS/изображения, на который вы ссылаетесь, не будет найден, и вы увидите ошибку 404 в консоли вашего браузера. Вы также заметите, что в URL 404 отсутствует хеш версии в имени файла (например, 404 к /assets/duck.js вместо пути вроде /assets/duck.1b7a64b3b3d31219c262cf72521a5267.js).

Обычно это происходит потому, что путь указан неправильно. Если вы ссылаетесь на файл непосредственно в шаблоне Twig:

1
<img src="{{ asset('images/duck.png') }}">

Тогда путь, который вы передаёте asset() должен быть "логическим путем" к файлу. Используйте команду debug:asset-map, чтобы увидеть все валидные логические пути в вашем приложении.

Скорее всего, вы импортируете неработающий ресурс из файла CSS (например @import url('other.css')) или файла JavaScript:

1
2
// assets/controllers/farm-controller.js
import '../farm/chicken.js';

При этом путь должен быть относительным к файлу, который его импортирует (а в JavaScript-файлах он должен начинаться с ./ или ../). В этом случае ../farm/chicken.js будет указывать на assets/farm/chicken.js. Чтобы просмотреть список всех невалидных импортированных данных в вашем приложении, выполните:

1
2
$ php bin/console cache:clear
$ php bin/console debug:asset-map

Любой невалидный импорт будет показан в виде предупреждений в верхней части экрана (убедитесь, что у вас установлен symfony/monolog-bundle):

1
2
WARNING   [asset_mapper] Unable to find asset "../images/ducks.png" referenced in "assets/styles/app.css".
WARNING   [asset_mapper] Unable to find asset "./ducks.js" imported from "assets/app.js".

Предупреждение об отсутствующем ресурсе в комментируемом коде

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

Если импортированный путь не будет найден, вы увидите предупреждение при создании этого ресурса, которое вы можете проигнорировать.

Развертывание с компонентом AssetMapper

Когда вы будете готовы к развёртыванию, "скомпилируйте" свои ресурсы во время развёртывания:

1
$ php bin/console asset-map:compile

Готово! Это запишет все ваши ресурсы в каталог public/assets/, вместе с несколькими JSON-файлами, чтобы importmap можно было отобразить молниеносно быстро.

Но чтобы гарантировать, что ваш сайт производительный, убедитесь, что ваш веб-сервер (или прокси) использует HTTP/2, сжимает ваши ресурсы и устанавливает долговременные заголовки Expires для них. Смотрите Оптимизация для более подробной информации.

Оптимизация продуктивности

Чтобы ваш сайт на основе AssetMapper работал без сбоев, вам нужно сделать несколько вещей. Если вы хотите сократить путь, вы можете воспользоваться таким сервисом, как Cloudflare, который автоматически сделает большинство из этих действий за вас:

  • Используйте HTTP/2: Ваш веб-сервер должен работать по протоколу HTTP/2 (или HTTP/3), чтобы браузер мог загружать ресурсы параллельно. HTTP/2 автоматически включён в Caddy и может быть активирован в Nginx и Apache. Или проксируйте ваш сайт через сервис, например, Cloudflare, который автоматически включит HTTP/2 для вас.
  • Сжимайте свои ресурсы: Ваш веб-сервер должен сжимать (например, с помощью gzip) ваши ресурсы (JavaScript, CSS, изображения) перед отправкой их браузеру. Эта функция автоматически включается в Caddy и может быть активирована в Nginx и Apache. Или же проксируйте ваш сайт через сервис вроде Cloudflare, который автоматически будет сжимать ваши ресурсы за вас. В Cloudflare вы также можете включить auto minify для дальнейшего сжатия ваших ресурсов (например, удаления пробелов и комментариев из файлов JavaScript и CSS).
  • Установите длительный срок действия кеша: Ваш веб-сервер должен установить долгоживущий HTTP-заголовок Cache-Control для ваших ресурсов. Поскольку компонент AssetMapper включает хеш версии в имени файла каждого ресурса, вы можете смело установить max-age на очень большой срок (например, 1 год). Это не является автоматическим ни в одном веб-сервере, но это можно легко включить.

После того, как вы выполнили эти действия, вы можете использовать такой инструмент, как Lighthouse для проверки производительности вашего сайта!

Продуктивность: добавление предварительной загрузки

Одна из распространённых проблем, про которую может сообщить LightHouse, следующая:

Avoid Chaining Critical Requests

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

  • assets/app.js импортирует ./duck.js
  • assets/duck.js импортирует bootstrap

Когда браузер загружает страницу, происходит следующее:

  1. Браузер загружает assets/app.js;
  2. Потом он видит импорт ./duck.js и загружает assets/duck.js;
  3. Потом он видит импорт bootstrap и загружает assets/bootstrap.js.

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

AssetMapper избегает этой проблемы, выводя «предзагрузочные» теги link. Логика работает следующим образом:

A) Когда вы вызываете importmap('app'') в вашем шаблоне, компонент AssetMapper просматривает файл assets/app.js и находит все файлы JavaScript которые он импортирует, или файлы, которые импортируют эти файлы, и т. д.

В) Затем он выводит тег link для каждого из этих файлов с атрибутом rel="preload". Это указывает браузеру на необходимость немедленно начать загрузку этих файлов, даже если он еще не видел утверждения import для них.

Кроме того, если в вашем приложении имеется Компонент WebLink, Symfony добавит в ответ заголовок Link для предварительной загрузки CSS-файлов.

Часто задаваемые вопросы

Объединяет ли компонент AssetMapper ресурсы?

Нет! Но это потому, что в этом больше нет необходимости!

В прошлом было принято объединять ресурсы, чтобы уменьшить количество HTTP-запросов, которые были сделаны. Благодаря развитию веб-серверов, таких как HTTP/2, обычно не проблема держать ваши ресурсы отдельно и позволить браузеру загружать их параллельно. На самом деле,
если вы держите их отдельно, когда вы обновляете один ресурс, браузер может продолжать использовать кешированную версию всех остальных ваших ресурсов.

Смотрите Оптимизация для получения более подробной информации.

Минимизирует ли компонент AssetMapper ресурсы?

Нет! Минимизация или сжатие ресурсов важна, но может быть выполнена вашим веб-сервером или с помощью сервиса вроде Cloudflare. Смотрите Оптимизация для более подробной информации.

Готов ли компонент AssetMapper к производству? Он производительный?

Да! Очень! Компонент AssetMapper использует достижения в технологиях браузеров (например importmaps и нативную поддержку import) и веб-серверов (например, HTTP/2, который позволяет ресурсам загружаться параллельно). Смотрите другие вопросы о минимизации и комбинировании и Оптимизацию для более подробной информации.

Сайт https://ux.symfony.com работает на компоненте AssetMapper и имеет 99% оценку от Google Lighthouse.

Работает ли компонент AssetMapper со всеми браузерами?

Да! Такие функции, как importmaps и утверждение import поддерживаются во всех современных браузерах, но компонент AssetMapper поставляется с ES-модулем shim для поддержки importmap в старых браузерах. Итак, он работает везде (см. примечание ниже).

В вашем собственном коде, если вы полагаетесь на современные возможности функций JavaScript ES6, например, синтаксис классов, это поддерживается во всех браузерах, кроме самых старых. Если вам необходимо поддерживать очень старые браузеры, вам следует использовать такой инструмент, как Encore вместо компонента AssetMapper.

Note

Утверждение import не может быть полизаполненным или шимминговым, чтобы работать во всех браузерах. Однако только старейшие браузеры не поддерживают его - в основном IE 11 (который больше не поддерживается Microsoft и имеет менее 0.4% глобального использования).

Функция importmap настраивается для работы во всех браузерах с помощью компонента AssetMapper. Однако, шим не работает с "динамическим" импортом:

1
2
3
4
5
6
7
// это работает
import { add } from './math.js';

// это не будет работать в старейших браузерах
import('./math.js').then(({ add }) => {
    // ...
});

Если вы хотите использовать динамический импорт и вам нужна поддержка определённых старых браузеров (https://caniuse.com/import-maps), вы можете воспользоваться функцией importShim() из shim: https://www.npmjs.com/package/es-module-shims#user-content-polyfill-edge-case-dynamic-import

Могу ли я использовать это с Sass или Tailwind?

Конечно! Смотрите Использование Tailwind CSS или Использование Sass .

Могу ли я использовать это с TypeScript?

Конечно! Смотрите Использование TypeScript .

Могу ли я использовать это с JSX или Vue?

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

JSX можно скомпилировать непосредственно в нативный JavaScript-файл, но если вы используете много JSX, вам, вероятно, захочется использовать инструмент вроде Encore . Смотрите Документацию UX React для более подробной информации об использовании с компонентом AssetMapper.

Vue-файлы могут быть написаны на нативном JavaScript, и они будут работать с компонентом AssetMapper. Но вы не можете писать однофайловые компоненты (то есть файлы .vue) с помощью компонента, поскольку они должны быть использованы в системе сборки. Обратитесь к Документации UX Vue.js для более подробной информации об использовании с с компонентом AssetMapper.

Использование TypeScript

Чтобы использовать TypeScript с компонентом AssetMapper, ознакомьтесь с sensiolabs/typescript-bundle.

Сторонние пакеты и пользовательские пути ресурсов

Ко всем пакетам, которые имеют каталог Resources/public/ или public/, будет автоматически добавлен этот каталог как "путь ресурсов" с помощью пространства имён: bundles/<BundleName>. Например, если вы используете BabdevPagerfantaBundle и выполните команду debug:asset-map, вы увидите ресурс, логический путь к которому будет bundles/babdevpagerfanta/css/pagerfanta.css.

Это означает, что вы можете отображать эти ресурсы в своих шаблонах с помощью функции asset():

1
<link rel="stylesheet" href="{{ asset('bundles/babdevpagerfanta/css/pagerfanta.css') }}">

На самом деле, этот путь - bundles/babdevpagerfanta/css/pagerfanta.css - уже работает в приложениях без компонента AssetMapper, поскольку команда assets:install копирует ресурсы из пакетов в public/bundles/. Однако, когда компонент AssetMapper включён, файл pagerfanta.css будет автоматически версионирован! В результате будет выведено что-то вроде:

1
<link rel="stylesheet" href="/assets/bundles/babdevpagerfanta/css/pagerfanta-ea64fc9c55f8394e696554f8aeb81a8e.css">

Переопределение сторонних ресурсов

Если вы хотите переопределить сторонний ресурс, вы можете сделать это, создав файл в вашем каталоге assets/ с тем же именем. Например, если вы хотите переопределить файл pagerfanta.css, создайте файл по адресу assets/bundles/babdevpagerfanta/css/pagerfanta.css. Этот файл будет использоваться вместо оригинального файла.

Note

Если пакет отображает свои собственные ресурсы, но они используют не пакет по умолчанию пакет ресурсов , то компонент AssetMapper не будет использоваться. Это происходит, например, с EasyAdminBundle.

Импорт ресурсов вне каталога assets/

Вы можете импортировать ресурсы, которые находятся вне вашего пути к ресурсам (т.е. каталога assets/). Например:

1
2
3
4
/* assets/styles/app.css */

/* вы можете достичь ресурсов выше/ */
@import url('../../vendor/babdev/pagerfanta-bundle/Resources/public/css/pagerfanta.css');

Однако если вы получите ошибку, подобную этой:

В записи "app" карты импорта содержится путь "vendor/some/package/assets/foo.js" но, похоже, его нет ни в одном из ваших путей к ресурсам.

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

1
2
3
4
5
6
# config/packages/asset_mapper.yaml
framework:
    asset_mapper:
        paths:
            - assets/
            - vendor/some/package/assets

Затем попробуйте ещё раз выполнить команду.

Опции конфигурации

Вы можете увидеть все доступные опции конфигурации и некоторую информацию, выполнив:

1
$ php bin/console config:dump framework asset_mapper

Некоторые из самых важных опций описаны ниже.

framework.asset_mapper.paths

Эта конфигурация содержит все каталоги, которые будут просканированы на наличие ресурсов. Это может быть простым списком:

1
2
3
4
5
framework:
    asset_mapper:
        paths:
            - assets/
            - vendor/some/package/assets

Вы можете предоставить каждому пути "пространство имён", которое будет использовано в карте ресурсов:

1
2
3
4
5
framework:
    asset_mapper:
        paths:
            assets/: ''
            vendor/some/package/assets/: 'some-package'

В этом случае "логический путь" ко всем файлам в каталоге vendor/some/package/assets/ будет дополнен префиксом some-package - например, some-package/foo.js.

framework.asset_mapper.excluded_patterns

Это список глобальных паттернов, которые будут исключены из карты ресурсов:

1
2
3
4
framework:
    asset_mapper:
        excluded_patterns:
            - '*/*.scss'

Вы можете использовать команду debug:asset-map, чтобы проверить, что ожидаемые вами файлы включены в карту ресурсов.

framework.asset_mapper.exclude_dotfiles

Нужно ли исключать из маппера ресурсов все файлы, начинающиеся с .. Этот полезно, если вы хотите избежать утечки чувствительных файлов, таких как .env или .gitignore в файлах, опубликованных маппером ресурсов.

1
2
3
framework:
    asset_mapper:
        exclude_dotfiles: true

Эта опция включена по умолчанию.

framework.asset_mapper.importmap_polyfill

Сконфигурируйте полизаполнение для старых браузеров. По умолчанию модуль ES shim загружается через CDN (т.е. значение по умолчанию для этой настройки - `es-module-shims`):

1
2
3
4
5
6
7
8
9
framework:
    asset_mapper:
        # установите эту опцию как false, чтобы отключить shim полностью
        # (ваше приложение website/web не будет работать в старых браузерах)
        importmap_polyfill: false

        # вы также можете использовать пользовательское полизаполнение, добавив его к вашему файлу importmap.php
        # и установив эту опцию как ключ этого файла в файле importmap.php
        # importmap_polyfill: 'custom_polyfill'

Tip

Вы можете указать AssetMapper загружать ES модуль shim локально,
используя следующую команду, без изменения вашей конфигурации:

1
$ php bin/console importmap:require es-module-shims

framework.asset_mapper.importmap_script_attributes

Это список атрибутов, которые будут добавлены к тегам <script>, отображённых функцией
Twig {{ importmap() }}}:

1
2
3
4
framework:
    asset_mapper:
        importmap_script_attributes:
            crossorigin: 'anonymous'

CSS и JavaScript для конкретных страниц

Иногда вы можете решить разместить файлы CSS или JavaScript только на определенных
страницах. Для JavaScript простым способом является загрузка файла с помощью динамического импорта:

1
2
3
4
5
6
7
const someCondition = '...';
if (someCondition) {
    import('./some-file.js');

    // or use async/await
    // const something = await import('./some-file.js');
}

Another option is to create a separate entrypoint . For example, create a checkout.js file that contains whatever JavaScript and CSS you need:

1
2
3
4
// assets/checkout.js
import './checkout.css';

// ...

Затем добавьте это в файл importmap.php и отметьте его как точку входа:

1
2
3
4
5
6
7
8
9
// importmap.php
return [
    // точка входа 'app' ...

    'checkout' => [
        'path' => './assets/checkout.js',
        'entrypoint' => true,
    ],
];

Наконец, на странице, которой нужен этот JavaScript, вызовите importmap() и передайте как app, так и checkout:

1
2
3
4
5
6
7
8
9
10
{# templates/products/checkout.html.twig #}
{#
    Переопределите блок "importmap" из base.html.twig.
    Если у вас нет этого блока, добавьте его вокруг вызова {{ importmap('app') }}.
#}
{% block importmap %}
    {# НЕ вызывайте parent() #}

    {{ importmap(['app', 'checkout']) }}
{% endblock %}

При передаче как app, так и checkout, функция importmap() выведет importmap, а также добавит тег <script type=«module»>, который загружает файл app.js и файл checkout.js. Важно не вызывать parent() в блоке importmap. Каждая страница может иметь только иметь одну карту импорта, поэтому importmap() должен быть вызван ровно один раз.

Если по какой-то причине вы хотите выполнить только checkout.js а не app.js, передайте в importmap() только checkout.

Система кеширования компонента AssetMapper в разработке

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

Система также учитывает "зависимости": Если app.css содержит @import url('other.css'), то содержание файла app.css также будет перевычисляться каждый раз, когда будет меняться other.css. Это связано с тем, что хеш версии other.css изменится... что приведёт к изменению конечного содержания app.css, поскольку он содержит в себе финальное имя файла other.css.

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

1
$ php bin/console cache:clear

Это заставит компонент AssetMapper перевычислить содержание всех файлов.

Проводите аудиты безопасности ваших зависимостей

Подобно npm, компонент AssetMapper поставляется в комплекте с командой, которая проверяет уязвимости безопасности в зависимостях вашего приложения:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ php bin/console importmap:audit

----------  --------------------------------------------------  ---------  -------  ------------  -----------------------------------------------------
Тяжесть     Название                                            Пакет      Версия   Исправлено в  Больше информации
----------  --------------------------------------------------  ---------  -------  ------------  -----------------------------------------------------
Средняя     Уязвимость межсайтового скриптинга в jQuery         jquery     3.3.1    3.5.0         https://api.github.com/advisories/GHSA-257q-pV89-V3xv
Серьезная   Загрязнение прототипов в JSON5 через метод разбора  json5      1.0.0    1.0.2         https://api.github.com/advisories/GHSA-9c47-m6qq-7p4h
Средняя     semver уязвима к отказу в обслуживании RegExp       semver     4.3.0    5.7.2         https://api.github.com/advisories/GHSA-c2qf-rxjj-qqgw
Критичная   Прототип загрязнения в minimist                     minimist   1.1.3    1.2.6         https://api.github.com/advisories/GHSA-xvch-5gv4-984h
Средняя     Зависимости ESLint уязвимы                          minimist   1.1.3    1.2.2         https://api.github.com/advisories/GHSA-7fhm-mqm4-2wp7
Средняя     Bootstrap уязвим для межсайтового скриптинга        bootstrap  4.1.3    4.3.1         https://api.github.com/advisories/GHSA-9v3M-8fp8-mi99
----------  --------------------------------------------------  ---------  -------  ------------  -----------------------------------------------------

7 пакетов найдено: 7 аудировано / 0 пропущено
6 уязвимостей найдено: 1 критичная / 1 серьезная / 4 средних

Команда вернет код выхода 0, если уязвимость не найдена, или код выхода 1 в противном случае. Это означает, что вы можете легко интегрировать эту команду в свой CI, чтобы получать предупреждение при обнаружении новой уязвимости.

Tip

Команда принимает опцию --format для выбора формата вывода между txt и json.