Руководство по Handlebars: движок шаблонов для Node / JavaScript

Введение В этой статье мы рассмотрим, как использовать механизм шаблонов Handlebars с Node.js и Express. Мы расскажем, что такое механизмы шаблонов и как можно использовать Handlebars для создания веб-приложений с рендерингом на стороне сервера (SSR). Мы также обсудим, как настроить Handlebars с фреймворком Express.js [https://expressjs.com/] и как использовать встроенные помощники для создания динамических страниц. Наконец, мы рассмотрим, как при необходимости разработать собственный помощник. W

Вступление

В этой статье мы рассмотрим, как использовать шаблонизатор Handlebars с Node.js и Express. Мы расскажем, что такое механизмы шаблонов и как можно использовать Handlebars для создания веб-приложений с рендерингом на стороне сервера (SSR).

Мы также обсудим, как настроить Handlebars с фреймворком Express.js и как использовать встроенные помощники для создания динамических страниц. Наконец, мы рассмотрим, как при необходимости разработать собственный помощник.

Что такое шаблонизатор?

Еще в 90-х годах, когда Интернет был представлен миру, он в основном использовался для научных целей, таких как публикация исследовательских работ и как канал связи между университетами и учеными. Большинство веб-страниц тогда были статичными. Статическая веб-страница одинакова для каждого пользователя и не меняется для каждого пользователя. Если что-то и нужно изменить на странице, это нужно сделать вручную.

В современном мире все гораздо более интерактивно и адаптировано под каждого пользователя. Сегодня почти у всех есть доступ к Интернету. Большинство веб-приложений сегодня динамические. Например, на Facebook вы и я увидим очень разные новостные ленты при входе в систему. Для каждого человека страница будет следовать одному и тому же шаблону (то есть последовательным сообщениям с именами пользователей над ними), но контент будет отличаться.

Это работа механизма шаблонов - определяется шаблон для новостной ленты, а затем на основе текущего пользователя и запроса к базе данных шаблон заполняется полученным контентом.

Мы можем использовать механизмы шаблонов как в серверной части, так и во внешнем интерфейсе. Если мы используем механизм шаблонов в бэкэнде для генерации HTML, мы называем это рендерингом на стороне сервера (SSR).

Рули

Handlebars популярен как для внутреннего, так и для внешнего шаблонов. Например, популярный интерфейсный фреймворк Ember использует Handlebars в качестве механизма создания шаблонов.

Handlebars - это расширение языка шаблонов Mustache , которое в основном ориентировано на простоту и минимальное создание шаблонов.

Использование Handlebars с Node.js

Для начала создайте пустую папку, откройте командную строку внутри этой папки и затем запустите npm init -y чтобы создать пустой проект Node.js с настройками по умолчанию.

Перед началом нам нужно установить необходимые библиотеки Node.js. Вы можете установить модули Express и Express-Handlebars , запустив:

 $ npm install --save express express-handlebars 

Примечание . При использовании Handlebars на стороне сервера вы, скорее всего, будете использовать вспомогательный модуль, такой как express-handlebars который интегрирует Handlebars с вашей веб-платформой. В этой статье мы сосредоточимся в основном на синтаксисе шаблонов, поэтому мы используем express-handlebars , но в случае, если вы сами обрабатываете компиляцию и рендеринг шаблона, вы захотите проверить справку по API компиляции также.

Затем давайте воссоздадим структуру каталогов Handlebars по умолчанию. views содержит все шаблоны Handlebars:

 . 
 ├── app.js 
 └── views 
 ├── home.hbs 
 └── layouts 
 └── main.hbs 

В layouts папки внутри views папки будет содержать макеты или обертку шаблона. Эти макеты будут содержать структуру HTML, таблицы стилей и сценарии, которые используются в шаблонах.

main.hbs - это основной макет. home.hbs - это пример шаблона Handlebars, который мы собираемся использовать.

По мере продолжения мы будем добавлять больше шаблонов и папок.

В нашем примере мы будем использовать один скрипт, чтобы упростить задачу. Импортируем необходимые библиотеки в наш файл app.js

 const express = require('express'); 
 const exphbs = require('express-handlebars'); 

Затем давайте создадим приложение Express:

 const app = express(); 

Теперь мы можем настроить express-handlebars качестве нашего механизма просмотра:

 app.engine('hbs', exphbs({ 
 defaultLayout: 'main', 
 extname: '.hbs' 
 })); 
 
 app.set('view engine', 'hbs'); 

По умолчанию расширение для шаблонов Handlebars - .handlebars . Но в настройках мы изменили его на .hbs помощью extname потому что он короче.

Давайте включим скрипты и стили Bootstrap в макет main.hbs

 <html lang="en"> 
 <head> 
 <!-- <meta> tags> --> 
 <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> 
 <title>Book Face</title> 
 </head> 
 
 <body> 
 <div class="container"> 
 {{{body}}} 
 </div> 
 
 <script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script> 
 <script src="https://cdn.jsdelivr.net/npm/ [email protected] /dist/umd/popper.min.js"></script> 
 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script> 
 </body> 
 </html> 

А теперь давайте изменим наш home.hbs включив в него сообщение:

 <h1>Hello World from Handlebars</h1> 

Чтобы получить доступ к этой странице, нам нужно настроить обработчик запросов. Установим его на корневой путь:

 app.get('/', (req, res) => { 
 res.render('home'); 
 }); 

Наконец, нам просто нужно начать прослушивать порт на предмет запросов:

 app.listen(3000, () => { 
 console.log('The web server has started on port 3000'); 
 }); 

Мы можем запустить приложение с помощью node app.js в консоли, хотя мы также можем использовать такой инструмент, как nodemon . С помощью nodemon нам не нужно перезапускать сервер каждый раз, когда мы вносим изменения - когда мы меняем код, nodemon обновляет сервер.

Установим:

 $ npm i -g nodemon 

И запуск приложения с помощью nodemon выполняется с помощью:

 $ nodemon app.js 

Зайдем в наше приложение через браузер:

настроенырули{.ezlazyload}

Когда все готово, давайте рассмотрим некоторые особенности Handlebars.

Особенности языка руля

Чтобы продемонстрировать некоторые функции Handlebars, мы создадим ленту в социальных сетях. Фид будет извлекать данные из простого массива, имитируя базу данных.

В ленте будут публикации с изображениями и комментариями. Если к изображению нет комментариев - появится сообщение «Прокомментируйте этот пост первым».

Давайте обновим наш home.hbs чтобы начать:

 <nav class="navbar navbar-dark bg-dark"> 
 <a class="navbar-brand" href="#">Book Face</a> 
 </nav> 
 
 <div class="posts"> 
 <div class="row justify-content-center"> 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="https://picsum.photos/500/500" 
 class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by Janith Kasun</h5> 
 
 <ul class="list-group"> 
 <li class="list-group-item">This is supposed to be a comment</li> 
 <li class="list-group-item">This is supposed to be a comment</li> 
 </ul> 
 </div> 
 </div> 
 </div> 
 </div> 
 </div> 

Как вы можете видеть в этом шаблоне Handlebars, мы добавили navbar и card с некоторыми жестко заданными значениями-заполнителями.

Наша страница теперь выглядит так:

рульшаблон{.ezlazyload}

Передача параметров в шаблоны

Теперь давайте удалим эти жестко запрограммированные значения с самой страницы и передадим их из сценария на страницу. Позже они будут заменены значениями комментариев в массиве:

 app.get('/', function (req, res) { 
 res.render('home', { 
 post: { 
 author: 'Janith Kasun', 
 image: 'https://picsum.photos/500/500', 
 comments: [] 
 } 
 }); 
 }); 

post содержит такие поля, как author , image и comments . Мы можем ссылаться на post в нашем шаблоне Handlebars {{post}} :

 <div class="posts"> 
 <div class="row justify-content-center"> 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="{{post.image}}" 
 class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{post.author}}</h5> 
 
 <ul class="list-group"> 
 <li class="list-group-item">This is suppose to be a comment</li> 
 <li class="list-group-item">This is suppose to be a comment</li> 
 </ul> 
 </div> 
 </div> 
 </div> 
 </div> 
 </div> 

