PowJS — это компилирующийся в реальном времени шаблонный движок для ECMAScript 6 с использованием Real-DOM.
Реальный DOM компилируется и отображается непосредственно на дереве DOM. Дерево DOM является шаблоном.
Нативная грамматика, где команды соответствуют нативной грамматике ECMAScript.
Экспортирование представлений осуществляется через исходный код ECMAScript.
Вставка значений атрибутов позволяет использовать интерполяцию для значений атрибутов, таких как `name="somethin {{expr}}"`.
Интерполяция текстовых узлов позволяет использовать интерполяцию для текстовых узлов, таких как `{{expr}}`. Пробелы в начале и конце текстового узла всегда удаляются.
По умолчанию параметры верхнего уровня имеют значение `(v, k)`.
Передача параметров происходит наследственным образом, если не используется директива `param`.
Для получения дополнительной информации перейдите на [Wiki][].
Процесс
Строка HTML ----> Real DocumentFragment
|
V
Real Node ----> PowJS <----> Представление
|
V
render(...args)
|
V
Real DocumentFragment ----> Real DOM
```## Установка
Среда NodeJS
```sh
yarn add powjs
Среда браузера
<script src="//unpkg.com/powjs"></script>
PowJS представляет собой модуль, основной функцией которого является:
module.exports = function (source, option) {
/**
* Аргументы
*
* source:
* undefined Возвращает PowJS.prototype
* string Компиляция HTML-источника
* Node Компиляция одного узла DOM
* [Node] Компиляция нескольких узлов DOM
* [Array] Загрузка уже скомпилированных представлений PowJS
* прочее Вызов ошибки или пустой результат рендера
*
* option:
* string Опциональный префикс команд при компиляции, по умолчанию ''
* Object Опциональный аддон при рендере
* прочее Пропущено
*
* Возвращаемое значение
*
* PowJS.prototype Если source === undefined
* Инстанс PowJS Если source instanceof Node
* или Array.isArray(source)
*/
};
Процесс рендера выполняется в объекте DocumentFragment
, что не влияет напрямую на страницу. Экспортированный вид представляет собой массив представлений, каждый из которых имеет структуру, соответствующую структуре узлов DOM:
/*! Генерировано PowJS. Не редактируйте */
module.exports = [
[
'TAG',
{/*Неконкатенированные атрибуты*/},
function(param, paramN) {
/*направления или конкатенация*/
},
[
/*...представление childNodes*/
]
/* Может быть имя */
]
/* более представлений ...*/
];
```Пример с навигационной панелью в виде хлебных крошек:
```html
<nav>
<div class="nav-wrapper">
<div class="col s12">
<a href="#!" class="breadcrumb">Первый</a>
<a href="#!" class="breadcrumb">Второй</a>
<a href="#!" class="breadcrumb">Третий</a>
</div>
</div>
</nav>
Высокочитаемый шаблон с использованием PowJS:
<nav func="breadcrumb" param="пути">
<div class="nav-wrapper">
<div class="col s12" each="пути, путь">
<a href="#!" class="breadcrumb">{{путь}}</a>
</div>
</div>
</nav>
Использование PowJS для компиляции и генерации кода (массив представлений):
const powjs = require('powjs');
let instance = powjs(htmlOrNodeOrView);
instance.toScript();
// instance.render(['Первый','Второй','Третий']);
Генерация:
[
[
"NAV", 0, 0,
[
[
"DIV",{ class: "nav-wrapper" }, 0,
[
[
"DIV", { class: "col s12" }, function(пути) {
this.each(пути);
},
[
[
"A", { href: "#!", class: "breadcrumb" }, 0,
[
[
"#", 0, function(путь, k, $l, $n) {
this.text(`${путь}`);
}
]
]
]
]
]
]
]
],
"breadcrumb"
]
]
Можно представить псевдокодом следующим образом:
function breadcrumb(пути) {
create('nav');
createChild(function() {
create('div', {class: 'nav-wrapper'});
createChild(function(){
create('div', {class: 'col s12'});
eachCreateChild(пути, function(путь) {
create('a', {href: '#!', class: 'breadcrumb'});
createChild(function(путь) {
text(путь);
});
});
});
});
}
Еще более простое представление, без имени функции (представления), использует значение параметра по умолчанию v
:
create('nav');
createChild(function() {
create('div', {class: 'nav-wrapper'});
createChild(function(){
create('div', {class: 'col s12'});
eachCreateChild(v, function(путь) {
create('a', {href: '#!', class: 'breadcrumb'});
createChild(function(путь) {
text(путь);
});
});
});
});
``````html
<nav>
<div class="nav-wrapper">
<div class="col s12" each="v">
<a href="#!" class="breadcrumb">{{v}}</a>
</div>
</div>
</nav>
Инструкции в узле являются атрибутами, значения которых представляют собой выражения или операторы ECMAScript, и конечный результат — создание функции представления. func = "name" Устанавливает имя сгенерированной функции представления param = "v,k" Параметры сгенерированной функции представления: см. пример ниже if = "условие" Условие отрисовки и изменяемой метки: см. раздел ниже let = "a=выражение,b=1" Локальные переменные: let a=выражение,b=1; do = "код" Выполнение кода: код; text = "выражение" Установка текста: this.text(выражение); html = "выражение" Установка HTML: this.html(выражение); end Сохраняет текущий узел, завершает отрисовку: return this.end(); end = "условие" Сохраняет текущий узел, завершает отрисовку при выполнении условия: если(условие) return this.end(); skip Пропускает отрисовку подузлов: this.skip(); skip = "условие" Отрисовывает подузлы при выполнении условия: если(условие) this.skip(); break Пропускает отрисовку братских узлов: this.break(); break = "условие" Пропускает отрисовку братских узлов при выполнении условия: если(условие) this.break(); render = "аргументы,...,аргументыN" Отображает подузлы: this.render(аргументы,...,аргументыN); each = "выражение,аргументы,...,аргументыN" Проходит по массиву и отображает подузлы: this.each(выражение,аргументы,...,аргументыN);### Порядок инструкций
Инструкции в узле являются атрибутами, значения которых представляют собой выражения или операторы ECMAScript, конечный результат — создание функции представления.
Порядок выполнения инструкций:
if
.Пример выше показывает, что структура шаблона PowJS полностью соответствует функциям представления:
<тег func="имя" param="данные" if="Array.isArray(данные)"
id="{{данные[0]}}" each="данные" break class="статичное">
<!-- ... -->
</тег>
Соответствующий псевдокод:
function имя(данные) {
if(Array.isArray(данные)) {
this.create('тэг', {class:'статичное'});
} else {
return;
}
this.attr('id', данные[0]);
}
``` func = "имя" Устанавливает имя сгенерированной функции представления
param = "v,k" Параметры сгенерированной функции представления: см. пример ниже
if = "условие" Условие отрисовки и изменяемой метки: см. раздел ниже
let = "a=выражение,b=1" Локальные переменные: let a=выражение,b=1;
do = "код" Выполнение кода: код;
text = "выражение" Установка текста: this.text(выражение);
html = "выражение" Установка HTML: this.html(выражение);
end Сохраняет текущий узел, завершает отрисовку: return this.end();
end = "условие" Сохраняет текущий узел, завершает отрисовку при выполнении условия: если(условие) return this.end();
skip Пропускает отрисовку подузлов: this.skip();
skip = "условие" Отрисовывает подузлы при выполнении условия: если(условие) this.skip();
break Пропускает отрисовку братских узлов: this.break();
break = "условие" Пропускает отрисовку братских узлов при выполнении условия: если(условие) this.break();
render = "аргументы,...,аргументыN" Отображает подузлы: this.render(аргументы,...,аргументыN);
each = "выражение,аргументы,...,аргументыN" Проходит по массиву и отображает подузлы: this.each(выражение,аргументы,...,аргументыN);### Порядок инструкций
Первое, инструкции как атрибуты узла не повторяются, это указано в спецификациях HTML DOM.
Порядок выполнения инструкций:
1. Создание узла, содержащего код, сгенерированный инструкцией `if`.
1. Установка статических атрибутов, без использования интерполяции.
1. Выполнение сгенерированной функции представления, конкретный код и инструкции или интерполируемые атрибуты следуют в том же порядке, что и в исходном шаблоне.
Пример выше показывает, что структура шаблона PowJS полностью соответствует функциям представления:
```html
<тег func="имя" param="данные" if="Array.isArray(данные)"
id="{{данные[0]}}" each="данные" break class="статичное">
<!-- ... -->
</тег>
Соответствующий псевдокод:
function имя(данные) {
if (Array.isArray(данные)) {
this.create('тег', {class: 'статичное'});
} else {
return;
}
this.attr('id', данные[ Yöntem dökümü için bu satırın çevirisi yapılmadı. ]);
this.each(данные);
this.break();
}
Инструкция skip
не должна использоваться после each
, render
, html
, text
, так как подузлы уже были отрисованы.
Аналогично, если первым используется end
, то последующие инструкции не будут выполнены.
Может потребоваться использовать инструкцию do
после each
, render
для дальнейшей обработки.
PowJS проверяет наличие явных конфликтов между инструкциями во время компиляции, но важно иметь хорошую практику записи порядка инструкций. Например, инструкции func
, param
, if
должны быть расположены в начале для удобства чтения.### ИнтерполяцияИнтерполяция может использоваться в текстовых узлах или ненаправленных атрибутах, но инструкции могут использовать только ECMAScript, использование интерполяции запрещено.
Интерполяция преобразуется в шаблонные строки ECMAScript, PowJS просто заменяет {{
, }}
на ${}
, {}
.
abc {{exp}} def ===> `abc ${exp} def`
Инструкция func
используется для присвоения имени сгенерированной функции представления, чтобы можно было вызывать её внутри представления.
<b func="name"><i>{{@name}}</i></b>
<b func="name"><i if="something && '@name'"></i></b>
<b func="name"><i do="if(something) return this.call('name', arg)"></i></b>
Как показано выше, есть два способа вызова функции подпредставлений:
{{@
, }}
@
в if
, при этом текущий узел ещё не создан, поведение — передача узлаthis.call
для передачи имени представления и других аргументов, когда текущий узел уже создан, поведение — создание подузлаВызов представления эквивалентен вызову подфункции, что может привести к рекурсии или даже бесконечному циклу, следует осторожно использовать.
Пример:
{{@name}}<span func="name">да</span>
<!-- render().html() вывод: -->
<span>да</span><span>да</span>
Пример:
<i if="'@name'">никогда</i><span func="name">да</span>
<!-- render().html() вывод: -->
<span>да</span><span>да</span>
```Пример:
```html
<b><i if="'@name'">никогда</i></b><span func="name">да</span>
<!-- render().html() вывод: -->
<b><span>да</span></b><span>да</span>
Пример:
<i do="return this.call('name')">никогда</i><span func="name">да</span>
<!-- render().html() вывод: -->
<i><span>да</span></i><span>да</span>
Пример:
<b><i do="return this.call('name')">никогда</i></b><span func="name">да</span>
<!-- render().html() вывод: -->
<b><i><span>да</span></i></b><span>да</span>
Инструкция param
используется для генерации параметров формы функции представления. Если она используется, то должна содержать полный список параметров формы; в противном случае параметры формы будут наследованы от родителя.
Инструкция render
используется для рендера подслоя, а инструкция each
используется для прохода по первому параметру и вызова render
с добавлением параметров.
Поддерживается выведение параметров формы для подслоев: выполнение выведения параметров формы происходит, если выполняется хотя бы одно условие; в противном случае подслой продолжает использовать наследованные параметры формы.
:
, из последующих аргументов извлекаются параметры формы подслоя param
.each
используется xxx-
для создания пользовательских параметров формы, в противном случае используются параметры формы по умолчанию.Инструкция each
всегда добавляет следующие четыре параметра в фиксированном порядке после пользовательских параметров:val-
для переопределения имени аргумента, по умолчанию v
.key-
для переопределения имени аргумента, по умолчанию k
.len-
для переопределения имени аргумента, по умолчанию $l
.num-
для переопределения имени аргумента, по умолчанию $n
.Поведение:each
всегда добавляются после пользовательских параметров в строго определённом порядке.param
для переопределения имени аргумента.Пример: параметры render
не начинаются с :
, поэтому дедукция аргументов не применяется.
<ul render="k,v"><li>{{k}}{{v}}</li></ul>
<!-- pow.render(1,2).html() вывод: -->
<ul><li>12</li></ul>
Пример: параметры render
начинаются с :
, поэтому происходит дедукция аргументов.
<ul render=":k,v"><li>{{k}}{{v}}</li></ul>
<!-- pow.render(1,2).html() вывод: -->
<ul><li>21</li></ul>
Пример: параметры each
всегда добавляются в конце.
<dl param="array, id" each=":array,id">
<dd>{{id}}:{{item}}</dd> <!-- функция(id,item,v,k,$l,$n) -->
</dl>
<dl param="array, id" each="array,id,val-item">
<dd>{{id}}:{{item}}</dd> <!-- функция(id,item,k,$l,$n) -->
</dl>
<dl param="array, id" each=":array,id,val-item,num-row">
<dd>{{id}}:{{item}}</dd> <!-- функция(id,item,key,$l,row) -->
</dl>
Алгоритм извлечения имен аргументов для дедукции аргументов:
/**
* Разделение выражения аргументов для дедукции аргументов
* @param {string} expOfRenderOrEach Без начального ':'
* @return {array}
*/
function splitArguments(expOfRenderOrEach) {
return expOfRenderOrEach.match(/(key-|val-)?(([a-z]\w*),|([a-z]\w*)$)/ig)
.map(function(s) {
return s.endsWith(',') ? s.slice(0, -1) : s;
});
}
Инструкция if
создаёт функцию, которая проверяет условие отрисовки и может менять имя узла или вызывать другую шаблонную функцию.Пример: чистое условие отрисовки
<ul param="data" if="Array.isArray(data)"></ul>
Создаётся:
[
[
function(data) {
return Array.isArray(data) && "UL";
}
]
]
Пример: если условие заканчивается на ;
, то не добавляется дефолтное имя узла.
<ul param="data" if="Array.isArray(data) && 'OL'||'DIV';"></ul>
Создаётся:
[
[
function(data) {
return (Array.isArray(data) && "OL") || "DIV";
}
]
]
Пример: использование заполнителя ---
в кавычках, но PowJS не проверяет наличие этих кавычек.
<ul param="data" if="Array.isArray(data) && '---'"></ul>
Создаётся:
[
[
function(data) {
return Array.isArray(data) && "UL";
},
]
]
внутренняя реализация команды:
directives.if = function(exp, tag) {
if (exp.includes('---')) {
exp = exp.replace(/---/g, tag);
return `return ${exp};`;
}
if (exp.endsWith(';')) return `return ${exp}`;
return `return ${exp} && '${tag}';`;
};
то есть:
---
, заменяет ---
текущим именем тэга||
, добавляет TAG
&& TAG
влияние возвращаемого значения:- Нечётное значение — отказаться от создания узла
#
— создать узел Text
, содержимое узла — всё после #
@
— вызвать названный вид, передать наследуемые аргументы=
— присвоить значение =
после this.parent.textContent
, типичный случай стиль
!
— создать узел комментария, содержимое узла — всё после !
:
— псевдоузел, выполнить действие this.node = this.parent
, затем продолжить выполнение последующих команд (кода)Element
this.node === null
### skip-breakВ методе рендера render
рендеринг потомков является шагом текущего уровня; skip
пропускает текущий уровень, то есть пропускает рендеринг потомков.
А рендеринг потомков происходит в цикле, break
выходит из цикла, то есть пропускает рендеринг братских уровней.
Когда другие команды не удовлетворяют требованиям, do
является последним средством, позволяющим напрямую использовать оригинальный ECMAScript код.
Пример:
<div param="array" if="isArray(array)"
do="if(isArray(array[0])) return this.each(array)">
...
</div>
генерирует:
[
[
function(array) {
return isArray(array) && "DIV";
},
0,
function(array) {
if (isArray(array[0])) return this.each(array);
this.render(array); // PowJS дополняет недостающее поведение
},
[["#..."]] // # начинается с Text узла
]
]
Если используемые команды не связаны с рендерингом, PowJS дополняет this.render
.
Аналогично, использование команды skip
позволяет избежать выполнения дополняющего this.render
.
Атрибуты:
x add-on, если требуется, можно установить в любое время
node только для чтения, текущий узел, созданный рендером
parent только для чтения, родительский узел текущего узла, самый верхний уровень — это временный узел типа DocumentFragment BODY
Методы: create() Внутренний метод, создаёт текущий узел
end() Внутренний метод, завершает команду
break() Внутренний метод, прерывает выполнение команды
render(...) Входной метод для отрисовки, отрисовывает и возвращает this
each(x,...) Обход отрисовки, отрисовывает и возвращает this, внутренне вызывает this.render(..., v, k)
text(expr) Уникальная команда
html(expr) Уникальная команда
call(name,...) Вызов представления
addon(object) Дополнительный метод, устанавливает плагин или контекст, возвращает this
isRoot() Дополнительный метод, проверяет является ли this корнем представления
isReal() Дополнительный метод, проверяет соединён ли текущий узел с реальным DOM страницы
attr(attrName,v) Дополнительный метод, устанавливает или возвращает значение атрибута текущего узла
prop(propName,v) Дополнительный метод, устанавливает или возвращает значение свойства текущего узла. Например, checked.
firstChild() Дополнительный метод, возвращает первый дочерний узел this.parent
childNodes() Дополнительный метод, возвращает все дочерние узлы this.parent
lastChild() Дополнительный метод, возвращает последний дочерний узел this.parent
query(selector) Дополнительный метод, выполняет выборку всех узлов this.parent согласно селектору
slice(...) Дополнительный метод, вызывает Array.prototype.slice
inc() Дополнительный метод, увеличивает счётчик: return ++counter pow(inc) Дополнительный метод, генерирует уникальный ID если (inc) this.inc(); return '-pow-' + counter;
toScript() Дополнительный метод, экспортирует исходный код представления
exports(target) Дополнительный метод, экспортирует исходный код представления, префикс module.exports =
renew(node) Операция узла, заменяет узел node отрисованным узлом
appendTo(node) Операция узла, добавляет отрисованный узел в конец node
insertBefore(node) Операция узла, вставляет отрисованный узел перед node
insertAfter(node) Операция узла, вставляет отрисованный узел после node
removeChilds() Операция узла, удаляет все дочерние узлы этого узла### Каждый
Уникальная команда each
, этот метод может проходить через объект типа [object Object]
или ArrayLike.
Обязательные параметры всегда передаются значением, ключом (индексом) в метод render
.
При вызове метода end()
, обязательно следует завершить представление, как это делается при использовании команды return
.
Иначе сложные логики или неправильный порядок команд могут привести к непредвиденным результатам.
Вершина представления создана в виде экземпляра PowJS, который является вершинным экземпляром, во время отрисовки создаются временные экземпляры.
У вершинного экземпляра свойства parent
и node
являются одним и тем же объектом, и свойство root
вершины равно null. Реализация:
PowJS.prototype.isRoot = function() {
return !this.root;
};
Этот объект является:
document.createElement('template').content;
Процесс рендера происходит в template
-> DocumentFragment
, что не直接影响页面。
Топ-уровневный экземпляр может иметь несколько дочерних узлов, что зависит от следующего:
render
выполняется несколько разФакты:- view
— массив представлений, каждый элемент которого представляет собой представление одного узла.
render
— рендеринг дочерних узлов, проходящий через массив представлений view
и генерирующий дочерние узлы для родителя.each
— проход по рендерингу дочерних узлов, вызывающий render
с передачей значений и ключей (индексов).Не следует использовать методы attr
, prop
для вершинного объекта. Будьте осторожны при использовании методов text
, html
.Использование this.parent === this.node
для isRoot
имеет более глубокие причины:
this.parent
и this.node
, создавая больше возможностей изменения.this.parent
или this.node
на узел на странице позволяет осуществлять реальное время рендеринг.Свойство x
экземпляра PowJS
представляет собой объект addon
, предоставленный пользователем, который объединяет плагины и контекст пользователя.
Когда имя свойства совпадает с именем свойства в addon
и это функция, то эта функция является плагином,
который будет выполнен при рендере, если его имя совпадает с атрибутом. Естественно, если он не определяется как плагин, то управляет им сам пользователь (в контексте).
Прототип функции плагина:
/**
* Прототип плагина, если он был выполнен, PowJS больше не будет устанавливать это свойство
* @param {PowJS} pow текущий экземпляр PowJS
* @param {string} val значение свойства
* @param {string} key имя свойства
*/
function plugin(pow, val, key) {
//...
}
Пример:
let pow = require('powjs');
pow(`<img src="1.jpg" do="this.attr('src','2.jpg')">`, {
src: function(pow, val) {
pow.attr('data-src', val);
}
}).render().html();
// вывод: <img data-src="2.jpg">
Псевдоузлы не создаются как DOM узлы и служат эффектом блока кода.Учитывая, что пользовательские узлы ещё не поддерживаются большинством браузеров, можно создать псевдоузлы с помощью if="':';"
:
<div param="array" if="':';" each="array,val-name">
<b>{{name}}</b>
</div>
<!-- render([1,2,3]) вывод -->
<b>1</b><b>2</b><b>3</b>
Представление:```js
[
[
function(массив) {
return ":";
},
0,
function(массив) {
this.each(массив);
},
[
[
"B",
0,
0,
[
[
"#",
0,
function(имя, k, $l, $n) {
this.text(${имя}
);
}
]
]
]
]
]
]
## xPowJS
Для расширения прототипа PowJS можно использовать `require('powjs')()`. Чтобы избежать конфликтов с будущими версиями, в PowJS сохранены свойства и методы, начинающиеся с символа `$`, гарантируется же отсутствие использования префикса `x`.
На самом деле, свойства с префиксом `x` уже используются для пользовательских плагинов.
## shadowRoot
PowJS не управляет shadowRoot. Если в конструкторе используется PowJS для создания потомков, то это независимо.
Поэтому `powjs(shadowroot).html()` не будет содержать внутренний HTML элемента `shadowroot.shadowRoot`, что полностью соответствует первоначальному значению Shadow DOM.
## Лицензия
Лицензия MIT <https://gitee.com/powjs/powjs/blob/master/LICENSE>
[PowJS]: https://gitee.com/powjs/powjs
[Wiki]: https://gitee.com/powjs/powjs/wiki
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )