Node.js — EJS樣板引擎的使用方式
早期(距今大約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頁面時,則會顯示「使用者」。