Ссылаясь на эти значения с помощью обработчика, который отображает страницу, они вставляются на стороне сервера, и пользователю предоставляется, казалось бы, статический HTML-код с уже существующими значениями.

Условия использования

Поскольку у нас есть условная логика, то есть показывать комментарии, если они есть, и сообщение, если их нет, давайте посмотрим, как мы можем использовать условные выражения в шаблонах Handlebars:

 <div class="posts"> 
 <div class="row justify-content-center"> 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="{{post.image}}" class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{post.author}}</h5> 
 
 {{#if post.comments}} 
 <ul class="list-group"> 
 <!-- Display comment logic --> 
 
 </ul> 
 {{else}} 
 <ul class="list-group"> 
 <li class="list-group-item">Be first to comment on this post!</li> 
 </ul> 
 {{/if}} 
 </div> 
 </div> 
 </div> 
 </div> 
 </div> 

Теперь вы должны видеть только раздел «Прокомментируйте этот пост первым», отображаемый на вашей странице, поскольку массив комментариев пуст:

шаблон руля с навигационной панелью икартой{.ezlazyload}

#if - это встроенный помощник в Handlebars. Если оператор if возвращает true , блок внутри #if будет отрисован. Если false , undefined , null , "" , 0 или [] , блок не будет отображаться.

Наш массив пуст ( [] ), поэтому блок не отображается.

#if принимает только одно условие, и вы не можете использовать синтаксис сравнения JavaScript ( === ). Если вам нужно использовать несколько условий или дополнительный синтаксис, вы можете создать переменную в коде и передать ее в шаблон. Кроме того, вы можете определить своего собственного помощника, что мы и сделаем в последнем разделе.

Использование петель

Поскольку сообщение может содержать несколько комментариев, нам понадобится цикл, чтобы просмотреть их все и отобразить. Давайте сначала заполним наш массив некоторыми комментариями:

 app.get('/', function (req, res) { 
 res.render('home', { 
 post: { 
 author: 'Janith Kasun', 
 image: 'https://picsum.photos/500/500', 
 comments: [ 
 'This is the first comment', 
 'This is the second comment', 
 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec fermentum ligula. Sed vitae erat lectus.' 
 ] 
 } 
 }); 
 }); 

А теперь в нашем шаблоне мы будем использовать #each чтобы просмотреть их все:

 <div class="posts"> 
 <div class="row justify-content-center"> 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="{{post.image}}" class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{post.author}}</h5> 
 
 {{#if post.comments}} 
 <ul class="list-group"> 
 {{#each post.comments}} 
 <li class="list-group-item">{{this}}</li> 
 {{/each}} 
 </ul> 
 {{else}} 
 <ul class="list-group"> 
 <li class="list-group-item">Be first to comment on this post</li> 
 </ul> 
 {{/if}} 
 </div> 
 </div> 
 </div> 
 </div> 
 </div> 

Внутри цикла #each вы можете использовать this для ссылки на элемент, находящийся в текущей итерации. В нашем случае это относится к строке, которая затем отображается:

каждая вспомогательная функция вруле{.ezlazyload}

Если у вас есть массив объектов, вы также можете получить доступ к любому атрибуту этого объекта. Например, если есть массив людей, вы можете просто использовать this.name для доступа к полю name

Теперь давайте изменим параметры нашего шаблона, чтобы он содержал несколько сообщений:

 app.get('/', function (req, res) { 
 res.render('home', { 
 posts: [ 
 { 
 author: 'Janith Kasun', 
 image: 'https://picsum.photos/500/500', 
 comments: [ 
 'This is the first comment', 
 'This is the second comment', 
 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec fermentum ligula. Sed vitae erat lectus.' 
 ] 
 }, 
 { 
 author: 'John Doe', 
 image: 'https://picsum.photos/500/500?2', 
 comments: [ 
 ] 
 } 
 ] 
 }); 
 }); 

Теперь мы также можем использовать #each для перебора сообщений:

 <div class="posts"> 
 <div class="row justify-content-center"> 
 {{#each posts}} 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 <img src="{{this.image}}" class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{this.author}}</h5> 
 
 {{#if this.comments}} 
 <ul class="list-group"> 
 {{#each this.comments}} 
 <li class="list-group-item">{{this}}</li> 
 {{/each}} 
 </ul> 
 {{else}} 
 <ul class="list-group"> 
 <li class="list-group-item">Be first to comment on this post</li> 
 </ul> 
 {{/if}} 
 </div> 
 </div> 
 </div> 
 {{/each}} 
 </div> 
 </div> 

Использование частичного

Практически все веб-страницы содержат разные разделы. На базовом уровне это разделы Header , Body и Footer. Поскольку верхний и нижний колонтитулы обычно используются на многих страницах, их наличие на всех веб-страницах скоро станет чрезвычайно раздражающим и просто излишним.

К счастью, мы можем использовать Handlebars для разделения этих разделов в шаблонах и просто включать эти шаблоны как «частичные» в сами страницы.

В нашем случае, поскольку у нас нет header.hbs posts.hbs файлы header.hbs и posts.hbs в каталоге partials

 . 
 ├── app.js 
 └── views 
 ├── home.hbs 
 ├── layouts 
 | └── main.hbs 
 └── paritials 
 └── header.hbs 
 └── posts.hbs 

Затем мы переместим код заголовка в файл header.hbs

 <nav class="navbar navbar-dark bg-dark"> 
 <a class="navbar-brand" href="#">Book Face</a> 
 </nav> 

И код подачи в файл posts.hbs

 <div class="posts"> 
 <div class="row justify-content-center"> 
 {{#each posts}} 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="{{this.image}}" class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{this.author}}</h5> 
 
 {{#if this.comments}} 
 <ul class="list-group"> 
 {{#each this.comments}} 
 <li class="list-group-item">{{this}}</li> 
 {{/each}} 
 </ul> 
 {{else}} 
 <ul class="list-group"> 
 <li class="list-group-item">Be first to comment on this post</li> 
 </ul> 
 {{/if}} 
 </div> 
 </div> 
 </div> 
 {{/each}} 
 </div> 
 </div> 

А теперь мы можем включить их в файл home.hbs

 {{>header}} 
 
 {{>posts posts=posts}} 

Пользователь не заметит разницы, но home.hbs намного чище. Это становится очень полезным, когда у вас сложные веб-страницы.

Здесь мы просто включили header.hbs файл и приняли posts параметра в posts области posts.hbs файла.

Это то, что он передает posts из нашего обработчика в параметр posts posts.hbs страницы posts.hbs.

Создание специального помощника

Как вы можете видеть на странице, у нас есть один комментарий, занимающий две строки. Давайте создадим собственный помощник, чтобы резюмировать этот текст.

Для этого в конфигурации Handlebars мы можем определить наши вспомогательные функции. В нашем случае мы обрежем комментарии до 64 символов:

 app.engine('hbs', exphbs({ 
 defaultLayout: 'main', 
 extname: '.hbs', 
 helpers: { 
 getShortComment(comment) { 
 if (comment.length < 64) { 
 return comment; 
 } 
 
 return comment.substring(0, 61) + '...'; 
 } 
 } 
 })); 

Теперь давайте воспользуемся этим помощником в нашем posts.hbs для обобщения комментариев:

 <div class="posts"> 
 <div class="row justify-content-center"> 
 {{#each posts}} 
 <div class="col-lg-7" style="margin-top: 50px;"> 
 <div class="card"> 
 
 <img src="{{this.image}}" class="card-img-top" alt="..."> 
 <div class="card-body"> 
 <h5 class="card-title">Posted by {{this.author}}</h5> 
 
 {{#if this.comments}} 
 <ul class="list-group"> 
 {{#each this.comments}} 
 <li class="list-group-item">{{getShortComment this}}</li> 
 {{/each}} 
 </ul> 
 {{else}} 
 <ul class="list-group"> 
 <li class="list-group-item">Be first to comment on this post</li> 
 </ul> 
 {{/if}} 
 </div> 
 </div> 
 </div> 
 {{/each}} 
 </div> 
 </div> 

Конечно же, комментарии на нашей странице теперь обрезаны:

пользовательская вспомогательная функцияруля{.ezlazyload}

Заключение

В этой статье мы рассмотрели основы Handlebars - движка шаблонов для Node.js и интерфейсного JavaScript. Используя Handlebars, мы можем создавать динамические веб-страницы, которые отображаются на стороне сервера или клиента. Используя условные выражения, циклы, частичные и настраиваемые вспомогательные функции Handlebars, наши веб-страницы становятся чем-то большим, чем просто статический HTML.

Код также, как обычно, доступен на GitHub. Вы также можете найти дополнительную информацию о Handlebars на их официальной веб-странице .

comments powered by Disqus