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文件中,同时返回页面提示登录成功,否则提示登录失败。
运行结果