Node.js — EJS樣板引擎的使用方式

Sean Yeh
Web Design Zone
Published in
13 min readFeb 27, 2020

早期(距今大約15~20年)在開發網頁時,並沒有明顯的分別前端與後端。當時的製作方式常常是把HTML、 CSS與JavaScript檔案放到PHP或者是ASP裡面(早期流行的語言)。透過PHP重新處理與組裝,然後在把資料傳給瀏覽器。這樣子的程式碼結構,看起來挺複雜的。比較像是肉醬麵。

後來,大家覺得這樣子的網站很不容易維護,因此有人就想,是否有辦法只要修改一個地方,就可以讓整個網頁類似的地方都一起發生變化?

於是就開發出樣板引擎(Template Engine)。透過樣板引擎,可以動態的產出HTML文件。例如:樣板引擎可以把網頁共通的部分(Header與Footer部分),做成Template樣板,並且與網站邏輯分開,也可以使用變數、條件、迴圈來撰寫更簡潔的程式碼。在檔案管理上採取放置不同資料夾分開管理的方式,讓程式碼的維護更加方便,讓網站的架構更加簡潔。

目前最常見的樣版引擎就屬JADE(PUG)與EJS。這裡我們要介紹的是EJS。

What is the “E” for? “Embedded?” Could be. How about “Effective,” “Elegant,” or just “Easy”? EJS is a simple templating language that lets you generate HTML markup with plain JavaScript. No religiousness about how to organize things. No reinvention of iteration and control-flow. It’s just plain JavaScript.

什麼是EJS?

就如EJS官網上面所說的。EJS是一個樣版引擎,它可以透過JavaScript幫助我們產出HTML標籤語法。

EJS透過簡潔的標籤插入頁面,具有快速編譯與繪製輸出的特色。EJS利用傳統的方式 <%=輸出變數%>來處理樣板,如有 Classic ASP/PHP/JSP 等經驗的話,推薦使用EJS,用起來會比較上手。在 Express 中可以使用EJS作為 view的樣板。

開始使用EJS

安裝套件

安裝ejs

$ npm install ejs --save

也可以再安裝 ejs-locals

$ npm install ejs-locals --save

安裝完畢後的 package.json如下:

{  "name": "express_exejs",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "echo \"Error: no test specified\" && exit 1"  },  "author": "",  "license": "ISC",  "dependencies": {  "ejs": "^3.0.1",  "ejs-locals": "^1.0.2",  "express": "^4.17.1"  }}

如果想要方便重新啟動伺服器,可以加裝nodemon套件進去。

導入套件

let express = require('express');  //載入express模組let app = express();  // 使用expressapp.get('/', function (req, res) {    res.send(`<p>Hello World</p>`)})let port = 3000;  //設定port位置app.listen(port);  // 監聽 port

在已經導入Express的情況下,我們再來把ejs-locals導入專案。

let engine = require('ejs-locals');

設定EJS引擎

設定ejs為樣版引擎以及設定讀取的資料夾為根目錄的views資料夾。並且在根目錄建立一個views的資料夾。

app.engine('ejs', engine);app.set('views', './views');app.set('view engine', 'ejs');

建立樣板

在views資料夾新增一個ejs檔案index.ejs,內容為簡單的html標籤。放置一個h1標籤,並且寫上文字Hello於h1中間。並存檔。

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>Document</title></head><body>  <h1>Hello</h1></body></html>

修改app.get

把 res.send(‘Hello world’) 改成 res.render(‘index’) 。也就是說,我們希望進入首頁時,呈現index這個畫面,而這裡的index代表的就是views資料夾裡面的index.ejs檔案。

app.get('/', function (req, res) {  res.render('index')})

啟動伺服器

樣版引擎使用前:res.send(‘Hello world’)

原本的畫面(上圖)就變成新的畫面(下圖)。

樣版引擎使用後:res.render(‘index’)

這樣子就完成了EJS引擎的基本使用。你可能會說,這樣看起來跟原來的方式似乎沒有什麼差別。

接下來我們要再進一步地利用這個引擎發揮使用EJS的效用。

量的增加(加頁面)

我們先增加幾個路由試試。

增加user路由

app.get('/user', function (req, res) {res.render('user');})

增加對應user路由的ejs檔案:user.ejs

<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><h1>User</h1></body></html>

測試網頁

輸入http://localhost:3000/user 後,頁面出現下面畫面,表示路由成功的增加了:

大家是不是覺得這樣子也沒有很厲害?我們繼續。

導入頁面

如果我們希望每個頁面的標題不一樣的話,除了去修改html頁面外,是否有別的方式?

<%=輸出變數%> 將變數導入頁面

其實,EJS引擎是可以夾帶變數的。我們可以在render()裡面夾帶變數進來。

將下面的變數放入render()裡面:

{'title': '首頁'}

變成:

app.get('/', function (req, res) {    res.render('index', {'title': '首頁'});})

