Web Assets (Веб-ресурсы) CMS Joomla 4

  1. Концепция управления правами доступа к ресурсам сайта на CMS Joomla 4
  2. Определение доступа в CMS Joomla 4
  3. Объяснение прав доступа в CMS Joomla 4
  4. Регистрация ресурса в CMS Joomla 4
  5. Включение доступа к ресурсам CMS Joomla 4
  6. Переопределение ресурсов
  7. Работа со стилями CSS
    1. Методы работы с CSS-стилями
    2. Добавление встроенных стилей CSS
  8. Работа со скриптами JavaScript
    1. Методы работы со со скриптами JavaScript в CMS Joomla 4
    2. Добавление встроенного скрипта JavaScript
  9. Работа с веб-компонентом
    1. Методы работы с веб-компонентом
  10. Работа с пресетами
    1. Методы работы с пресетами
  11. Дополнительно: Пользовательский класс WebAssetItem
    1. Определение пользовательского класса WebAssetItem в joomla.asset.json


Концепция управления правами доступа к ресурсам сайта на CMS Joomla 4

Во внешнем мире многие активы связаны между собой. Например, наш сценарий передачи и сохранения данных зависит от файла core.js для управления операциями с этими данными. В Joomla никогда не было простого способа указать это, вам просто нужно было включить несколько файлов. Joomla 4 меняет это с концепцией веб-ресурсов (concept of web assets).

Определение доступа в CMS Joomla 4

Связанные активы определяются в файле JSON, таком как system/joomla.asset.json#L14-L21

Это имеет структуру, состоящую из определения схемы (для проверки), имени, версии, лицензии, а затем одного или нескольких определений ресурсов. Ресурсы состоят из списка файлов js и файлов css, связанных с ресурсами и любыми зависимостями. Раздел зависимости - это просто список имен ресурсов, которые необходимы для функционирования запрашиваемого ресурса. Пример:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "version": "4.0.0",
  "description": "Joomla CMS",
  "license": "GPL-2.0+",
  "assets": [
    {
      "name": "bar",
      "type": "style",
      "uri": "com_example/bar.css"
    },
    {
      "name": "bar",
      "type": "script",
      "uri": "com_example/bar.js"
    },
    {
      "name": "beer",
      "type": "style",
      "uri": "com_example/beer.css",
      "dependencies": [
        "bar"
      ],
    },
    {
      "name": "beer",
      "type": "script",
      "dependencies": [
        "core",
        "bar"
      ],
      "uri": "com_example/beer.js",
      "attributes": {
        "defer": true,
        "data-foo": "bar"
      }
    }
  ]
}

Атрибут $schema - это файл определения схемы, который позволяет проверять файл с помощью схемы JSON. Прочитайте официальный веб-сайт для получения дополнительной информации о работах по проверке схемы json.

Примечание: Наличие joomla.asset.json для вашего расширения или шаблона рекомендуется, но не требуется для работы WebAssset (см. Следующий раздел).

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

Объяснение прав доступа в CMS Joomla 4

Каждый ресурс состоит из 2 этапов: регистрация и использование.

  1. Зарегистрирован — это место, где ресурс загружается в WebAssetRegistry. Это означает, что WebAssetManager знает о существовании этих ресурсов, но не будет прикреплять их к документу во время визуализации. Все ресурсы, загруженные из joomla.asset.json, находятся на стадии регистрации registered.
  2. Используется — это когда ресурс включается с помощью "$wa->useAsset()" (->useScript(), ->useStyle(), ->registerAndUseX() и т.д.). Это означает, что WebAssetManager подключит эти ресурсы и их зависимости к документу во время визуализации.

Ресурс нельзя использовать, если он не был зарегистрирован ранее, это приведет к исключению неизвестного ресурса.

Регистрация ресурса в CMS Joomla 4

Все известные ресурсы загружаются и затем сохраняются в WebAssetRegistry (чтобы включить/отключить элемент ресурса, вы должны использовать WebAssetManager, см. Следующий раздел).

Joomla будет автоматически искать следующее определение ресурсов во время выполнения (в следующем порядке):

media/vendor/joomla.asset.json (при первом доступе к WebAssetRegistry)
media/system/joomla.asset.json
media/legacy/joomla.asset.json
media/{com_active_component}/joomla.asset.json (по запросу приложения)
templates/{active_template}/joomla.asset.json

И загрузит их в реестр известных ресурсов.

