07 ejs 模板引擎
模板引擎在前後端分離之前是最web開發必不可少的一個組件,隨着電腦手機設備性能的提高,瀏覽器也能做更多的事情了,前後端分離逐漸流行,模板引擎也逐漸沒落,不過在某些特需領域或者一些小項目中模板引擎仍然再用。
背景
ejs是後臺模板引擎,在web剛剛發展起來時,一般網頁開發web後端是主力,前端人員僅僅是寫頁面,頁面完成之後交於後端人員,不負責聯調。
後端的程序一般爲MVC
架構,MVC
是指模型層、視圖層、控制器層(後期加入了service層)。其中模型層與數據庫直接交互,爲控制器層提供數據,控制器層爲瀏覽器提供接口,service層負責業務邏輯,視圖層將控制層拿到的數據渲染到前端頁面。以用戶訪問瀏覽器爲例,其運行流程是:
拿數據流程:接口訪問 -> Control層 -> Service層 -> Model層
Control層從modal層拿到數據後 -> 渲染到View層。
View層的具體作用就是之前寫好的假數據替換成從數據庫拿到的真是數據,完成這一操作的就是模板引擎。
概念
ejs是nodejs環境下的模板引擎,它是一個第三方模塊,可以通過npm來安裝
npm install ejs --save
使用
模板引擎就是在html中使用特定的語法實現循環、表達式賦值等操作,ejs的語法比較簡單,類似於jsp,常用標籤如下
常用標籤
- <% %> 流程控制標籤,可以在標籤內部寫js的表達式和邏輯判斷以及循環等
- <%= %> 輸出標籤(原文輸出HTML標籤,裏面是什麼輸出就是什麼)
- <%- %> 輸出標籤(HTML會被瀏覽器解析,類似於瀏覽器原生解析內容)
eg:
<img src = "<%= imgUrl" %>" /> 其中imgUrl是nodejs變量,可能是從數據庫中拿到的數據
//再如流程控制
<div>
<% if(imgUrl !=null){%>
<img src = "<%= imgUrl" %>" />
<%} else { %>
<span>圖片地址無效,請檢查程序</span>
<% } %>
</div>
例子
1.模擬從數據庫讀取數據並渲染到html的例程:
//node.js程序
const http = require("http");
const fs = require("fs");
const ejs = require('ejs');
const url = require('url');
let app = http.createServer((req, res) => {
let pathname = url.parse(req.url).pathname;
console.log("您訪問的pathname是:", pathname);
let data = "我是從數據庫拿到的數據,哈哈哈";
let list = [
"我是張三",
"我是李四",
"我是王五",
"我們都是假裝從數據庫拿到的數據"
];
//把數據庫的數據渲染到模板上面
ejs.renderFile('view/test.ejs', { //注意:這個讀取函數是異步的!
msg: data,
list: list
}, function (err, data) {
res.end(data);
})
})
app.listen(9999, "127.0.0.1");
console.log("ejs測試程序監聽在 127.0.0.1:9999");
//ejs程序
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登錄</title>
</head>
<body>
<h1>這是一個ejs後臺模板引擎測試程序</h1>
<!-- ejs代碼 -->
<h2>
<!-- 這裏的msg是變量,是由nodejs傳遞進來的 -->
<%=msg%>
</h2>
<br>
<hr>
<ul>
<p>
這裏是一個列表渲染
</p>
<% for(var i = 0; i< list.length; i++){ %>
<!-- 變量輸出 -->
<li> <%= list[i] %> </li>
<% } %>
</ul>
</body>
</html>
執行結果:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3wLLMMJO-1576235754255)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20191212185205224.png)]
整個程序分爲兩個部分,一個是ejs文件,另外一個是nodejs文件,當瀏覽器訪問服務器時,nodejs程序將數據庫數據讀出來,(在這裏是通過變量來模擬的)然後渲染到ejs文件中,ejs其實就是一個html文件,只不過裏面寫了ejs的一些標籤,用於渲染從nodejs傳來的變量。
2.ejs結合路由來獲取get和post傳值
//nodejs 程序
const http = require("http");
const url = require("url");
const ejs = require("ejs");
const fs = require("fs");
//字符串鍵值分離函數,輸入字符串 "a=1&b=2",返回{a=1,b=2}
function devideKeyAndVal(str) {
let arr = str.split("&");//分離post數據
let obj = {};
for (let i = 0; i < arr.length; i++) {
obj[arr[i].split("=")[0]] = arr[i].split("=")[1];
}
return obj;
}
let app = http.createServer((req, res) => {
let pathname = url.parse(req.url).pathname;
let method = req.method.toLowerCase();//獲取請求方式
console.log("請求的pathname是:", pathname);
console.log("請求的method是:", method);
switch (pathname) {
case "/":
res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" });
res.write("請訪問 http://127.0.0.1:9999/login 進行post傳值測試,或者 http://127.0.0.1:9999/register 進行get測試");
res.end();
break;
case "/register":
//返回register 頁面
ejs.renderFile("view/register.ejs", function (err, data) {
if (err) {
console.log("err is: ", err);
} else {
res.end(data);
}
})
break;
case "/doRegister":
//接受get方式傳來的值
let getData = url.parse(req.url, true).query;
console.log("獲取 到的 getData是:", getData);
//利用fs模塊將註冊的用戶名和密碼寫入到文本文件中
fs.writeFile("./userdata.text", JSON.stringify(getData), (err) => {
if (err) {
console.log("寫入註冊信息失敗,錯誤信息是:err,", err);
} else {
console.log("註冊信息寫入成功");
ejs.renderFile("./view/result.ejs", {
operate: "註冊",
result: "成功",
}, (err, data) => {
if (err) {
console.log("讀取ejs模板文件出錯,err:", err);
} else {
res.end(data);
}
})
}
});
break;
case "/login":
//如果是get請求,則返回登錄頁面
if (method == "get") {
ejs.renderFile("view/login.ejs", function (err, data) {
if (err) {
log
console.log("err is :", err);
} else {
// console.log(data);
res.end(data);//將渲染完的數據返回給瀏覽器
}
})
} else if (method == "post") {
//如果是post請求則接收用戶名和密碼,判斷是否正確並返回頁面
//開始接受post數據
let postData = "";
req.on("data", function (dataChunk) {//數據監聽函數
postData += dataChunk;
})
req.on("end", function () {//數據結束傳輸監聽函數
let loginFlag = false;
console.log("接受完畢的原始post數據是:", postData);
let userData = devideKeyAndVal(postData);
console.log("解析後的postdata是:", userData);
if ((userData.username == "admin") && (userData.password == "123456")) {
console.log("用戶名和密碼正確");
loginFlag = true;
//寫入登錄信息
let time = new Date().getTime();//獲取時間戳
fs.writeFile(".login.log", `用戶${userData.username} 在${time} 登錄系統成功`, (err) => {
if (err) {
console.log("寫入登錄日誌出錯,錯誤原因是:", err);
} else {
console.log("登錄日誌寫入成功");
}
})
} else {
console.log("用戶名和密碼錯誤");
}
ejs.renderFile("view/result.ejs", {
operate: "登錄",
result: loginFlag
}, function (err, data) {
if (err) {
console.log("ejs模板引擎解析失敗,err:", err);
} else {
res.end(data);//將結果返回
}
})
})
}
break;
default: break;
}
})
app.listen(9999, "127.0.0.1");
console.log("ejs和get/post接收數據的綜合聯繫程序運行在 127.0.0.1:9999")
//login.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>登錄頁面</title>
</head>
<body>
<h1>請輸入用戶名和密碼登錄</h1>
<form action="/login" method="post">
用戶名<input type="text" name="username" />
密碼 <input type="password" name="password" />
<input type="submit" value="登錄" />
</form>
</body>
</html>
//result.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<!-- result是從nodejs傳遞過來的數據 -->
<h1><%= operate %> 結果是<%= result %> </h1>
</body>
</html>
nodejs服務器程序在接受到瀏覽器請求後,判斷請求路徑,也就是路由,如果是register
則返回註冊頁面,手動填寫用戶名和密碼後利用get方式提交到doRegister
路由,nodejs程序接收到get數據後寫入到userdata.txt
文件中(用來模擬數據庫),同事提示註冊成功。
當接受到login
url請求時,也就是路由匹配到/login
時,返回登錄的ejs模板,然後寫入用戶名和密碼,利用post方式將用戶名和密碼發送到nodejs後臺程序,後臺校驗用戶名是admin密碼是123456則將登錄信息保存到login.log文件中,同時返回頁面提示登錄成功,否則提示登錄失敗。
運行結果