Сразу предупреждаю, что если вы не работали с JavaScript или просто скудно с ним знакомы, начните именно с него, иначе Node.js может показаться вам тёмным лесом. Хочу познакомить вас с языком сразу на практике, и так же показать идею по опять же практическому применению. Я приведу пример реализации маленького парсера шаблонов, всё это уложится в 2-а небольших файла, нарочито без излишеств, чтобы наглядно и прозрачно посмотреть «как это работает».
Итак, концепт таков: мы будем использовать вставки в шаблоне следующего вида:
<script type="nodejs">100+235;</script>Этот код выведет 335 на экран. Почему именно такой приём, а не например: <%=100+235%>? В этом и заключается идея, во первых, в этих тегах будет заключаться обычный JS-код, и такая конструкция будет поддерживать подсветку синтаксиса в редакторах без каких-либо ритуалов жертвоприношений с бубном. Почему именно JS-код? «Ведь это же варварство!» Ну мне лично нравится модель шаблонов 1С-Битрикса, по-моему очень удобно, просто делать условия и циклы на том языке, что знаешь, а не учить каждый раз новый синтаксис шаблонизатора и влезать в его тесные рамки. Плюс ко всему это же JS! Ловите мысль? Обыкновенно верстальщики как верстают, так и пишут на JS, а тут вставки, с тем же JS, то-есть они как бы и верстать продолжают, только зная что их код заменится на серверной стороне тем, что они в нём вернут в конце манипуляции. По мне так лучшее решение.
Теперь наконец перейдём к любимой практике, поэтапно напишем код с описанием действий. Первым делом создайте два файла: start.js для нашего скрипта и template.html для шаблона. Дальнейший код пишем в start.js. Подключим встроенные модули Node.js, которые необходимы для работы примера:
var http = require('http'); // для создания сервера и работы с запросами var fs = require('fs'); // для работы с файлами, в частности с файлом шаблона var vm = require('vm'); // для исполнения JS-кода в шаблонах, изолированно от внешней среды скрипта
Обратите внимание, что для Node.js не требуется никаких Апачей и прочего, если вы сравниваете его с PHP, то это иная стихия. Сервер поднимается прямо в скрипте и создаётся при выполнении скрипта из консоли. Объявим переменную для создания сервера и так же сразу объявим для примера JSON-структуру данных для экспорта в шаблон:
var host = { addr: '127.0.0.1', port: 8070 }; var jsonExport = { title: 'Мой первый парсер', list: [ {name:'Первый пункт списка', msg:'это только начало'}, {name:'Следующий', msg:'продолжение'}, {name:'Третий', msg:'и так сколько пожелаете!'} ] };Обратите внимание на порт 8070, это на всякий случай, если 80-ый занят у вас Apache-м, или ещё чем-нибудь. Если вы уверены, что порт свободен, можете и 80-ый поставить. 127.0.0.1 — это локальная машина, и доступен сервер будет только в её рамках, этого нам пока достаточно.
Далее создаём сам сервер с обработчиком запросов от браузера:
var server = http.createServer(function (request, response) { // код обработчика });
Пока проскочим код обработчика и сразу запустим прослушку порта 8070 на локальной машине и отправим отчёт в консоль:
server.listen(host.port, host.addr); console.log('Сервер успешно запущен: '+host.addr+':'+host.port);
Теперь пора описать код обработчика:
response.writeHead(200, {'content-type':'text/html; charset=utf-8'}); fs.readFile('./template.html', function (err, data) { // код обработки шаблона });Первая строчка — возврат в заголовках 200-ого ответа сервера, аля OK, всё хорошо. Второй параметр объектом передаёт остальные заголовки, в данном случае тип контента и кодировку. Следующая строка — это чтение файла шаблона и обработчик.
И как вы уже могли догадаться — код обработки шаблона, завершающая часть скрипта start.js:
var data = data.toString(); var dataArr = data.match(/\<script type=(nodejs|'nodejs'|"nodejs")\>([^\0]+?)<\/script\>/gim); for (var i=0; i<(dataArr.length||0); i++) { var code = dataArr[i].replace(/^\<script([^\>]+)\>([^\0]+?)<\/script\>$/im, '$2'); data = data.replace(/\<script type=(nodejs|'nodejs'|"nodejs")\>([^\0]+?)<\/script\>/im, vm.runInNewContext(code, jsonExport)); } response.end(data);1-ая строка — преобразуем полученный контент файла в строку, чтобы работать с регами. Подробностей о регах писать не буду, только отмечу ключи у рег, для понимания происходящего.
2-ая строка — создаем массив из найденный кусков кода парсера, который нужно обработать и заменить результатом выполнения. Ключи gim, g — более одного совпадения, i — независимость от регистра символов, m — многострочный поиск (вместо поиска в отдельных строках). По поводу рег, хочу отметить только один мой приём, который я часто использую, он может вам пригодиться. Символ точки захватывает все символы, кроме переводов строк, в данном же случае нам нужны и переводы строк! И вместо точки мы пишем: [^\0], — что значит всё, что не равняется null (пустоте). Интересно, да? Можно и просто [^], но если применять это в JavaScript, конечно же Internet Explorer выдаст ошибку, потому используем первый, более длинный вариант.
3-я строка — цикл, проходим по массиву найденных вызовов кода парсера.
1-ая строка цикла — у текущего кода парсера вытаскиваем код между <script*>...</script> и складываем в переменную code.
2-ая и 3-я строка цикла — в переменной data заменяем текущий вызов парсера на результат выполнения с помощью модуля vm, который выполняет заданный код в определённом контексте. В данном случае он передал нашу структуру JSON в виде этого контекста. Заметьте, что рега без ключа g, чтобы работать только с текущей вставкой, так как мы производим замену во всём коде целиком.
Наш файл start.js готов, вот его цельный вид:
var http = require('http'); var fs = require('fs'); var vm = require('vm'); var host = { addr: '127.0.0.1', port: 8070 }; var jsonExport = { title: 'Мой первый парсер', list: [ {name:'Первый пункт списка', msg:'это только начало'}, {name:'Следующий', msg:'продолжение'}, {name:'Третий', msg:'и так сколько пожелаете!'} ] }; var server = http.createServer(function (request, response) { response.writeHead(200, {'content-type':'text/html; charset=utf-8'}); fs.readFile('./template.html', function (err, data) { var data = data.toString(); var dataArr = data.match(/\<script type=(nodejs|'nodejs'|"nodejs")\>([^\0]+?)<\/script\>/gim); for (var i=0; i<(dataArr.length||0); i++) { var code = dataArr[i].replace(/^\<script([^\>]+)\>([^\0]+?)<\/script\>$/im, '$2'); data = data.replace(/\<script type=(nodejs|'nodejs'|"nodejs")\>([^\0]+?)<\/script\>/im, vm.runInNewContext(code, jsonExport)); } response.end(data); }); }); server.listen(host.port, host.addr); console.log('Сервер успешно запущен: '+host.addr+':'+host.port);
А дальше, недолго думая, пишем в файл template.html целиком следующий код (пояснений по вёрстке я думаю не требуется):
<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title><script type="nodejs">title;</script></title> </head> <body> <h1><script type="nodejs">title;</script></h1> <script type="nodejs"> var out = ''; out += '<ul>\n'; for (var i=0; i<list.length; i++) { out += '\t\t<li><b>'+list[i].name+'</b> '+list[i].msg+'</li>\n'; } out += '\t</ul>'; out; </script> </body> </html>Я думаю проанализировать этот код самим вам будет несложно. Во вставках просто выводятся значения по ключам из нашей JSON структуры контекста. Внизу выводится список, сначала всё складывается в локальную переменную, а потом она возвращается в конце для вывода.
Итак, теперь немного магии, набираем в консоли:
node start.jsЗаходим на http://127.0.0.1:8070 и любуемся! Успехов с экспериментами!
P.S. Не забывайте, что это только пример, тут даже нет обработчиков ошибок и прочего, пример очень минималистичен.
UPD 2011/05/26: Первоначально не учёл, что метод match у строки, в случае отсутствия совпадений — возвращает null. И если совпадений нет (а в нашем случае шаблонных вставок), то нет и метода length у переменной dataArr, которая используется в цикле, что повергало запрос в бесконечный цикл. Исправил:
for (var i=0; i<dataArr.length; i++) {На:
for (var i=0; i<(dataArr.length||0); i++) {
Комментариев нет:
Отправить комментарий