Примечание. Каждое следующее определение ресурсов переопределит их элементы из предыдущего определения ресурса по имени элемента.

Вы можете зарегистрировать свое собственное определение ресурсов с помощью WebAssetRegistry:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wr = $wa->getRegistry();
$wr->addRegistryFile('relative/path/to/your/joomla.asset.json');

Чтобы добавить пользовательский элемент ресурса во время выполнения:

$wr->add('script', new Joomla\CMS\WebAsset\WebAssetItem('foobar', 'com_foobar/file.js', ['type' => 'script']));

Или ещё проще, с помощью WebAssetManager:

$wa->registerScript('foobar', 'com_foobar/file.js');

Новый элемент foobar ресурса будет добавлен в реестр известных ресурсов, но не будет прикреплен к документу до тех пор, пока ваш код (макет, шаблон и т.д.) Не запросит его.

Чтобы проверить, существует ли ресурс используйте:

if ($wa->assetExists('script', 'foobar'))
{
    var_dump('Script "foobar" exists!');
}

Включение доступа к ресурсам CMS Joomla 4

Все управление ресурсами в текущем документе обрабатывается WebAssetManager, который доступен с помощью $doc->getWebAssetManager();

С помощью WebAssetManager вы можете легко включить или отключить необходимый актив в Joomla стандартными методами ядра CMS.

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

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->useScript('keepalive');

// Or multiple
$wa->useScript('keepalive')
    ->useScript('fields.validate')
    ->useStyle('foobar')
    ->useScript('foobar');

// Добавьте новый элемент ресурса с зависимостью и используйте его
$wa->registerAndUseScript('bar', 'com_foobar/bar.js', [], [], ['core', 'foobar']);

WebAssetManager проверит WebAssetRegistry, существует ли запрошенный ресурс, и включит его для текущего экземпляра документа. В противном случае это вызовет исключение UnknownAssetException.

Чтобы отключить ресурс на странице, используйте функцию disableAsset. Приведенный ниже пример отключит загрузку ресурса jquery-noconflict.

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->disableScript('jquery-noconflict');

Примечание. Если есть какие-либо зависимости от отключенного ресурса, этот ресурс будет автоматически включен, несмотря ни на что.

Чтобы проверить, включен ли актив, и состояние актива используйте:

// Проверка того, активен ли ресурс (включен вручную или автоматически в качестве зависимости)
if ($wa->isAssetActive('script', 'foobar'))
{
    var_dump('Script "foobar" are active!');
}

// Проверка состояния
switch($wa->getAssetState('script', 'foobar')){
	case Joomla\CMS\WebAsset\WebAssetManager::ASSET_STATE_ACTIVE:
		var_dump('Активен! Был включен вручную');
		break;
	case Joomla\CMS\WebAsset\WebAssetManager::ASSET_STATE_DEPENDENCY:
		var_dump('Активен! Был включен автоматически при разрешении зависимостей');
		break;
	default:
		var_dump('не активен!');
}

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

Переопределение может быть полезно, когда вам нужно переопределить URI элемента ресурса или его зависимостей. Как уже отмечалось, каждое из следующих определений ресурсов из joomla.asset.json переопределит элементы ресурсов из предыдущих определений ресурсов по имени элемента. Это означает, что если вы предоставите joomla.asset.json, который содержат уже загруженные элементы ресурсов, они будут заменены вашими элементами. Другой способ переопределения ресурса в коде — зарегистрировать элемент с тем же именем. Например, у нас есть скрипт "foobar", который загружает com_example/foobar.js библиотеку, и мы хотим использовать CDN именно для этой библиотеки:

Как это было определено в системе изначально:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "com_example/foobar.js",
  "dependencies": ["core"]
}
...

Чтобы переопределить URI, мы определяем элемент ресурса с именем "foobar" в нашем joomla.asset.json:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "http://foobar.cdn.blabla/foobar.js",
  "dependencies": ["core"]
}
...

Или зарегистрируйте новый элемент ресурса в AssetManager:

$wa->registerScript('foobar', 'http://fobar.cdn.blabla/foobar.js', [], [], ['core']);

Работа со стилями CSS

AssetManager позволяет управлять файлами CSS-стилей. Элемент актива таблицы стилей имеет тип "style".

Пример определения json элемента в joomla.asset.json:

...
{
  "name": "foobar",
  "type": "style",
  "uri": "com_example/foobar.css"
}
...

Методы работы со стилями CSS

AssetManager предлагает следующие методы для работы с файлами CSS-стилей:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Присоединить foobar к документу
$wa->useStyle('foobar');

