Проблема
Однажды, в ходе переписывания большого проекта, возникла необходимость улучшить механизм кастомизации html шаблонов под разные версии нашего web-приложения. В старой версии кастомизация выглядела подобным образом:
{{if app.version==versions.main}}
<!--один html код-->
{{else if app.version==versions.custom1}}
<!--другой html код-->
{{else if app.version==versions.custom2}}
<!--и ещё html код-->
{{endif}}
И подобной лапшой был пронизан весь проект. Такой код сложно поддерживать и изменять, при активном использовании шаблоны превращаются в непонятную кашу, где бизнес логика отображения отдельных блоков переплетается с кастомизацией под разные версии.
Привыкнув к удобным методам управления версиями приложения с помощью инъекции зависимостей, когда в зависимости от версии используются разные реализации интерфейсов, я решил изобрести свой велосипед для XSLT-подобного управления версиями html файлов: grunt-html-inheritance. Он позволяет подменять кусочки базового html-файла с помощью маленьких патчей.
При переписывании проекта в качестве фреймворка был выбран AngularJS, поэтому вёрстка в приложении хранится в виде множества html файлов, подгружаемых при необходимости, что позволило легко внедрить систему сборки этих файлов с кастомизацией под разные версии приложения.
Пример
Давайте представим, что у нас имеется следующий кусок вёрстки в HTML файле «myfile.html»:
<div>
Blah blah blah
<div>Main version</div>
</div>
И, как часто бывает в разных версиях приложения, нам понадобилось, чтобы в версии «myversion» вместо одного текста «Main version» отображался другой: «My version». Чтобы сделать это с помощью html-inheritance, нужно сделать 2 простых шага:
1. Добавить к кастомизируемому тегу аттрибут, начинающийся на «bl-»:
<div>
Blah blah blah
<div bl-mytag>Main version</div>
</div>
2. Создать файл «myfile.myversion.html» с патчем к родительскому файлу:
<div bl-mytag="replace">My version</div>
Всё! При билде проекта все html файлы будут аккуратно сложены в указанную папку с разбивкой по версиям:
dist | main/ myfile.html myversion/ myfile.html
Теперь достаточно указать вашему приложению, откуда грузить html файлы в зависимости от версии, и всё будет работать само по себе!
Установка
Чтобы добавить grunt-html-inheritance в ваш проект, нужно установить npm модуль командой
npm install grunt-html-inheritance --save-dev
или добавить зависимость в package.json, загрузить задачу в Gruntfile.js командой
grunt.loadNpmTasks('grunt-html-inheritance');
и настроить задачу следующим образом:
grunt.initConfig({
html_inheritance: {
main: {
files: {
src:'**.html' //селектор для файлов
},
options: {
modules: ["version1", "version2"], //список модулей
dstDir: "../dist",//папка, в которую будут складываться скомпиленные файлы
},
},
},
});
В AngularJS приложении важным условием легкого переключения между версиями HTML файлов является использование хелпера для построения путей к файлам шаблонов вместо жёсткого прописывания этих путей в директивах и роутингах.
Преимущества
Самое интересное преимущество такой системы кастомизации — основную версию проекта можно использовать без компиляции html файлов, так как bl-аттрибуты не мешают браузеру отображать исходный файл. Так же для использования такого метода не нужно изучать новый синтаксис какого-либо шаблонизатора, вся логика реализуется с помощью обычных html-аттрибутов, знакомых каждому разработчику.
Использование
Кроме замены базового тега в патче, описанного в примере выше, так же доступны следующие режимы:
- Удаление — удаление в патче родительского элемента
- Вставка — вставка в патче элемента, тогда как в основной версии элемента не будет
- Модификация аттрибутов — удаление и добавление аттрибутов к родительскому элементу, удаление и добавление классов.
Полная документация доступна на странице пакета или в репозитории на github. Модуль покрыт тестами и нуждается в дальнейшем совершенстовании. Приглашаю сообщество помочь в развитии, писылайте pull-реквесты!
Автор: Houston