Сейчас на 99.9% сайтов используются PHP шаблоны. Во многих движках это является неотемлемой частью. Я хочу представить вам совершенно другой подход.
Недавно я разрабатывал тестовое задание и решил попробовать использовать JS шаблоны.
Вот, что в результате получилось:
1) Загрузка начинается с парсинга URL с помощью .htaccess и возвратом необходимой HTML-страницы.
2) Браузер подгружает с сервера javascript.
3) Запускает первую инициализацию: подгрузка файлов шаблонов (.skin) и файла необходимого языка (.lang — например, в виде XML).
4) Подгружает данные и рендерит их в браузер, используя подгруженные шаблоны.
5) Обрабатывает нажатие клавиш, ссылок итд. (про обработку back и next возможно расскажу в другой теме :) )
6) При необходимости, посылает запрос на сервер на приём новых данных.
На входе — URL, функция JS для обработки request, дополнительные параметры для функции, [URL для следующего последовательного запроса, функция JS, дополнительные параметры,[URL...]]
В модуле имеются функции:
trace — для генерации дополнительных шаблонов из строк, вида:
<!SKIN 'название_шаблона_1'>… ...<!END 'название_шаблона_1'>
Преобразуя их в %название_шаблона_1%, %название_шаблона_1.название_вложенного_шаблона%, %название_шаблона_2%.
use — подставление параметров в шаблон. Параметры в шаблонах хранятся в %название_параметра%.
redraw — записывает преобразованный шаблон в элемент, используя HTML id для идентификации нужного элемента.
К примеру, пусть шаблон (main.skin) выглядит так:
Добавляем нехватающие функции, которые выводят шаблон в окно браузера после инициализации и те, которые используются в шаблоне на onclick:
Работающий пример можно посмотреть здесь: zcn.ru/market
— Невозможность без введения дополнительных средств работать с URL.
— Невозможно использовать клавиши «назад» и «вперед». Если автор не предусмотрел эмуляцию.
— Поисковики не умеют парсить Javascript и AJAX.
— Вероятность отключенного JS у пользователя.
О данных проблемах и методах решения было сказанно сдесь — habrahabr.ru/blog/webdev/44753.html, по этому я не буду заострять внимание на них.
Плюсы тоже существенны:
— Довольно сильно (при хорошей оптимизации) снижается нагрузка на сервер.
— Для изменения вида сайта не требуется никакой доработки PHP-кода — только ccs/javascript и шаблоны.
— Прозрачный и достаточно ясный код Шаблонов и обработчика. Очень просто добавлять новые «страницы».
— Простое изменение различных настроек, простой подгрузкой других XML-файлов. Языка, Валюты (доллар, руб) и пр. Без перезагрузки страницы.
Недавно я разрабатывал тестовое задание и решил попробовать использовать JS шаблоны.
Вот, что в результате получилось:
1) Загрузка начинается с парсинга URL с помощью .htaccess и возвратом необходимой HTML-страницы.
2) Браузер подгружает с сервера javascript.
3) Запускает первую инициализацию: подгрузка файлов шаблонов (.skin) и файла необходимого языка (.lang — например, в виде XML).
4) Подгружает данные и рендерит их в браузер, используя подгруженные шаблоны.
5) Обрабатывает нажатие клавиш, ссылок итд. (про обработку back и next возможно расскажу в другой теме :) )
6) При необходимости, посылает запрос на сервер на приём новых данных.
1. AJAX Модуль
Модуль для получения XML/JSON и отправке на дальнейшее расстерзание в указанную JS-функцию. При надобности, возможен запуск нескольких последовательных запросов.function ajax_load(url,parcer,par,next){C помощью этой функции, мы как раз и будем подгружать данные с сервера без перезагрузки основной страницы.
var req, ab, done=0;
this.request = function(){
if (window.XMLHttpRequest)
ajaxRequest = new XMLHttpRequest();
else if (window.ActiveXObject){
ajaxRequest = new ActiveXObject(«Msxml2.XMLHTTP»);
if (!ajaxRequest)
ajaxRequest = new ActiveXObject(«Microsoft.XMLHTTP»);
}
return ajaxRequest;
}
this.ReqStatus=function() {
if (req.readyState == 4) {
clearTimeout(ab);
if (req.status == 200){
parcer(req,par);
if (next)
ajax_load(next[0],next[1],next[2],next[3]);
} else
alert(«AJAX Error: \n» + req.statusText + "\n" + url);
}
}
if (!url)
return parcer(par);
req=this.request();
req.onreadystatechange = this.ReqStatus;
req.open(«GET», url, true);
req.send(null);
ab = window.setTimeout(function(){req.abort();}, 10000);
return req;
}* This source code was highlighted with Source Code Highlighter.
На входе — URL, функция JS для обработки request, дополнительные параметры для функции, [URL для следующего последовательного запроса, функция JS, дополнительные параметры,[URL...]]
2. Skin Модуль
Модуль для преобразования Шаблона с параметрами в HTML.function Skin(){
var that = this;
this.skin = new Array();
this.trace = function (file){
var f,x;
var rx=/<!SKIN '([\w\d]+)'>([\s\S]*)<!END '\1'>/gim
f=that.skin[file].match(rx);
for (var i in f){
x=rx.exec(f[i]);
that.skin[file+"."+x[1]]=x[2];
that.trace(file+"."+x[1]);
that.skin[file]=that.skin[file].replace(f[i],'');
}
}
this.use = function (html){
var str;
do{
if (!(str = html.match(/%([\w\d.]+)%/gim)))
break;
for (var i in str){
var tp=str[i].replace(/%/gi,'');
if (that.skin[tp])
html=html.replace(str[i],that.skin[tp]);
else
html=html.replace(str[i],'');
}
}while(1);
return html;
}
this.redraw = function (window,html){
if (!document.getElementById(window))
alert(«ERR with ID „+window);
document.getElementById(window).innerHTML=that.use(html);
}
}* This source code was highlighted with Source Code Highlighter.
В модуле имеются функции:
trace — для генерации дополнительных шаблонов из строк, вида:
<!SKIN 'название_шаблона_1'>… ...<!END 'название_шаблона_1'>
Преобразуя их в %название_шаблона_1%, %название_шаблона_1.название_вложенного_шаблона%, %название_шаблона_2%.
use — подставление параметров в шаблон. Параметры в шаблонах хранятся в %название_параметра%.
redraw — записывает преобразованный шаблон в элемент, используя HTML id для идентификации нужного элемента.
3. Использование шаблонов
Возьмём маленькую часть от интернет магазина. Пусть она просто показывает- был пользователь или нет и выводила некоторую информацию в виде таблицы.К примеру, пусть шаблон (main.skin) выглядит так:
<!SKIN 'BODY'>А XML файл с языковым интерфейсом (rus.xml):
<div id=“menu»>
<span class=«goods_name» onclick=«ShowInfo();»>%LANG.INFO%</span>
<span class=«goods_name» onclick=«ChangeLang('rus')»>%LANG.RUSSIAN%</span>
<span class=«goods_name» onclick=«ChangeLang('eng')»>%LANG.ENGLISH%</span>
</div>
<div id=«main»>
<!SKIN 'WELCOME'>
<!SKIN 'FIRST'>%LANG.WELCOME_NEW%<!END 'FIRST'>
<!SKIN 'DATE'>%DAY%.%MONTH%.%YEAR%<!END 'DATE'>
<!SKIN 'LAST'>%LANG.WELCOME_OLD% %SKIN.WELCOME.DATE%<!END 'LAST'>
<!END 'WELCOME'>
</div>
…
<!SKIN 'ORDER'>
<table class=«price» width=«500px»>
%PRICES%
<!SKIN 'PIECE'><tr id="%GOODS_ID%"><td class=«order_name»>%GOODS.NUM%. %GOODS.NAME%</td><td>%GOODS.PRICE%</td></tr><!END 'PIECE'>
</table>
<!END 'ORDER'>
<!SKIN 'LOADING'>
%LANG.LOADING%
<!END 'LOADING'>
…
<!END 'BODY'>* This source code was highlighted with Source Code Highlighter.
<?xml version=«1.0» encoding=«windows-1251»?><objects>Теперь создаём JS-код для начальной загрузки шаблона и передачи управления (main.js):
<language>rus</language>
<word name=«ENCODING»>windows-1251</word>
<word name=«INFO»>Информация</word>
<word name=«SUPPORT»>Связаться с нами</word>
<word name=«ENGLISH»>English</word>
<word name=«RUSSIAN»>Русский</word>
<word name=«WELCOME_NEW»>Добро пожаловать, новый пользователь.</word>
<word name=«WELCOME_OLD»>Ваш последний визит был</word>
<word name=«LOADING»>Идёт соединение с Базой сайта</word>
</objects>* This source code was highlighted with Source Code Highlighter.
//Создаём объект для шаблонов
var skins=new Skin();
//Добавление в шаблоны, загруженного в виде XML, шаблонов языка %LANG.xxxxxx%
function SetLanguageXML(req){
var r=req.responseXML.getElementsByTagName(«word»),i;
for (i=0;i<r.length;i++)
skins.skin['LANG.'+r[i].attributes.getNamedItem(«name»).value]=r[i].firstChild.nodeValue;
}
//Парсинг загруженного шаблона в %SKIN.xxxxxx%
function TraceResponse(req){
skins.skin['SKIN']=req.responseText;
skins.trace('SKIN');
}
//Загружаем язык, загружаем шаблон и после всего этого выходим на инициализацию
ajax_load(«rus.xml»,SetLanguageXML,null,[«main.skin»,TraceResponse,null,[null,RedrawAll]]);* This source code was highlighted with Source Code Highlighter.
Добавляем нехватающие функции, которые выводят шаблон в окно браузера после инициализации и те, которые используются в шаблоне на onclick:
//Функция для изменения языка по onclickЗамечу, что во многих случаях движек для шаблонов позволяет делать 'исполняеммые' шаблоны, что правда делает их сложными в разборе. Здесь это легко реализованно с помощью Javascript:
function ChangeLang(lang){
//Если иметь rus.xml и eng.xml — то переход на другой язык потребует только перезагрузки файла с языком.
ajax_load(lang+".xml",SetLanguageXML,null,[null,RedrawAll]);
}
//Функция для регенерации начальной страницы при инициализации
function RedrawAll(req){
//Надо сказать, что в html обязанно быть объявлен элемент с id='body'. Например, <div id='body'></div>.
skins.redraw('body',skins.skin['SKIN.BODY']);
DrawWelcome();
}
//Функции для работы с Cookie
function createCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else var expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
function readCookie(name) {
var nameEQ = name + "=";
var ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0)==' ') c = c.substring(1,c.length);
if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
}
return null;
}
//Выводим Welcome screen ;)
function DrawWelcome(){
var date,t;
//Есть ли Cookie? Тогда пользователь у нас уже был. Считываем, когда и выводим эту информацию
if (t=readCookie('date')){
date=new Date;
date.setTime(t*1000);
skins.skin['DAY']=date.getDate();
skins.skin['MONTH']=date.getMonth();
skins.skin['YEAR']=date.getFullYear();
show=skins.skin['SKIN.BODY.WELCOME.LAST'];
}else{
//Если в первый раз на сайте — создаём Cookie и выводим другую информацию
date = new Date;
createCookie('date',parseInt(date.getTime() / 1000),7);
show=skins.skin['SKIN.BODY.WELCOME.FIRST'];
}
skins.redraw('main',show);
}* This source code was highlighted with Source Code Highlighter.
//Подгрузить с сервера и вывести info.xml в ясном формате (onclick функция)При этом файл info.xml необходимо будет генерить в таком виде:
function ShowInfo(){
skins.redraw('main',skins.skin['SKIN.BODY.LOADING']);
ajax_load(«info.xml»,DrawInfo);
}
function DrawInfo(req){
var i;
var p=req.responseXML.getElementsByTagName(«pricelist»);
//Обнуляем список товаров
skins.skin['PRICES']="";
skins.skin['GOODS.NUM']=1;
for (i=0;i<p.length;i++){
//Устанавливаем параметры для шаблона
skins.skin['GOODS.NAME']=p[i].getElementsByTagName(«name»)[0].firstChild.nodeValue;
skins.skin['GOODS.PRICE']=p[i].getElementsByTagName(«price»)[0].firstChild.nodeValue;
skins.skin['GOODS.ID']=p[i].getElementsByTagName(«id»)[0].firstChild.nodeValue;
//Добавляем в список товаров, готовые (отрендеренные) части таблицы
skins.skin['PRICES']+=skins.use(skins.skin['SKIN.BODY.ORDER.PIECE']);
skins.skin['GOODS.NUM']++;
}
skins.redraw('main',skins.skin['SKIN.BODY.ORDER']);
}* This source code was highlighted with Source Code Highlighter.
<?xml version=«1.0» encoding=«windows-1251»?><objects>Ну и наконец, index.html. Да, я не ошибся- именно html:
<pricelist>
<id>1</id>
<name>Хлеб</name>
<price>12</price>
</pricelist>
<pricelist>
<id>2</id>
<name>Арбуз</name>
<price>200</price>
</pricelist>
<pricelist>
<id>3</id>
<name>Картошка</name>
<price>32</price>
</pricelist>
</objects>* This source code was highlighted with Source Code Highlighter.
<html><head>
<meta http-equiv=«Content-Type» content=«text/html; charset=windows-1251»>
</head>
<body><div id='body'></div>
<script type=«text/javascript» src=«main.js»></script>
</body></html>* This source code was highlighted with Source Code Highlighter.
Работающий пример можно посмотреть здесь: zcn.ru/market
4. Недостатки и плюсы данного подхода
Недостатков мало, но они достаточно существенны:— Невозможность без введения дополнительных средств работать с URL.
— Невозможно использовать клавиши «назад» и «вперед». Если автор не предусмотрел эмуляцию.
— Поисковики не умеют парсить Javascript и AJAX.
— Вероятность отключенного JS у пользователя.
О данных проблемах и методах решения было сказанно сдесь — habrahabr.ru/blog/webdev/44753.html, по этому я не буду заострять внимание на них.
Плюсы тоже существенны:
— Довольно сильно (при хорошей оптимизации) снижается нагрузка на сервер.
— Для изменения вида сайта не требуется никакой доработки PHP-кода — только ccs/javascript и шаблоны.
— Прозрачный и достаточно ясный код Шаблонов и обработчика. Очень просто добавлять новые «страницы».
— Простое изменение различных настроек, простой подгрузкой других XML-файлов. Языка, Валюты (доллар, руб) и пр. Без перезагрузки страницы.