// Отключить подключение foobar
$wa->disableStyle('foobar');

// Регистрация пользовательского элемента без определения json
$wa->registerStyle('bar', 'com_example/bar.css', [], ['data-foo' => 'some attribute'], ['some.dependency']);
// И используйте его позже
$wa->useStyle('bar');

// Зарегистрируйте и прикрепите пользовательский элемент в одну строку
$wa->registerAndUseStyle('bar', 'com_example/bar.css', [], ['data-foo' => 'some attribute'], ['some.dependency']);

Добавление встроенных стилей CSS

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

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

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Добавить встроенный контент, как обычно, он будет отображаться в потоке после всех ресурсов
$wa->addInlineStyle('content of inline1');

// Добавить встроенное содержимое, которое будет размещено после актива "foobar" 
$wa->addInlineStyle('content of inline2', ['position' => 'after'], ['data-foo' => 'bar'], ['foobar']);

// Добавить встроенное содержимое, которое будет размещено перед ресурсом "foobar"
$wa->addInlineStyle('content of inline3', ['position' => 'before'], [], ['foobar']);

// Именованный встроенный ресурс
$wa->addInlineStyle('content of inline4', ['name' => 'my.inline.asset']);

Примечание: ресурс "foobar" должен существовать в реестре ресурсов, в противном случае вы получите исключение неудовлетворенной зависимости.

Пример выше приведет к:

...
<style>content of inline3</style>
<link rel="stylesheet" href="/foobar.css" />
<style data-foo="bar">content of inline2</style>
...
...
<style>content of inline1</style>
<style>content of inline4</style>
...

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

$wa->addInlineStyle('content of inline1', ['position' => 'before'], [], ['foo', 'bar']);
$wa->addInlineStyle('content of inline2', ['position' => 'after'], [], ['foo', 'bar']);

Произведет:

...
<link rel="stylesheet" href="/foo.css" />
<style>content of inline1</style>
<link rel="stylesheet" href="/bar.css" />
<style>content of inline2</style>
...

Примечание. Именованные встроенные ресурсы могут быть зависимостью от другого встроенного ресурса, однако не рекомендуется использовать встроенный ресурс в качестве зависимости от не встроенного ресурса. Это сработает, но в будущем такое поведение может измениться. Предпочтительно вместо этого использовать атрибут "position".

Работа со скриптами JavaScript

AssetManager позволяет управлять файлами сценариев JavaScript. Элемент актива скрипта имеет тип "script". Пример определения json элемента в joomla.asset.json:

...
{
  "name": "foobar",
  "type": "script",
  "uri": "com_example/foobar.js",
  "dependencies": ["core"]
}
...

Пример определения json сценария модуля ES6 с возвратом к устаревшему браузеру:

...
{
  "name": "foobar-legacy",
  "type": "script",
  "uri": "com_example/foobar-as5.js",
  "attributes": {
    "nomodule": true,
    "defer": true
  },
  "dependencies": ["core"]
}
{
  "name": "foobar",
  "type": "script",
  "uri": "com_example/foobar.js",
  "attributes": {
    "type": "module"
  },
  "dependencies": [
    "core", 
    "foobar-legacy"
  ]
}
...

Методы работы со со скриптами JavaScript в CMS Joomla 4

AssetManager предлагает следующие методы работы с файлами сценариев, написанных на JavaScript:

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Присоединение foobar к документу
$wa->useScript('foobar');

// Отключение присоединения foobar к документу
$wa->disableScript('foobar');

// Регистрация пользовательского элемента без определения json
$wa->registerScript('bar', 'com_example/bar.js', [], ['defer' => true], ['core']);
// And use it later
$wa->useScript('bar');

// Регистрация и присоединение пользовательского элемента в одну строку
$wa->registerAndUseScript('bar','com_example/bar.js', [], ['defer' => true], ['core']);

Добавление встроенного скрипта JavaScript

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

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

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Добавить встроенный контент, как обычно, он будет отображаться в потоке после всех ресурсов
$wa->addInlineScript('content of inline1');

// Добавить встроенное содержимое, которое будет размещено после ресурса "foobar" 
$wa->addInlineScript('content of inline2', ['position' => 'after'], ['data-foo' => 'bar'], ['foobar']);

// Добавить встроенное содержимое, которое будет размещено перед ресурсом "foobar" 
$wa->addInlineScript('content of inline3', ['position' => 'before'], [], ['foobar']);

