JavaScript >> Javascript 文檔 >  >> JavaScript

Handlebars 指南:Node/JavaScript 的模板引擎

簡介

在本文中,我們將了解如何將 Handlebars 模板引擎與 Node.js 和 Express 一起使用。我們將介紹什麼是模板引擎以及如何使用 Handlebars 創建服務器端渲染 (SSR) 網絡應用程序。

我們還將討論如何使用 Express.js 框架配置 Handlebars,以及如何使用內置幫助程序來創建動態頁面。最後,我們將看看如何在需要時開發自定義助手。

什麼是模板引擎?

早在 90 年代,當互聯網被引入世界時,它主要用於科學目的,例如發表研究論文以及作為大學和科學家之間的溝通渠道。當時的大多數網頁都是靜態的。靜態網頁對於每個用戶都是相同的,並且不會因每個用戶而改變。如果要在頁面上更改任何內容,則必須手動完成。

在現代世界中,事物的交互性更強,並且為每個用戶量身定制。今天,幾乎每個人都可以訪問互聯網。今天的大多數網絡應用程序都是動態的。例如,在 Facebook 上,您和我在登錄時會看到非常不同的新聞提要。對於每個人,頁面將遵循相同的模板(即,上面有用戶名的連續帖子),但內容會有所不同。

這是模板引擎的工作——定義新聞提要的模板,然後根據當前用戶和對數據庫的查詢,用接收到的內容填充模板。

我們可以在後端和前端使用模板引擎。如果我們在後端使用模板引擎來生成 HTML,我們稱之為 Server-Side Rendering (SSR)。

車把

Handlebars 在後端和前端模板中都很流行。例如,流行的前端框架 Ember 使用 Handlebars 作為模板引擎。

Handlebars 是 Mustache 模板語言的擴展,主要側重於簡單性和最小化模板。

在 Node.js 中使用 Handlebars

首先,創建一個空文件夾,在該文件夾中打開命令提示符,然後運行 npm init -y 使用默認設置創建一個空的 Node.js 項目。

在開始之前,我們需要安裝所需的 Node.js 庫。你可以通過運行來安裝 express 和 express-handlebars 模塊:

$ npm install --save express express-handlebars

注意 :當在服務器端使用 Handlebars 時,你可能會使用像 express-handlebars 這樣的輔助模塊 將 Handlebars 與您的 Web 框架集成在一起。在本文中,我們將主要關注模板語法,這就是我們使用 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 標記,因為它更短。

讓我們在 main.hbs 中包含 Bootstrap 腳本和样式 佈局:

<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

讓我們通過瀏覽器訪問我們的應用:

一切就緒後,讓我們探索一些 Handlebars 功能。

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 帶有一些硬編碼的佔位符值。

我們的頁面現在看起來像這樣:

免費電子書:Git Essentials

查看我們的 Git 學習實踐指南,其中包含最佳實踐、行業認可的標準以及隨附的備忘單。停止谷歌搜索 Git 命令並真正學習 它!

向模板傳遞參數

現在,讓我們從頁面本身中刪除這些硬編碼值並將它們從腳本傳遞到頁面。這些稍後將替換為數組中的註釋值:

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

post 包含 author 等字段 , image , 和 comments .我們可以參考post 在我們的車把模板 {{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>

現在,由於評論數組為空,您應該只會在頁面上看到“成為第一個評論此帖子的人”部分:

#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 引用當前迭代中的元素。在我們的例子中,它指的是一個隨後被渲染的字符串:

如果您有一個對像數組,您也可以訪問該對象的任何屬性。例如,如果有一組人,您可以簡單地使用 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 , 正文 , 和 頁腳 部分。由於頁眉和頁腳通常在許多頁面之間共享,因此在 all 網頁很快就會變得非常煩人,簡直是多餘的。

幸運的是,我們可以使用 Handlebars 在模板中劃分這些部分,並且只需 include 這些模板作為頁面本身的“部分”。

在我們的例子中,由於我們沒有頁腳,讓我們創建一個 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 中的參數 頁面文件。

構建自定義助手

正如您在頁面上看到的,我們有一個使用兩行的註釋。讓我們創建一個自定義助手來總結該文本。

為此,在 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>

果然,現在我們頁面上的評論被剪輯了:

結論

在本文中,我們介紹了 Handlebars 的基礎知識——Node.js 和前端 JavaScript 的模板引擎。使用 Handlebars,我們可以創建在服務器端或客戶端呈現的動態網頁。使用 Handlebars 的條件、循環、部分和自定義幫助函數,我們的網頁不僅僅是靜態 HTML。

與往常一樣,該代碼也可以在 GitHub 上找到。您還可以在其官方網頁上找到有關 Handlebars 的更多信息。


Tutorial JavaScript 教程
  1. 防止 NPM 在 Docker 容器之外安裝包

  2. 製作我自己的 ForEach() javascript – 未定義元素

  3. TIL:如何使用 GraphQL 變量來保證我的查詢類型安全

  4. 作為初學者學習的最佳編程語言

  5. 標記表情符號的國家代碼

  6. 使用 HTML 和 JavaScript 驗證和格式化字段和數據

  7. Electron Adventures:第 54 集:使用 useImmer 進行筆記本狀態管理

  1. Learning React - 使用組件和虛擬數據構建用戶界面

  2. Photoswipe 只打開第一張或第二張圖片

  3. 控制台.timeLog

  4. 5 分鐘內反應鉤子

  5. 使用自定義鉤子將計算值添加到 Redux

  6. 如何在沒有 bootstrap-vue 的情況下在 vue.js 2 中使用 bootstrap 4?

  7. 從兩個選擇元素中獲取值並在另一個函數中進行比較

  1. JavaScript 中的記憶

  2. 如何使用動態重定向來改進您的二維碼活動

  3. 將 Google 日曆添加到您的 JAMStack

  4. 使用 Vue.js 和 Strapi 註冊和登錄(身份驗證)