Сообщить об ошибке.

Объединение и сжатие файлов JavaScript и CSS в приложении Flask

Расширение Flask-Assets предназначено для объединения и сжатия файлов JavaScript и CSS, а также преобразования разметки sass и less в реальный CSS.

Установка модуля Flask-Assets в виртуальное окружение

# создаем виртуальное окружение, если нет
$ python3 -m venv .venv --prompt VirtualEnv
# активируем виртуальное окружение 
$ source .venv/bin/activate
# обновляем `pip`
(VirtualEnv):~$ python3 -m pip install -U pip
# ставим модуль `Flask-Assets`
(VirtualEnv):~$ python3 -m pip install -U Flask-Assets

Содержание:


Подключение и использование модуля Flask-Assets.

При инициализации приложения, необходимо создать экземпляр класса flask_assets.Environment и зарегистрировать в нем необходимые ресурсы в виде так называемых пакетов flask_assets.Bundle.

from flask import Flask
from flask_assets import Environment, Bundle

app = Flask(__name__)
assets_env = Environment(app)

js = Bundle('jquery.js', 'base.js', 'widgets.js',
            filters='jsmin', output='gen/packed.js')
assets_env.register('js_all', js)

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

Все пути относятся к директории со статическими файлами приложения или директории со статикой схемы Flask.

Если есть необходимость, то можно определить статические активы (css и js) во внешнем файле конфигурации и прочитать их оттуда. Используемый в зависимостях модуль webassets включает ряд вспомогательных классов для некоторых популярных форматов, таких как YAML.

Экземпляр Flask-Assets может использоваться с несколькими приложениями путем инициализации через вызовы init_app(), а не передачи фиксированного объекта приложения:

app = Flask(__name__)
assets_env = flask_assets.Environment()
assets_env.init_app(app)

Ссылка на созданный пакет Flask-Assets

Теперь, когда ресурсы правильно определены, можно их объединить и минимизировать, а также включить ссылку на сжатый результат на веб-странице:

{% assets "js_all" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}

Модуль Flask-Assets автоматически объединит и сожмет исходные файлы пакета при первом отображении шаблона и автоматически обновит сжатый файл при каждом изменении исходного файла. Если в конфигурации приложения установить ASSETS_DEBUG в значение True, то каждый исходный файл будет выводиться по отдельности.

Использование Flask-Assets со схемами blueprint

Если используются схемы Flask (blueprint), то можно ссылаться на статические файлы схем через префикс, точно так же, как Flask позволяет ссылаться на шаблоны схем:

js = Bundle('app_level.js', 'blueprint/blueprint_level.js')

В приведенном выше примере пакет будет ссылаться на два файла: {APP_ROOT}/static/app_level.js и {BLUEPRINT_ROOT}/static/blueprint_level.js.

Если ранее приложение Flask использовало модуль webassets, то скорее всего знакомы с требованием установки в конфигурации базового каталога и URL-адреса. Расширение Flask-Assets этого не требует, т.к. используется поддержка статических папок Flask. Обратите внимание, что если необходимо, то можно установить собственный корневой каталог Environment.directory или URL-адрес Environment.url. Но при этом отключается поддержка схем Flask, то есть ссылка на статические файлы в разных схемах с использованием префикса, как описано выше, уже невозможна. Все пути будут рассматриваться относительно каталога и указанного URL-адреса.

Использование Flask-Assets только в шаблоне.

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

{% assets filters="jsmin", output="gen/packed.js",
          "common/jquery.js", "site/base.js", "site/widgets.js" %}
    <script type="text/javascript" src="{{ ASSET_URL }}"></script>
{% endassets %}

Режим отладки Flask-Assets

Режим отладки можно установить через конфигурацию Flask. Следующие два утверждения эквивалентны:

assets_env.debug = True
# или
app.config['ASSETS_DEBUG'] = True

Если установить ASSETS_DEBUG в значение True, то каждый исходный файл будет выводиться по отдельности.

Класс flask_assets.Environment().

Объект flask_assets.Environment(app=None) используется для хранения набора пакетов и конфигурации.

Если он инициализирован экземпляром приложения Flask, тогда расширение webassets-Jinja2 регистрируется автоматически (смотрите "Использование Flask-Assets только в шаблоне").

app = Flask(__name__)
assets = Environment(app)