在 index.ejs 裡面,尋找適當的地方加上<%= title %>。

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title><%= title %></title></head><body>    <h1><%= title %></h1></body></html>

結果,「首頁」就會顯現出來。

以上是傳送參數的方法。

<%-輸出變數%> 將HTML導入頁面

如果想要輸出HTML標籤給ejs檔使用的話,我們就不能用原來的<%=輸出變數%>格式輸出,必須調整一下輸出的方式。要完成輸出的話,需要將「=」等號改成「-」減號。例如下面我們增加一個變數titleH2,參數對應的值為包括HTML標籤的內容。

app.get('/', function (req, res) {    res.render('index', {        'title': '首頁',        'titleH2': '<h2>第二級標題</h2>'    });})

並且在ejs裡面插入<%- titleH2 %>的標籤。結果首頁顯示如下圖示:

可以看出,「第二級標題」等字樣被<h2>標籤包覆著。

依照條件判斷顯示結果

我們也可以使用if statement來進行條件判斷,讓樣板可以依照判斷的結果,顯示不同的內容。

if statement的寫法如下:

<% if(條件) { %>
//執行內容
<% } %>

我們拿剛剛的例子來試試,首先先加入一組變數 ‘show’: true。程式碼如下:

app.get('/', function (req, res) {     res.render('index', {         'title': '首頁',         'titleH2': '<h2>第二級標題</h2>',         'show': true     });})

接下來,在ejs檔案中,插入下面的判斷條件。下面的意思是,如果條件show為真(條件滿足)的話,就會顯示由<p>所包覆的那段文字。

<% if(show) { %>    <p>條件滿足</p><% } %>

我們也可以把條件寫複雜一點。

<% if(show) { %>    <p>條件滿足</p><% } else { %>   <p>條件不滿足</p><%} %>

這時候頁面呈現的結果應該還是跟上面的圖一樣。不過,如果我們把show改成 false的話,結果就會不一樣了。

陣列載入

如果我們想要顯示陣列的資料?假設我們有一組陣列foods裡面有[‘apple’, ‘banana’, ‘mongo’]。我們要如何顯示在頁面上?

app.get('/', function (req, res) {    res.render('index', {        'title': '首頁',        'titleH2': '<h2>第二級標題</h2>',        'show': false,        'foods': ['apple', 'banana', 'mongo']    });})

在ejs檔案我們可以這樣子寫:

<ul>    <% for(var i=0; i< foods.length; i++){ %>        <li><%-foods[i] %></li>    <% } %></ul>

其中,裡面所有的程式碼都被<% %>包覆在裡面。主要是一個for迴圈。這個迴圈與javascript裡面寫的迴圈是一樣的。唯一不同的地方是,迴圈裡面顯示資料的foods[i] 部分,如果沒有加上<%- %>的包覆,隨然會顯示出三筆資料,卻不會正確的顯示內容。

include layout

下圖是我們這次要嘗試的。我們透過EJS引擎把重複的網站元素抽離出來成為layout.ejs檔案。並且依照實際需要與個別頁面的元素組合成完整的完整的HTML檔案,並且顯示在瀏覽器上:

建立layout.ejs

首先,在views資料夾裡面(記得,ejs檔案都要放在這個資料夾裡面,伺服器才不會找不到。其理由是因為我們在前面設定了app.set(‘views’, ‘./views’); )建立一個檔案,命名為layout.ejs。

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title><%= title %></title></head><body>    <%- body %></body></html>

修改index.ejs與user.ejs

在index.ejs與user.ejs兩個檔案裡面。我們先把與layout.ejs重複的區塊刪除掉。也就是<body> 以外的部分。移除之後,還要建立與layout.ejs的聯繫關係。這樣子,伺服器才會知道需要把layout.ejs載入到index裡面。要達到這個效果,我們需要在index.ejs裡面加上一行<% layout(‘layout’) %> 。其中,括弧裡面的layoput就是指layout.ejs。如果你想要使用別的名稱也是可以的。

index.ejs

這是移除是<body> 以外的部分的結果。

<% layout('layout') %><h1><%= title %></h1><%- titleH2 %><% if(show) { %><p>條件滿足</p><% } else { %><p>條件不滿足</p><%} %><ul>    <% for(var i=0;i< foods.length;i++){ %>    <li><%-foods[i] %></li>    <%  } %></ul>

user.ejs

這是移除是<body> 以外的部分的結果。

<% layout('layout') %><h1>User</h1>

重新執行伺服器

由於我們在layout.ejs裡面加入<title><%= title %></title> 。在首頁的時候,就會顯示「首頁」,到了user頁面時,則會顯示「使用者」。

首頁index頁面

使用者user頁面

--

--

Sean Yeh
Web Design Zone

# Taipei, Internet Digital Advertising,透過寫作讓我們回想過去、理解現在並思考未來。並樂於分享,這才是最大贏家。