Nodejs后端开发(三)-模板引擎artTemplate
模板引擎的基础概念
- 模板引擎
- 模板引擎不是Node.js本身提供的,它是Node.js的第三方模块
- 让开发者以更加友好的方式拼接字符串,使项目代码更加清晰、更加易于维护。
- art-template模板引擎
- 该模板引擎由腾讯公司出品,目前市场运行最快的模板引擎
- 在命令行工具中使用
npm install art-template
命令进行下载 - 使用
const template = require('art-template')
引入模板引擎 - 渲染模板:告诉模板引擎要拼接的数据和模板在哪
const html = template(‘模板路径’, 数据);
- 使用模板语法告诉模板引擎,模板与数据应该如何进行拼接
- 通常在项目中新建一个views文件夹,用于存储所有的模板,模板的代码放在
.art
文件中
- art-template代码示例
-
模板引擎处理部分app.js文件
// 导入模板引擎模块 const template = require('art-template'); // 将特定模板与特定数据进行拼接 const html = template('./views/index.art',{ data: { name: '张三', age: 20 } });
-
模板部分index.art文件
<div> <span>{*data.name}}</span> <span>{*data.age}}</span> </div>
-
模板引擎的语法
- 模板语法
- art-template同时支持两种模板语法:标准语法和原始语法。
- 标准语法可以让模板更容易读写,原始语法具有强大的逻辑处理能力。
- 标准语法:
{* 数据 }}
- 原始语法:
<%=数据 %>
- 标准语法:
- 输出
- 将某项数据输出在模板中,标准语法和原始语法如下:
- 标准语法:
{* 数据 }}
- 原始语法:
<%=数据 %>
- 标准语法:
-
代码举例:
<!-- 标准语法 --> <h2>{*value}}</h2> <h2>{*a ? b : c}}</h2> <h2>{*a + b}}</h2> <!-- 原始语法 --> <h2><%= value %></h2> <h2><%= a ? b : c %></h2 <h2><%= a + b %></h2>
- 将某项数据输出在模板中,标准语法和原始语法如下:
- 原文输出
- 如果数据中携带HTML标签,默认模板引擎不会解析标签,会将其转义后输出。
- 标准语法:
{*@ 数据 }}
- 原始语法:
<%-数据 %>
- 标准语法:
-
代码举例:
<!-- 标准语法 --> <h2>{*@ value }}</h2> <!-- 原始语法 --> <h2><%- value %></h2>
- 如果数据中携带HTML标签,默认模板引擎不会解析标签,会将其转义后输出。
-
条件判断
<!-- 标准语法 --> {*if 条件}} ... {*/if}} {*if v1}} ... {*else if v2}} ... {*/if}} <!-- 原始语法 --> <% if (value) { %> ... <% } %> <% if (v1) { %> ... <% } else if (v2) { %> ... <% } %>
- 循环
- 标准语法:
{*each 数据}} {*/each}}
- 原始语法:
<% for() { %> <% } %>
<!-- 标准语法 --> {*each target}} {*$index}} {*$value}} {*/each}} <!-- 原始语法 --> <% for(var i = 0; i < target.length; i++){ %> <%= i %> <%= target[i] %> <% } %>
- 标准语法:
- 子模版
- 使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。
- 标准语法:
{*include '模板'}}
- 原始语法:
<%include('模板') %>
<!-- 标准语法 --> {*include './header.art'}} <!-- 原始语法 --> <% include('./header.art') %>
- 标准语法:
-
代码举例:
{* include './common/header.art' }} <% include('./common/header.art') %> <div> {* msg }} </div> {* include './common/footer.art' }} <% include('./common/footer.art') %>
- 使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。
- 模板继承
- 以上讲过可以将网站页面中的公共部分抽取到子模板中
- html骨架(html、head、body标签)也属于每个页面的公共部分,但是这部分不能抽取到子模板中
- 使用模板继承可以将网站HTML骨架抽离到单独的文件中,其他页面模板可以继承骨架文件。
- 骨架文件中是可以设置一些坑位的,比如title、css、js等坑位,用于继承者填充
- 模板继承示例
-
layout.art骨架模板
<!doctype html> <html> <head> <meta charset="utf-8"> <title>HTML骨架模板</title> <!--设置坑位--> {*block 'head'}}{*/block}} </head> <body> <!--设置坑位--> {*block 'content'}}{*/block}} </body> </html>
-
index.art继承模板
<!--index.art 首页模板--> <!--继承./layout.art--> {*extend './layout.art'}} <!--填坑--> {*block 'head'}} <link rel="stylesheet" href="custom.css"> {*/block}} {*block 'content'}} <p>This is just an awesome page.</p> {*/block}}
-
app.js中引用index.art模板
-
- 模板配置
- 向模板中导入变量
template.defaults.imports.变量名 = 变量值;
- 可以在模板文件中直接调用外面的方法函数
- 设置模板根目录
template.defaults.root = 模板目录
- 渲染模板时就不需要再传入路径了,直接传入模板文件名称即可:
template('文件名.后缀名', {})
- 渲染模板时就不需要再传入路径了,直接传入模板文件名称即可:
- 设置模板默认后缀
template.defaults.extname = '.art'
- 渲染模板时传入的文件名称可以不带后缀名了:
template('文件名', {})
- 渲染模板时传入的文件名称可以不带后缀名了:
- 代码举例:
-
app.js文件
const template = require('art-template'); const path = require('path'); // 时间格式化第三方模块,在npmjs这个网站上,可以通过npm install dateformat下载 const dateFormat = require('dateformat'); // 1. 设置模板的根目录,那么渲染模板时就自动到这个模板下找对应的模板文件 template.defaults.root = path.join(__dirname, 'views'); // 2. 导入模板变量,将这个时间格式化第三方模块导入到模板中,在模板中使用 template.defaults.imports.dateFormat = dateFormat; // 3. 配置模板的默认后缀 // template.defaults.extname = '.art'; // 模板也可以写在html文件中,那么下面没有后缀名的模板,就会寻找后缀名为.html的模板了 template.defaults.extname = '.html'; // 4. 渲染模板 // 因为上面设置了模板根目录,所以这里直接写要渲染的模板名称即可,不用再写路径了 const html = template('06.art', { time: new Date() }); // 4. 渲染模板 // 因为上面设置了模板后缀名,所以这里不需要加后缀名了,这里寻找到的是07.html的模板文件 console.log(template('07', {})); console.log(html);
-
06.art模板
<!--模板文件中使用外部导入的对象的函数功能--> {* dateFormat(time, 'yyyy-mm-dd')}}
-
- 向模板中导入变量
模板使用总结
- js文件中
- 下载模板组件
- 导入模板组件
- 渲染模板
- art文件
- 编写模板内容
案例
-
案例介绍 – 学生档案管理,效果图如下:
- 目标: 模板引擎应用,强化node.js项目制作流程。
- 知识点:http请求响应、数据库、模板引擎、静态资源访问。
服务端制作流程
- 建立项目文件夹并生成项目描述文件
- 新建项目文件夹Students
- 打开命令行进入Students文件夹输入:
npm init -y
,生成项目描述文件packge.json文件
- 创建网站服务器-实现客户端和服务器端通信
-
项目根目录下常见app.js用于编写服务端代码
// 引入http模块 const http =require('http'); // 创建网站服务器 const app = http.createServer(); // 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ //向客户端相应内容 res.end('OK') }); // 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!'); // 以上打开浏览器,地址输入:localhost,就可以看到'OK‘字样,说明客户端与浏览器通信成功
-
- 连接数据库并根据需求设计学员信息表
- 通过命令
npm install mongoose
下载MongoDB第三方模块 - 根目录下新建model文件夹,在里面新建一个connect.js文件,内部封装数据库连接代码
- app.js中导入该代码
require('./model/connect')
- 在model文件夹下,新建user.js用于创建学生这个集合信息,然后导出
- 在app.js中导入该集合:
const Student = require('./model/user')
- 代码部分:
-
connect.js代码
// 导入mongoose const mongoose = require('mongoose'); // 连接数据库 mongoose.connect('mongodb://localhost/playground',{useNewUrlParser: true,useUnifiedTopology: true}) .then(()=>console.log('数据库连接成功!')) .catch(()=>console.log('数据库连接失败!'))
-
user.js代码:
const mongoose = require('mongoose'); // 创建学生集合规则 const studentsSchema = new mongoose.Schema({ name: { type: String, required: true, minlength: 2, maxlength: 10 }, age: { type: Number, min: 10, max: 25 }, sex: { type: String }, email: String, hobbies: [ String ], collage: String, enterDate: { type: Date, default: Date.now } }); // 创建学生信息集合 const Student = mongoose.model('Student', studentsSchema); // 将学生信息集合进行导出 module.exports = Student;
-
- 通过命令
- 创建路由并实现页面模板呈递
- 下载router:
npm install router
- app.js引入模块:
const getRouter = require('router')
- app.js中编写路由代码
- 下面模板引擎:
npm install art-template
- 项目根目录下新建views文件夹,在该文件夹下创建模板文件:index.art/list.art
-
项目根目录下新建public文件夹,在该文件夹下存放静态文件比如css文件(新建一个css文件夹,内部存放css文件:list.css/main.css)
// 引入http模块 const http =require('http'); // 引入router模块 const getRouter = require('router'); // 引入模板引擎 const template = require('art-template'); // 引入path模块 const path = require('path'); const router = getRouter(); // 配置模板的根目录 template.defaults.root = path.join(__dirname,'views'); // 根据客户端访问的路径不同设置不同的代码逻辑 router.get('/add',(req,res)=>{ let html = template('index.art',{}) res.end(html); }); // router.get('/test',(req,res)=>{ // res.end('test'); // }); // router.get('/index',(req,res)=>{ // res.end('index'); // }) // 连接数据库 require('./model/connect') // 导入Student集合 const Student = require('./model/user') // 创建网站服务器 const app = http.createServer(); // 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ //向客户端相应内容 // res.end('OK') // 启动路由 router(req,res,()=>{ console.log('请求完毕了'); }); }); // 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!');
- 下载router:
- 实现静态资源访问
- 上面的模板文件index.art/main.art本质就是html文件修改后缀名为art文件,那么这里面分别引用了index.css/main.css静态文件,那么也需要处理
- 实现学生信息添加功能
- 实现学生信息展示功能
第三方模块 router
- 功能:实现路由
- 使用步骤:
- 下载第三方模块:
npm install router
- 引入模块:
const getRouter = require('router')
- 获取路由对象
- 调用路由对象提供的方法创建路由
- 启用路由,使路由生效
const getRouter = require('router') //获取路由对象 const router = getRouter(); //接收get请求 router.get('/add', (req, res) => { res.end('Hello World!') }) //启用路由 server.on('request', (req, res) => { router(req, res) })
- 下载第三方模块:
- 代码举例:app.js
// 引入http模块 const http =require('http'); // 引入router模块 const getRouter = require('router') const router = getRouter(); // 根据客户端访问的路径不同设置不同的代码逻辑 router.get('/test',(req,res)=>{ res.end('test'); }); router.get('/index',(req,res)=>{ res.end('index'); }) // 创建网站服务器 const app = http.createServer(); // 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ //向客户端相应内容 // res.end('OK') // 启动路由 router(req,res,()=>{ console.log('请求完毕了'); }); }); // 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!');
第三方模块 serve-static
- 功能:实现静态资源访问服务
- 步骤:
- 下载:
npm install serve-static
- 引入serve-static模块获取创建静态资源服务功能的方法
- 调用方法创建静态资源服务并指定静态资源服务目录
- 启用静态资源服务功能
//引入serve-static模块 const serveStatic = require('serve-static') //调用方法创建静态资源服务并指定静态资源服务目录 const serve = serveStatic('public') //启用静态资源服务功能-监听静态资源的访问请求 server.on('request', () => { serve(req, res) }) server.listen(3000)
- 下载:
-
代码举例:
// 引入http模块 const http =require('http'); // 引入router模块 const getRouter = require('router'); // 引入模板引擎 const template = require('art-template'); // 引入path模块 const path = require('path'); // 引入静态资源访问模块 const serveStatic = require('serve-static'); // 实现静态资源访问服务 const serve = serveStatic(path.join(__dirname, 'public')) const router = getRouter(); // 配置模板的根目录 template.defaults.root = path.join(__dirname,'views'); // 根据客户端访问的路径不同设置不同的代码逻辑 //创建路由并实现页面模板呈递 router.get('/add',(req,res)=>{ let html = template('index.art',{}) //相应客户端 res.end(html); }); // 创建网站服务器 const app = http.createServer(); // 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ // 启动路由 router(req,res,()=>{ console.log('请求完毕了'); }); // 启用静态资源访问服务功能 serve(req, res, () => {}) }); // 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!');
- 这样路由相应的模板就会自动引用public文件夹中的静态文件(css)了
添加学生信息功能步骤分析
- 在模板的表单中指定请求地址与请求方式
- 为每一个表单项添加name属性
- 添加实现学生信息功能路由
- 接收客户端传递过来的学生信息
- 将学生信息添加到数据库中
- 将页面重定向到学生信息列表页面
学生信息列表页面分析
- 从数据库中将所有的学生信息查询出来
- 通过模板引擎将学生信息和HTML模板进行拼接
- 将拼接好的HTML模板响应给客户端
整体代码
-
app.js代码:
// 引入http模块,用于创建服务 const http =require('http'); // 引入router模块,用于设置路由 const getRouter = require('router'); // 引入模板引擎art-template const template = require('art-template'); // 引入path模块,用于拼接路径 const path = require('path'); // 引入系统模块,将字符串转对象,不需要下载 const querystring = require('querystring'); // 引入静态资源访问模块,用于访问项目中静态资源 const serveStatic = require('serve-static'); // 引入第三方模块dateformat,用于处理时间 const dateformat = require('dateformat'); /******服务器创建,并监听客户端请求*********/ // a-1 创建网站服务器 const app = http.createServer(); // a-2 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ // b-3 启动路由 router(req,res,()=>{ console.log('请求完毕了'); }); //c-2 启用静态资源访问服务功能 serve(req, res, () => {}) }); // a-3 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!'); /******数据库连接部分*********/ // c-1 创建并连接数据库 require('./model/connect') // c-2导入Student集合:创建集合Student const Student = require('./model/user'); /******模板部分*********/ // 配置模板的根目录 template.defaults.root = path.join(__dirname,'views'); // 将该对象导入到模板中使用 template.defaults.imports.dateformat = dateformat; /******静态资源部分*********/ // c-1 实现静态资源访问服务 const serve = serveStatic(path.join(__dirname, 'public')); /******创建路由部分,用于监听客户端请求的路径*********/ // b-1 获取路由对象 const router = getRouter(); // b-2 组建路由 //根据客户端访问的路径不同设置不同的代码逻辑 //创建路由并实现页面模板呈递 // student添加信息页面get请求 router.get('/add',(req,res)=>{ //渲染模板:将模板页面展示 let html = template('index.art',{}) res.end(html); }); //student列表页面get请求 router.get('/list',async (req,res)=>{ //1. 从数据库中将所有的学生信息查询出来 let sutdents = await Student.find(); //2. 通过模板引擎将学生信息和HTML模板进行拼接 // 渲染模板:学生列表页面展示 let html = template('list.art',{students:sutdents}) //3. 将拼接好的HTML模板响应给客户端 res.end(html); }); //student信息页面点击添加post请求 // 1. 添加实现学生信息功能路由 router.post('/add', (req, res) => { //2. 接收客户端传递过来的学生信息 // 接收post请求参数 let formData = ''; req.on('data', param => { formData += param; }); req.on('end', async () => { // 3. 将学生信息添加到数据库中 await Student.create(querystring.parse(formData)) //4. 将页面重定向到学生信息列表页面 res.writeHead(301, { Location: '/list' }); res.end() }) });
- model文件夹下的user.js/connect.js代码(略)
- public文件夹下的css文件内容(main.css/list.css)(略)
- views文件夹下
-
index.art
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> <title>学生档案</title> <link rel="stylesheet" href="./css/main.css"> </head> <body> <form action="/add" method="post"> <fieldset> <legend>学生档案</legend> <label> 姓名: <input class="normal" type="text" autofocus placeholder="请输入姓名" name="name"> </label> <label> 年龄: <input class="normal" type="text" placeholder="请输入年龄" name="age"> </label> <label> 性别: <input type="radio" value="0" name="sex"> 男 <input type="radio" value="1" name="sex"> 女 </label> <label> 邮箱地址: <input class="normal" type="text" placeholder="请输入邮箱地址" name="email"> </label> <label> 爱好: <input type="checkbox" value="敲代码" name="hobbies"> 敲代码 <input type="checkbox" value="打篮球" name="hobbies"> 打篮球 <input type="checkbox" value="睡觉" name="hobbies"> 睡觉 </label> <label> 所属学院: <select class="normal" name="collage"> <option value="前端与移动开发">前端与移动开发</option> <option value="PHP">PHP</option> <option value="JAVA">JAVA</option> <option value="Android">Android</option> <option value="IOS">IOS</option> <option value="UI设计">UI设计</option> <option value="C++">C++</option> </select> </label> <label> 入学日期: <input type="date" class="normal" name="enterDate"> </label> <label class="btn"> <input type="submit" value="提交" class="normal"> </label> </fieldset> </form> </body> </html>
-
list.art
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>学员信息</title> <link rel="stylesheet" href="./css/list.css"> </head> <body> <table> <caption>学员信息</caption> <tr> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>邮箱地址</th> <th>爱好</th> <th>所属学院</th> <th>入学时间</th> </tr> {*each students}} <tr> <th>{*$value.name}}</th> <th>{*$value.age}}</th> <th>{*$value.sex ==0 ? "男":"女"}}</th> <th>{*$value.email}}</th> <th> {*each $value.hobbies}} <span>{*$value}}</span> {*/each}} </th> <th>{*$value.collage}}</th> <th>{*dateformat($value.enterDate, 'yyyy-mm-dd')}}</th> </tr> {*/each}} </table> </body> </html>
-
app.js代码优化
- 将路由抽取到一个单独文件中去
-
项目根目录下新建一个route文件夹,内部创建index.js
// 引入router模块,用于设置路由 const getRouter = require('router'); // 引入模板引擎art-template const template = require('art-template'); // 引入系统模块,将字符串转对象,不需要下载 const querystring = require('querystring'); // 导入Student集合:创建集合Student const Student = require('../model/user'); /******创建路由部分,用于监听客户端请求的路径*********/ // b-1 获取路由对象 const router = getRouter(); // b-2 组建路由 //根据客户端访问的路径不同设置不同的代码逻辑 //创建路由并实现页面模板呈递 // student添加信息页面get请求 router.get('/add',(req,res)=>{ //渲染模板:将模板页面展示 let html = template('index.art',{}) res.end(html); }); //student列表页面get请求 router.get('/list',async (req,res)=>{ //1. 从数据库中将所有的学生信息查询出来 let sutdents = await Student.find(); //2. 通过模板引擎将学生信息和HTML模板进行拼接 // 渲染模板:学生列表页面展示 let html = template('list.art',{students:sutdents}) //3. 将拼接好的HTML模板响应给客户端 res.end(html); }); //student信息页面点击添加post请求 // 1. 添加实现学生信息功能路由 router.post('/add', (req, res) => { //2. 接收客户端传递过来的学生信息 // 接收post请求参数 let formData = ''; req.on('data', param => { formData += param; }); req.on('end', async () => { // 3. 将学生信息添加到数据库中 await Student.create(querystring.parse(formData)) //4. 将页面重定向到学生信息列表页面 res.writeHead(301, { Location: '/list' }); res.end() }) }); module.exports = router;
-
app.js中代码
// 引入http模块,用于创建服务 const http =require('http'); // 引入模板引擎art-template const template = require('art-template'); // 引入path模块,用于拼接路径 const path = require('path'); // 引入静态资源访问模块,用于访问项目中静态资源 const serveStatic = require('serve-static'); // 引入第三方模块dateformat,用于处理时间 const dateformat = require('dateformat'); /******服务器创建,并监听客户端请求*********/ // a-1 创建网站服务器 const app = http.createServer(); // a-2 当客户端请求服务端的时候处理代码 app.on('request',(req,res)=>{ // b-3 启动路由 router(req,res,()=>{ console.log('请求完毕了'); }); //c-2 启用静态资源访问服务功能 serve(req, res, () => {}) }); // a-3 监听客户端访问端口 app.listen(80); console.log('服务器启动成功!'); /******数据库连接部分*********/ // c-1 创建并连接数据库 require('./model/connect'); /******模板部分*********/ // 配置模板的根目录 template.defaults.root = path.join(__dirname,'views'); // 将该对象导入到模板中使用 template.defaults.imports.dateformat = dateformat; /******静态资源部分*********/ // c-1 实现静态资源访问服务 const serve = serveStatic(path.join(__dirname, 'public')); // 导入路由对象 const router = require('./route/index');
项目目录结构
model: 存放数据库代码
public: 存放静态资源文件
views: 存放模板文件
app.js: 项目入口文件