По факту, LibJS это связка нескольких библиотек. О Mask и Include я уже вкратце рассказал, а сегодня завершит трилогию CompoJS(@github).
Постановка задачи
- разработка компонент вне основного проекта
- «dev»-независимая маршрутизация —
route('compo','file:///d:/dev/compo/{name}/lib/{name}.js');
- легкое внедрение в проект —
include('compo','mycomponent');
- инициализация прямо из макета —
<myComponent <!-- settings --> />
(без javascript-a)
Итого 2 строчки кода, (3) и (4), и компонент подключен в проект вместе с ресурсами и другими компонентами. Больше не надо копировать картинки и стили — все под капотом. Хочу отметить, что разрабатываю в основном мобильные приложения, так что «под капотом» должно быть что-то легкое, но очень быстрое. В статье также будет приведен пример компоненты с использованием библиотеки @PrismJS
Компонентное проектирование
Начнем пожалуй с организации кода.
Так выглядит «средне статистическая» структура. Компонент может находится как в директории проекта, если это проект-специфический компонент, так и вне проекта, если это для повторного использования. Компонент может быть как маленький, так и вплоть до целого приложения. Разработка таким образом в разы ускоряется и замечательно распределяется(делегируется). Все сводится собственно к разработке компонент, которая у меня начинается в {component}/dev/*
— здесь создается и тестируется компонент. Теперь от идеи до реализации —
Загрузка
@github IncludeJS
@github IncludeJS.Builder
С прошлой статьи про IncludeJS я переделал сборщик под node.js и включил в проект. Что нам эти вещи дают:
- все то, что может AMD — асинхронное подключение скриптов,
- свободный стиль декларации «модуля» и без дополнительных манифестов,
- ленивые скрипты,
- подключение стилей,
- загрузка данных через XMLHTTPRequest,
- возможность собрать проект —
- скопировать все ресурсы внешних компонентов в рабочий каталог проекта.
- подправить пути к новым ресурсам
- объединить все скрипты и стили.
- ленивые скрипты и загруженные данные встроить в html.
Инициализация Компонентов
В качестве макета выступает MaskJS — он удобный, очень быстрый и поддерживает «кастомные контролы», на которых в свою очередь и базируются компоненты.
interface ICustomControl{
attribute String tagName;
IAppendChild render(JsonTemplateData values, IAppendChild container, Object context);
}
Здесь интересным аргументом является context — через него происходят подписки на события и в него же строится дерево компонентов. Важным событием является DOMInsert
, это вот почему мы можем иметь такой макет:
scroller { /** content */ }
И после вставки в DOM наш компонент Scroller сам обновится, подпишется на события мыши, нарисует скроллы. То есть, происходит по смыслу тот же $('.placeholder').initScroller()
, но уже без нашего участия.
Класс «Компонент»
Всю документацию приводить не буду, а остановлюсь на главных моментах.
-
$
Работает в паре с jQuery, Zepto — после рендеринга компоненты доступен ".$" с обернутыми
HTMLElement(s)
-
События
В конструкторе можно указать перечень событий которые будут делегированы элементам
this.events = { "touchEnd: header > .touchMe": this.onTouchMe, ... }
В ключе, как видим, указывается тип события и через двоеточие selector (какому элементу делегируем событие)
-
Дочерние элементы и компоненты
В конструкторе можно указать перечень элементов или других компонентов, которые будут выбраны после рендеринга в DOM
this.compos = { touchMe: '$: button.touchMe', //'[how: ][who]' ... }
$:
— выбирает елемент черезthis.$.find
compo:
— ищет дочерний компонент по css подобному селектору. Доступны (tagName, id, class
)
пусто
— указывает, что надо выбрать черезthis.$[0].querySelector
Такой подход удобный тем, что нужные элементы/компоненты всегда под рукой, и видно что наш компонент использует.
Пример
Постановка задачи
В приложении надо «подсветить» исходный код, и так, что б можно было его свернуть.
Участков много? Самое ТО для компоненты!
- Найдем хорошую библиотеку — PrismJS
- Создадим где-то папку с нашей компонентой, скопируем туда библиотеку #Prism с её стилями, найдем картинки "+","-"
- Напишем код компоненты:
include .js('prism/prism.lib.js') .css(['style/main.css', 'prism/prism.lib.css']) .done(function() { mask.registerHandler('highlight', Class({ Base: Compo, Extends: CompoUtils, Construct: function(){ this.compos = { header: '$: header' } }, events: { 'click: header': function(e) { this.compos.header.toggleClass('closed'); } }, render: function(values, container, cntx) { var code = this.nodes.content, attr = this.attr; /** Готовим container */ this.addClass('highlight'); this.tagName = 'div'; /** Готовим структуру компоненты */ var t = 'header; pre class="language-#{language}" > code class="language-#{language}";'; this.nodes = mask.compile(t); Compo.find(this.nodes, 'code', null, 'node').nodes = { /** Передаем код который должен быть подсвеченным */ content: code }; /** Отдаем родителю завершить рендеринг */ Compo.prototype.render.call(this, this.attr, container, cntx); /** Подсвечиваем код*/ Prism.highlightElement(this.$.find('code').get(0)); } })) });
- Добавляем файл стилей:
.highlight { background: rgb(222,222,222); /* ... */ } .highlight > header { height:50px; background:url(../images/collapse.png) 0 0 no-repeat; cursor: pointer; /* ...*/ } .highlight > header.closed { background-image: url(../images/expand.png); } .highlight > header.closed + pre { display: none; }
- Подключаем:
/*...*/ highlight language="javascript" > "var a = 10;" /*...*/
И больше никаких скриптов для инициализации не нужно.А если мы разработали highlighter вне главного проекта, и потом просто «заинклудили», то при релизе Include.Builder сам сможет собрать нужные скрипты, стили и скопировать наши "+" "-" картинки в каталог приложения.
Результат - А нужно целые файлы показать и подсветить?
/** ... */ render: function(){ /** ... */ $.get(this.attr.src, function(response){ var _code = this.$.find('code').get(0); _code.innerText = response; Prism.highlightElement(_code); }.bind(this) }
highlight lang="javascript" src="example/script.js";
Я этим хотел показать, что дополнительный функционал легко добавляется, а потом легко используется.
Здесь, @CompoJS, можно посмотреть дополнительные примеры и описания.
Вот такая система получилась и потихоньку буду выкладывать компоненты к ней.
Возможно вам и не понравилось, но нам она очень сильно упрощает разработку. Буду рад выслушать замечания. А если есть подобные вещи, буду тоже рад на них взглянуть.
Удачи.
Автор: tenbits