// Именованный встроенный ресурс
$wa->addInlineScript('content of inline4', ['name' => 'my.inline.asset']);

// Указать тип сценария
$wa->addInlineScript('content of inline5', [], ['type' => 'module']);

Примечание: ресурс "foobar" должен существовать в реестре ресурсов, в противном случае вы получите исключение неудовлетворенной зависимости.

Пример выше приведет к:

...
<script>content of inline3</script>
<script src="/foobar.js"></script>
<script data-foo="bar">content of inline2</script>
...
...
<script>content of inline1</script>
<script>content of inline4</script>
<script type="module">content of inline5</script>
...

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

$wa->addInlineScript('content of inline1', ['position' => 'before'], [], ['foo', 'bar']);
$wa->addInlineScript('content of inline2', ['position' => 'after'], [], ['foo', 'bar']);

Произведет:

...
<script src="/foo.js"></script>
<script>content of inline1</script>
<script src="/bar.js"></script>
<script>content of inline2</script>
...

Примечание. Именованный встроенный ресурс может быть зависимостью от другого встроенного ресурса, однако не рекомендуется использовать встроенный ресурс в качестве зависимости от не встроенного ресурса. Это сработает, но в будущем такое поведение может измениться. Предпочтительно вместо этого использовать "position".

Работа с веб-компонентом

Joomla позволяет вам использовать веб-компоненты для ваших нужд. В Joomla веб-компоненты загружаются не как обычный скрипт, а через загрузчик веб-компонентов, поэтому они загружаются асинхронно. Следовательно, элемент актива веб-компонента должен иметь флаг "webcomponent", установленный в логическое значение "true". Во всех других аспектах работа с веб-компонентами в AssetManager аналогична работе с элементом ресурса "script".

Пример определения json некоторых веб-компонентов в joomla.asset.json (как модуль ES6):

...
{
  "name": "webcomponent.foobar",
  "type": "style",
  "uri": "com_example/foobar-custom-element.css",
},
{
  "name": "webcomponent.foobar",
  "type": "script",
  "uri": "com_example/foobar-custom-element.js",
  "attributes": {
     "type": "module"
  },
}
...

Пример с резервным вариантом для браузеров, которые не поддерживают функцию ES6 "module". Обратите внимание, что устаревший сценарий должен иметь зависимость "wcpolyfill", а сценарий модуля должен иметь зависимость от устаревшего сценария:

...
{
  "name": "webcomponent.foobar",
  "type": "style",
  "uri": "com_example/foobar-custom-element.css",
},
{
  "name": "webcomponent.foobar-legacy",
  "type": "script",
  "uri": "com_example/foobar-custom-element-es5.js",
  "attributes": {
    "nomodule": true,
    "defer": true
  },
  "dependencies": [
    "wcpolyfill"
  ]
},
{
  "name": "webcomponent.foobar",
  "type": "script",
  "uri": "com_example/foobar-custom-element.js",
  "attributes": {
    "type": "module"
  },
  "dependencies": [
    "webcomponent.foobar-legacy"
  ]
}
...

В качестве альтернативы вы можете зарегистрировать их в PHP (как модуль ES6):

$wa->registerStyle('webcomponent.foobar', 'com_example/foobar-custom-element.css')
    ->registerScript('webcomponent.foobar', 'com_example/foobar-custom-element.js', ['type' => 'module']);

Присоединение к документу:

$wa->useStyle('webcomponent.foobar')
    ->useScript('webcomponent.foobar');

Примечание: Предпочтительно использовать префикс имени ресурса с "webcomponent.", чтобы его было легко обнаружить и отличить от обычных сценариев в макете документа.

Методы работы с веб-компонентом

Все методы работы с веб-компонентом такие же, как и методы работы с элементом ресурса сценария на JavaScript.

Работа с пресетами

"Preset" — это особый вид элемента htcehcf, который содержит список элементов, которые необходимо включить, так же, как прямой вызов useAsset() для каждого элемента в списке. Пресет может содержать смешанные типы ресурсов (сценарий, стиль, другой пресет и т.д.), Тип должен указываться после символа # и следовать после имени ресурса, например: foo#style, bar#script.

Пример определения json элемента в joomla.asset.json:

...
{
  "name": "foobar",
  "type": "preset",
  "uri": "",
  "dependencies": [
    "core#script",
    "foobar#style",
    "foobar#script",
  ]
}
...

Методы работы с пресетами