Объект Environment определяет несколько методов:

  • Свойство Environment.directory: базовый каталог, к которому будут относиться все пути. По умолчанию 'static'. Служит только в качестве выходного каталога output.
  • Свойство Environment.url: префикс URL-адреса, используемый для построения URL-адресов файлов в Environment.directory. По умолчанию 'static'.
  • Метод Environment.from_module(path): регистрирует пакеты из модуля Python, указанного в аргументе path.
  • Метод Environment.from_yaml(path): регистрирует пакеты из файла конфигурации YAML, переданный аргументу path.

    Ожидается следующий формат YAML:

    bundle-name:
      filters: sass,cssutils
      output: cache/default.css
      contents:
          - css/jquery.ui.calendar.css
          - css/jquery.ui.slider.css
    another-bundle:
      # ...
    

    Пакеты могут ссылаться друг на друга:

    js-all:
      contents:
          - jquery.js
          - jquery-ui    # Это ссылка на пакет
    jquery-ui:
      contents: jqueryui/*.js
    

Класс flask_assets.Bundle().

Класс flask_assets.Bundle(*contents, **options) представляет собой пакет - набор файлов *contents, которые необходимо сгруппировать вместе, с некоторыми присоединенными свойствами **options. К таким свойствам относятся фильтры, которые следует применять, или место, где должен храниться выходной файл.

Обратите внимание, что все имена файлов и пути считаются относительными к настройке Environment.directory, а сгенерированные URL-адреса будут относиться к настройке Environment.url.

js = Bundle('common/inheritance.js', 'portal/js/common.js',
       'portal/js/plot.js', 'portal/js/ticker.js',
       filters='jsmin',
       output='gen/packed.js')

Ключевые аргументы **options могут содержать:

  • filters: один или несколько фильтров для применения. Если фильтры не указаны, то исходные файлы *contents будут просто объединены в выходной файл. Фильтры применяются в том порядке, в котором они заданы.

    Встроенные фильтры:

    • jsmin - сокращает Javascript, удаляя пробелы, комментарии и т. д.
    • cssmin - сокращает CSS, удаляя пробелы, комментарии и т. д.
    • less - преобразует разметку less в реальный CSS.
    • sass - преобразует разметку sass в реальный CSS. Требует, чтобы исполняемый файл Sass был доступен извне. Чтобы установить его, можно сделать следующее: $ sudo gem install sass
  • output: имя/путь выходного файла.

  • depend: дополнительные файлы, которые должны отслеживаются на предмет изменения и определения, нужно ли пересобирать пакет. Этот аргумент необходим, если используются компиляторы, допускающие инструкции @import. Для простоты можно использовать символы подстановки в стиле модуля glob, например, Bundle(depends=('**/*.scss')).

Вложенные пакеты

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

from flask_assets import Bundle

all_js = Bundle(
    # jQuery
    Bundle('common/libs/jquery/jquery.js',
        'common/libs/jquery/jquery.ajaxQueue.js',
        'common/libs/jquery/jquery.bgiframe.js',),
    # jQuery Tools
    Bundle('common/libs/jqtools/tools.tabs.js',
        'common/libs/jqtools/tools.tabs.history.js',
        'common/libs/jqtools/tools.tabs.slideshow.js'),
    # собственные скрипты
    Bundle('common/inheritance.js', 'portal/js/common.js',
        'portal/js/plot.js', 'portal/js/ticker.js'),
    # фильтр минификации для всех вложенных пакетов
    filters='jsmin',
    # результирующий файл
    output='gen/packed.js')

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

  1. Использование компиляторов CSS, например:

    • один вложенный пакет будет содержать файлы, которые нужно прогнать через компилятор CSS;
    • другой вложенный пакет - обычные CSS-файлы, которые нужно только сжать.

    Расширение Flask-Assets включает встроенные фильтры для ряда популярных компиляторов CSS, которые можно использовать как любой другой фильтр. Фильтры компилятора запускаются даже в режиме отладки:

    less = Bundle('css/base.less', 'css/forms.less',
                filters='less,cssmin', output='screen.css')
    

    Блок кода примера при отладке скомпилирует CSS, но не минимизируется. В продакшене будут применяться оба фильтра.

    Иногда нужно объединить код CSS, и файлы написанные с использованием расширения sass:

    sass = Bundle('*.sass', filters='sass', output='gen/sass.css')
    all_css = Bundle('css/jquery.calendar.css', sass,
                   filters='cssmin', output="gen/all.css")
    

    В приведенном выше случае фильтр sass применяется только к исходным файлам Sass во вложенном пакете (которому необходим output='gen/sass.css'!). Минификация применяется ко всему содержимому CSS во внешнем пакете.

  2. Использование предварительно сжатых файлов JS, например:

    • один вложенный пакет будет содержать файлы jquery.min.js;
    • другой вложенный пакет будет содержать файлы JS, которые необходимо сжать.

    Например, если проект использует предварительно сжатые JS-файлы, которые нужно объединить с несжатыми, то можно сделать это следующим образом:

    js = Bundle('jquery.min.js', Bundle('uncompressed.js', filters='jsmin'), output='gen/packed.js')