- PVSM.RU - https://www.pvsm.ru -
По факту, LibJS это связка нескольких библиотек. О Mask [1] и Include [2] я уже вкратце рассказал, а сегодня завершит трилогию CompoJS(@github [3]).
route('compo','file:///d:/dev/compo/{name}/lib/{name}.js');
include('compo','mycomponent');
<myComponent <!-- settings --> />
(без javascript-a) Итого 2 строчки кода, (3) и (4), и компонент подключен в проект вместе с ресурсами и другими компонентами. Больше не надо копировать картинки и стили — все под капотом. Хочу отметить, что разрабатываю в основном мобильные приложения, так что «под капотом» должно быть что-то легкое, но очень быстрое. В статье также будет приведен пример компоненты с использованием библиотеки @PrismJS [4]
Начнем пожалуй с организации кода.
Так выглядит «средне статистическая» структура. Компонент может находится как в директории проекта, если это проект-специфический компонент, так и вне проекта, если это для повторного использования. Компонент может быть как маленький, так и вплоть до целого приложения. Разработка таким образом в разы ускоряется и замечательно распределяется(делегируется). Все сводится собственно к разработке компонент, которая у меня начинается в {component}/dev/*
— здесь создается и тестируется компонент. Теперь от идеи до реализации —
@github IncludeJS [5]
@github IncludeJS.Builder [6]
С прошлой статьи про IncludeJS я переделал сборщик под node.js и включил в проект. Что нам эти вещи дают:
В качестве макета выступает 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
Такой подход удобный тем, что нужные элементы/компоненты всегда под рукой, и видно что наш компонент использует.
В приложении надо «подсветить» исходный код, и так, что б можно было его свернуть.
Участков много? Самое ТО для компоненты!
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 сам сможет собрать нужные скрипты, стили и скопировать наши "+" "-" картинки в каталог приложения.
Результат [7]
/** ... */
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 [8], можно посмотреть дополнительные примеры и описания.
Вот такая система получилась и потихоньку буду выкладывать компоненты к ней.
Возможно вам и не понравилось, но нам она очень сильно упрощает разработку. Буду рад выслушать замечания. А если есть подобные вещи, буду тоже рад на них взглянуть.
Удачи.
Автор: tenbits
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/16494
Ссылки в тексте:
[1] Mask: http://habrahabr.ru/post/149509/
[2] Include: http://habrahabr.ru/post/150370/
[3] @github: https://github.com/tenbits/CompoJS
[4] @PrismJS: http://prismjs.com
[5] IncludeJS: http://tenbits.github.com/CompoJS/#include
[6] IncludeJS.Builder: http://tenbits.github.com/CompoJS/#includeBuilder
[7] Результат: http://tenbits.github.com/CompoJS/dev/sample/highlight/index.html
[8] @CompoJS: http://tenbits.github.com/CompoJS/#compo
Нажмите здесь для печати.