AssetManager предлагает следующие методы работы с предустановленными элементами (пресетами):

/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();

// Прикрепить все элементы из пресета foobar к документу
$wa->usePreset('foobar');

// Отключить прикрепление всех элементов из песета foobar
$wa->disablePreset('foobar');

// Регистрирация пресета без определения json
$wa->registerPreset('bar', '', [], [], ['core#script', 'bar#script']);

// и его дальнейшее использование
$wa->usePreset('bar');

// Регистрирация and подключение пользовательского пресета в одну строку
$wa->registerAndUsePreset('bar','', [], [], ['core#script', 'bar#script']);

Дополнительно: Пользовательский класс WebAssetItem

Класс по умолчанию для всех элементов WebAsset находится в Joomla\CMS\WebAsset\WebAssetItem.

Вам также разрешено использовать пользовательский класс, который должен реализовывать интерфейс Joomla\CMS\WebAsset\WebAssetItemInterface или расширять интерфейс Joomla\CMS\WebAsset\WebAssetItem.

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

class MyComExampleAssetItem extends WebAssetItem
{
	public function getUri($resolvePath = true): string
	{
		$langTag = Factory::getApplication()->getLanguage()->getTag();
		// Для ресурса скрипта JavaScript используйте ".js", для стиля - ".css"
		$path    = 'com_example/bar-' . $langTag . '.js';

		if ($resolvePath)
		{
			// Для ресурса скрипта JavaScript используйте "script", для стиля - "stylesheet"
			$path = $this->resolvePath($path, 'script');
		}

		return $path;
	}
}

Кроме того, реализация интерфейса Joomla\CMS\WebAsset\WebAssetAttachBehaviorInterface позволяет добавлять параметры сценария (которые могут зависеть от среды), когда ваш ресурс включен и прикреплен к документу.

class MyFancyFoobarAssetItem extends WebAssetItem implements WebAssetAttachBehaviorInterface
{
	public function onAttachCallback(Document $doc): void
	{
		$user = Factory::getApplication()->getIdentity();
		$doc->addScriptOptions('com_example.fancyfoobar', ['userName' => $user->username]);
	}
}

Важное примечание: Элемент ресурса, реализующий интерфейс WebAssetAttachBehaviorInterface, должен быть включен до события onBeforeCompileHead, в противном случае "onAttachCallback" будет проигнорирован.

Определение пользовательского класса WebAssetItem в joomla.asset.json

В joomla.asset.json вы можете определить, какой класс следует использовать с конкретным объектом AssetItem. Для этого вы можете использовать 2 свойства namespace и class. namespace может быть определено на корневом уровне (тогда оно будет использоваться как пространство имен по умолчанию для всех элементов ресурсов в joomla.asset.json) или на уровне элементов. Например:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "version": "4.0.0",
  "namespace": "Joomla\Component\Example\WebAsset",
  "assets": [
    {
      "name": "foo",
      "type": "script",
      "class": "FooAssetItem",
      "uri": "com_example/foo.js"
    },
    {
      "name": "bar",
      "type": "script",
      "namespace": "MyFooBar\Library\Example\WebAsset",
      "class": "BarAssetItem",
      "uri": "com_example/bar.js"
    }
  ]
}

Здесь ресурс foo будет связан с классом Joomla\Component\Example\WebAsset\FooAssetItem, а bar - с классом MyFooBar\Library\Example\WebAsset\BarAssetItem.

Примечание: Если пространство имен (namespace) не определено, то по умолчанию будет использоваться Joomla\CMS\WebAsset. Если пространство имен (namespace) определено, но пусто, то пространство имен не будет использоваться, будет использоваться только class. Пример:

{
  "$schema": "https://developer.joomla.org/schemas/json-schema/web_assets.json",
  "name": "com_example",
  "assets": [
    {
      "name": "foo",
      "type": "script",
      "class": "FooAssetItem",
      "uri": "com_example/foo.js"
    },
    {
      "name": "bar",
      "type": "script",
      "namespace": "",
      "class": "BarAssetItem",
      "uri": "com_example/bar.js"
    }
  ]
}

Здесь ресурс foo будет связан с классом Joomla\CMS\WebAsset\FooAssetItem, а bar - с классом BarAssetItem (без пространства имен).

Перевод с английского официальной документации CMS Joomla 4:
https://docs.joomla.org/J4.x:Web_Assets

Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)

Старт! Горячий старт на просторы интернета
Старт! Горячий старт на просторы интернета
Старт! Меню