Цитата:
Цитата:
Цитата:
В моем предствлении это будет так:
@set theme_1, theme_2, theme_3 < theme (data) {
&.title: data.title
}
|
Цитата:
<div class="b-me"> ... </div>
.b-me {
@extend .b-about;
}
Т.е. у мя тоже всё гуд :) Единственное принципиальное отличие твоего подхода от моего, что ты полностью абстрагируешься от html, заменяя сущность тега, на сущность элемента, а у меня абстракция на уровне блоков, т.е. структура блока всё равно описывается старым добрым HTML:
{setBEM b-button, tag: 'span'}
{template bButton() extends iBlock}
<button class="{this.blockName}__btn">
...
</button>
{end}
/// Пример вызова:
{bem b-button, name: '...', ...}Текст кнопки{end}
Но кстати, если у меня меняется именно тема, то я создаю не дочерний блок, а применяю модификатор: b-button_theme_dark и т.д. |
Цитата:
|
Небольшой апдейт: в скрипт трансляции добавлен сборщик файлов Jossy, т.е. если вы компилите шаблоны с помощью консольной команды snakeskin или с помощью фаел вотчера шторма, то вы можете использовать директивы вроде
//#includeдля конкатенации файлов - это может быть очень удобным при разбиении шаблонов на разные файлы. |
У меня тоже есть подобный препроцессор. Правда пока нельзя писать инструкции в виде комментариев )
А чем грант не подошел? |
Цитата:
ЗЫ: понравилась твоя либа для тестирования :) |
Цитата:
|
Цитата:
|
Цитата:
|
kobezzza, у меня возник глупый вопрос: как ты навешиваешь обработчики событий для шаблонизируемых элементов? Через делегирование?
Хочется максимально простого и понятного кода. Допустим, когда я использую ангуляр, я пишу так:
<div ng-repeat="item in array"> <!-- повторить див столько раз, сколько элементов в массие -->
<div ng-click="controller.click(item)"></div> <!-- навесить обработчик клика на каждый элемент -->
</div>
// упрощенный js
var controller = {
click: function(item) {
// item - элемент модели, по кот. кликнули
}
};
можно ли как-нибудь добиться подобного поведения? Так же хочется услышать/увидеть, как ты навешиваешь обработчики с данным шаблонизатором. |
Цитата:
{template bButton.prototype.tpl()}
<div class="{this|el 'cont'|elMod 'focus', 'true'}">
</div>
{end}
Тут фильтры el и elMod и т.д. являются часть моего фреймворка который реализует паттерн БЭМ, т.е. это отдельная хреновина не имеющая отношения к шаблонизатору. Сам Snakeskin - это просто транслятор и микролиба (фильтры + итераторы) и ничего больше. PS: раз уж тут отметился, то скажу, что в декабре хочу запилить новую версию с поддержкой доопределения блоков и прототипов (сейчас есть только переопределение), т.е. что-то вроде:
{template page()}
{block scripts}
<script src="1"></script>
<script src="2"></script>
{end}
{end}
{template page2() extends page}
{block scripts}
{super} /// будет заменено на block scripts из page
<script src="3"></script>
{end}
{end}
Сейчас похожее можно сделать с помощью миксина из блоков и прототипов, но выглядит это криво.
{template page()}
{proto scripts}
<script src="1"></script>
<script src="2"></script>
{end}
{block scripts}
{apply scripts}
{end}
{end}
{template page2() extends page}
{proto page2_scripts}
<script src="3"></script>
{end}
{block scripts}
{apply scripts}
{apply page2_scripts}
{end}
{end}
|
Цитата:
Цитата:
Цитата:
<div onclick="fn(this)"></div> |
Цитата:
|
Цитата:
|
Цитата:
Шаблон же это простая яваскриптовая функция, которая выплёвывает текст, который затем в innerHTML пихается, т.е. можно делегировать как угодно :) У меня "свои" обработчики пишутся не в шаблоне, а в коде блока. А обработчики для связи 2-х разных блоков в шаблоне как декларативные схемы. PS: а если тебе нужен дата-биндинг почему бы не взять один из уже имеющихся популярных фреймворков, или у тебя спортивный интерес/узкозаточенная задача ? |
kobezzza, спасибо за ответы и терпение :) Форум, как обычно, не плюсует.
|
Цитата:
|
А это должно работать? http://jsfiddle.net/NAPWB/5/
|
Цитата:
{template foo.tpl(name)}
<h1 onclick="bar.click(this)">Hello {name}!</h1>
{call this.header.tpl(name, 'ещё параметр')}
{/template}
/// Можно смело использовать неймспейсы любой длины, т.к. Snakeskin создаст их если что
{template foo.header.tpl(name, header)}
<h2>{header}</h2>
<h3>{name}</h3>
{/template}
НО Для повторного использования кода в шаблоне и его дочерних шаблонах есть прототипы, которые в отличии от вызова шаблона в шаблоне не создают зависимостей, т.е.
{template base(name)}
{proto foo}
121
{/proto}
{apply foo}
{apply foo}
{/template}
Прототип может содержать в себе другие прототипы, блоки и т.д. В дочернем шаблоне можно переопределить и вызвать любой родительский прототип. Прототип не может вызывать сам себя, т.е.
{proto foo}
{apply foo}
{/proto}
не получится:) И главное, в скомпилированном шаблоне прототипы никак не фигурируют и не создают никаких связей, т.е. они используются только на этапе трансляции. http://habrahabr.ru/post/168093/ здесь довольно исчерпывающее описание прототипов, хотя сама статья уже местами устарела, т.к. писалась для версии 1 почти год назад, а ща уже 2.4 |
Цитата:
|
На самом деле, для вызова любой функции (а шаблон - это простая функция) можно использовать простой вывод, аля:
{this.header.tpl(name, 'ещё параметр')}
Но дело в том, что на все такие выводы по умолчанию ставится фильтр html, который экранирует всякие вредные сущности:) По сути
{this.header.tpl(name, 'ещё параметр')|!html}
и
{call this.header.tpl(name, 'ещё параметр')}
эквивалентны, т.к. фильтр !html отменяет действие html, но с call как то получше выглядит |
Оказывается, так нельзя:
<script type="text/x-snakeskin-template" id="templates">
<!-- msg -->
{template app.templates.test(data)}
<div></div>
{/template}
</script>
на комменты ругается, если я правильно понял |
Цитата:
<script type="text/x-snakeskin-template" id="templates">
/*
* Мой супер шаблон
*/
{template app.templates.test(data)}
<div></div>
{/template}
</script>
Однако в след версии я планирую добавить возможность комментов, которые не вырезаются парсером, а вставляются в JS, для того чтобы делать анотации для GCC. А зачем тебе понадобилось вне шаблона что-то делать и как ты это планировал потом использовать? |
Цитата:
<script type="text/x-snakeskin-template" id="templates"> ... </script> использовать много шаблонов. Хотел добавить комментарии к каждому из них. Воспользуюсь таким синтаксисом: Цитата:
|
Цитата:
http://screencast.com/t/iU7AgM6jvjaj *** А по поводу подсветки, в WebStorm можно создать новый формат данных и задать ему подсветку, я так и сделал (но тут надо выносить шаблоны в отдельный файлики). ЗЫ: Если очень нужно, я могу добавит в парсер лексему XML комментариев, как комментарий ВНЕ декларации шаблона, это будет не сложно. |
Цитата:
За видео спасибо :) Цитата:
Цитата:
Цитата:
|
Цитата:
1) npm i -g snakeskin 2) Затем создаёшь новый тип данных в шторме (FileTypes) 3) Потом создаёшь новый вотчер (File Watchers) и настраиваешь его У меня параметры такие: Program: C:\Users\kobez_000\AppData\Roaming\npm\snakeskin.c md Arguments: -s $FileName$ -o $FileNameWithoutExtension$.ss.js Working Directory: $FileDir$ Output Path: $FileNameWithoutExtension$.ss.js Если компилишь шаблоны под ноду, то также нужно поставить флаг -n PS: Вместе со след версией выпущу плагин для grunt |
kobezzza, отлично, спасибо :)
|
Сегодня потратил день, на сортировку скопившихся "хотелок" от новой версии Snakeskin и утвердил следующее:
1) Директива
{super}
- для безопасной подстановки тела родительского блока или прототипа в указанное место в дочернем.2) Поддержка параметров для директивы proto Я долго думал над этой фичей, т.к. сама суть прототипов - это "мягкая" декомпозиция шаблона, с поддержкой механизма наследования и безопасной работой, т.е. замена пресловутых вызовов других шаблонов в шаблоне. В общем получается, что сейчас прототипы могут очень ограниченно выполнять свою функцию, т.к. не хватает двух вещей: параметров и поддержки рекурсии. Ожидаемый синтаксис будет таким:
{template foo(param)}
{proto icon(%iconName)}
{param} - {%iconName}
{end}
{apply icon('foo')}
{end}
Разумеется, старый синтаксис прототипов (без указания параметров) останется рабочим. 3) Поддержка рекурсий в прототипах. Хотя прототип напоминает простые функции JS, но на самом деле это не так, поэтому использование рекурсии изначально было запрещено, ввиду некорректной работы транслятора (он уходил в бесконечный цикл), но любую рекурсию можно представить в виде простого цикла, собственно что я и планирую сделать для рекурсивных прототипов. 4) Директива для конструкции switch-case-default 5) Директивы {return} {break} и {continue} для прерывания в теле директив циклов и прерывания общего шаблона (return) 6) Директива-хэлпер для безопасной работы с атрибутами узлов:
<input {attr 'type', 'text'} />
7) Декларация прототипов вне тела шаблона:
/// Прототип getIcon добавится в конец шаблона foo
{proto foo->getIcon}
...
{end}
8) Поддержка JSDoc комментариев: комментарии вида /** ... */ не будут вырезаться из шаблона и будут вставлены в конечный JS, чтобы можно было использовать их при дальнейшем сжатии с помощью GCC. В остальном будут исправления ошибок и различные доработки. Завтра приступаю к реализации. |
Здорово, но большинство опций я вряд ли буду использовать. Это уже для продвинутых "юзеров" :)
|
Цитата:
|
В последнее время все больше склоняюсь к выбору шаблонизатора с трансляцией на клиенте.
Кстати вот очень клевая штука: http://www.ractivejs.org/ |
Фух, почти закончил работу над новой версией, вышло дольше чем думал. На этой недели закончу, но уже предлагаю поиграться с бетой и заодно потестить : http://jsfiddle.net/NAPWB/6/.
С функциональной стороны сделано всё, что хотел, и даже больше, однако ещё нужно написать новые тесты, обновить доку и т.д. Ну а теперь по порядку. Изначально данный апдейт планировался как эволюционное развитие ветки 2.x, однако в ходе разработки, стало ясно, что необходимы некоторые принципиальные изменения, которые повлекут за собой несовместимость с предыдущей версией, поэтому новая версия выйдет как 3.0.0. Что изменилось по сравнению с 2.x: 1) Реализована блочная область видимости переменных вместо единой шаблонной, т.е.
{template foo()}
{if 1}
{var a = 1}
{a} /// 1
{/}
{a} /// error, a is not defined
{/}
Отмечу, что данное нововведение распространяется именно на переменные, т.е. константы работают также, как и раньше. 2) Переосмыслена и переделана работа с пробельными символами: теперь любой пробельный символ (будь то табуляция или переход строки) трактуются как пробел, однако смежные пробельные символы схлопываются в один, т.е.
{template foo()}
/// перевод строки даст пробел
<div
class="foo">
{/}
Также введена новая директива &, которая декларирует, что все пробельные символы после этой директивы до любого не пробельного символа будут вырезаться, т.е.:
{template foo()}
Привет{&} Мир! /// получится ПриветМир!
{/}
3) Директивы cut и save были убраны, а их функционал частично заменила новая директива placeholder. Эта директива имеет одинаковый синтаксис с директивой template, однако, в отличии от template, placeholder существует только на этапе трансляции, т.е.
{placeholder foo()}
foobar
{/}
foo(); // error Директива может участвовать в цепи наследований также, как и template. 4) Добавлен новый возможный аргумент для директив forEach и forIn (forIn отличается от forEach тем, что всегда итерирует объект, как for (key in data), причём без проверки на hasOwnProperty). Теперь порядок аргументов для forEach такой: Для массивов: *) Ссылка на элемент массива; *) Индекс массива; *) Ссылка на итерируемый массив; *) Является ли первым; *) Является ли последним; *) Длина массива. Для объектов: *) Ссылка на свойство; *) Ключ свойства; *) Ссылка на итерируемый объект; *) Номер итерации; *) Является ли первым; *) Является ли последним; *) Длина объекта. Для forIn аргументы такие же, как и у forEach для объектов. Кстати, теперь директивы forEach и forIn разворачиваются в циклы на этапе трансляции, что позволило увеличить скорость скомпилированных шаблонов, а также возможность использования новых директив break и continue. 5) Для директивы data изменено сокращение, теперь это =, т.е.
{template foo()}
{= foo, bar}
{/}
Старое сокращение {{}} теперь используется новой директивой decl, которая вставляет текст, так как он был написан вместе со скобками директивы, т.е.:
{template foo()}
{{foo}} /// даст {{foo}}
{/}
Также, как и в data, в decl можно прокидывать переменные из шаблона, с помощью синтаксиса ${}. Новая директива decl создана для удобного использования библиотек, которые осуществляют data binding (вроде http://www.ractivejs.org/). 6) Костыль для console был удалён, теперь нужно использовать директиву void
{template foo()}
{void console.log(1)}
/// или так
{?console.log(1)}
{/}
На этом ломающие изменения заканчиваются. Основные нововведения: 1) Добавлена директива super, которая осуществляет вставку тела родительского блока или прототипа:
{template base()}
<head>
{block scripts}
<script src="../1.js"></script>
{/}
</head/>
{/}
{template child() extends base}
{block scripts}
{super}
<script src="../2.js"></script>
{/}
{/}
2) Прототипы теперь поддерживают параметры и рекурсию, а также новую директиву return:
{template foo()}
{proto bar(i)}
{i}
{if i === 2}
{return}
{/}
{if i}
{apply bar(--i)}
{/}
{/}
{apply bar(4)} /// 4 3 2
{/}
3) Прототипы теперь можно выносить за пределы шаблона, перед декларацией
{proto foo->bar}
1
{/}
{template foo()}
{apply bar}
{/}
4) Добавлена поддержка jsDoc
/**
* Данный комментарий будет в скомпилированном JS
* @return {string}
*/
{template foo()}
1
{/}
5) Директива var теперь имеет сокращение : и поддерживает множественную декларацию
{template foo()}
{:a = 1}
{var b = 2, e = 3}
{/}
6) Новые директивы switch case (имеет сокращение >) default
{template foo()}
{switch 1}
{case 1} foo {/}
{> 2} foo2 {/}
{default} foo3 {/}
{/}
{/}
7) Новые директивы try catch finally
{template foo()}
{try}
{void dddd()}
{catch err}
{err}
{finally}
bar!!!
{/}
{/}
8) Новые директивы break и continue. Могут использоваться внутри forEach, forIn, for, while, do, repeat. 9) Для цикла repeat-until добавлен псевдоним do-while 10) Новая директива return. Может использоваться внутри прототипов или шаблона (в случае шаблона может принимать значение) 11) Новая директива attr для удобного задания атрибутов узлам в xml разметке:
{template foo()}
<div {attr 'class', 'foo'} {attr -'bind', 'bar'}></div> /// class="foo" data-bind="bar"
{/}
12) Частично переписано АПИ добавления директив, в релизе будет иметь документацию, пример:
Snakeskin.addDirective(
'if',
{
placement: 'template',
notEmpty: true
},
function (command) {
this.startDir();
if (this.isSimpleOutput()) {
this.save('if (' + this.prepareOutput(command, true) + ') {');
}
}
);
Snakeskin.addDirective(
'else',
{
placement: 'template'
},
function () {
if (this.structure.name !== 'if') {
throw this.error('Directive "' + this.name + '" can only be used with a "if"');
}
if (this.isSimpleOutput()) {
this.save('} else {');
}
}
);
13) Увеличена скорость работы транслятора и исправлено множество ошибок 14) Сильно увеличена скорость работы в "живом" режиме в браузере 15) Очень сильно доработана обработка ошибок 16) 100% code review и рефакторинг + теперь исходный код переписан на ECMAScript 6 :) Вроде всё :) |
Ради интереса сравнил скорость рендеринга с другими шаблонными движками... и каково было моё удивление, что Snakeskin занял первое место, обойдя даже самые примитивные шаблонки http://jsperf.com/templates/2 я почему то был уверен, что результат должен быть другим
|
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
{template}
<script>
....
</script>
{/template}
?Зачем усложнять то? Цитата:
switch text
| "one" => 1
| "two" => 2
| => 0
Ну или как LiveScript:
switch text
case "one"
then 1
case "two"
then 2
case "three", "four"
then "3-4"
default
then 0
|
Цитата:
Цитата:
Просто в современной разботке single-page приложений перерисовывать весть блок не очень гуд, особенно если в этом блоке сотни и тысячи элементов. |
Цитата:
Цитата:
|
Цитата:
Цитата:
/// Срежет не нужный пробел
<p>{&}
1
2
</p>
Цитата:
Цитата:
Цитата:
|
Цитата:
Если уж вводить синтаксический сахар, то лично мне кажется нужно отталкиваться от существующих решений. Вот пример из того же Haskell:
f x =
case x of
0 -> "one"
1 -> "two"
_ -> 0 - x
Цитата:
Цитата:
Цитата:
Вот к примеру в fest нет ничего лишнего, только основные инструкции и возможность писать произвольный JS код:
<fest:script>
var obj = {"foo": "bar"};
</fest:script>
<fest:each iterate="obj" index="i">
<fest:value>obj[i]</fest:value>
</fest:each>
Пока я вижу в этом только плюсы, т.к. не нужно придумывать новые инструкции и усложнять парсинг. Цитата:
Цитата:
|
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
Цитата:
|
| Часовой пояс GMT +3, время: 20:49. |