Nodejs后端开发(三)-模板引擎artTemplate

模板引擎的基础概念

  1. 模板引擎
    1. 模板引擎不是Node.js本身提供的,它是Node.js的第三方模块
    2. 让开发者以更加友好的方式拼接字符串,使项目代码更加清晰、更加易于维护。
  2. art-template模板引擎
    1. 该模板引擎由腾讯公司出品,目前市场运行最快的模板引擎
    2. 在命令行工具中使用npm install art-template命令进行下载
    3. 使用const template = require('art-template')引入模板引擎
    4. 渲染模板:告诉模板引擎要拼接的数据和模板在哪 const html = template(‘模板路径’, 数据);
    5. 使用模板语法告诉模板引擎,模板与数据应该如何进行拼接
    6. 通常在项目中新建一个views文件夹,用于存储所有的模板,模板的代码放在.art文件中
  3. art-template代码示例
    1. 模板引擎处理部分app.js文件

       // 导入模板引擎模块
       const template = require('art-template');
       // 将特定模板与特定数据进行拼接
       const html = template('./views/index.art',{
           data: { 
               name: '张三', 
               age: 20 
           } 
       });
      
    2. 模板部分index.art文件

       <div> 
           <span>{*data.name}}</span> 
           <span>{*data.age}}</span> 
       </div>
      

模板引擎的语法

  1. 模板语法
    1. art-template同时支持两种模板语法:标准语法和原始语法
    2. 标准语法可以让模板更容易读写,原始语法具有强大的逻辑处理能力。
      1. 标准语法: {* 数据 }}
      2. 原始语法:<%=数据 %>
  2. 输出
    1. 将某项数据输出在模板中,标准语法和原始语法如下:
      1. 标准语法:{* 数据 }}
      2. 原始语法:<%=数据 %>
    2. 代码举例:

       <!-- 标准语法 --> 
       <h2>{*value}}</h2> 
       <h2>{*a ? b : c}}</h2> 
       <h2>{*a + b}}</h2>
      
       <!-- 原始语法 --> 
       <h2><%= value %></h2> 
       <h2><%= a ? b : c %></h2
       <h2><%= a + b %></h2>
      
  3. 原文输出
    1. 如果数据中携带HTML标签,默认模板引擎不会解析标签,会将其转义后输出。
      1. 标准语法:{*@ 数据 }}
      2. 原始语法:<%-数据 %>
    2. 代码举例:

       <!-- 标准语法 --> 
       <h2>{*@ value }}</h2>
      
       <!-- 原始语法 --> 
       <h2><%- value %></h2>
      
  4. 条件判断

     <!-- 标准语法 -->
     {*if 条件}} ... {*/if}} 
     {*if v1}} ... {*else if v2}} ... {*/if}} 
     <!-- 原始语法 --> 
     <% if (value) { %> ... <% } %> 
     <% if (v1) { %> ... <% } else if (v2) { %> ... <% } %>
    
  5. 循环
    1. 标准语法:{*each 数据}} {*/each}}
    2. 原始语法:<% for() { %> <% } %>
     <!-- 标准语法 -->
     {*each target}} 
         {*$index}} {*$value}} 
     {*/each}} 
     <!-- 原始语法 --> 
     <% for(var i = 0; i < target.length; i++){ %>
         <%= i %> <%= target[i] %>
     <% } %>
    
  6. 子模版
    1. 使用子模板可以将网站公共区块(头部、底部)抽离到单独的文件中。
      1. 标准语法:{*include '模板'}}
      2. 原始语法:<%include('模板') %>
       <!-- 标准语法 --> 
       {*include './header.art'}} 
       <!-- 原始语法 --> 
       <% include('./header.art') %>
      
    2. 代码举例:

       {* include './common/header.art' }}
       <% include('./common/header.art') %>
       <div> {* msg }} </div>
       {* include './common/footer.art' }}
       <% include('./common/footer.art') %>
      
  7. 模板继承
    1. 以上讲过可以将网站页面中的公共部分抽取到子模板中
    2. html骨架(html、head、body标签)也属于每个页面的公共部分,但是这部分不能抽取到子模板中
    3. 使用模板继承可以将网站HTML骨架抽离到单独的文件中,其他页面模板可以继承骨架文件。
    4. 骨架文件中是可以设置一些坑位的,比如title、css、js等坑位,用于继承者填充
    5. 模板继承示例
      1. layout.art骨架模板

         <!doctype html> 
         <html> 
             <head>
                 <meta charset="utf-8">
                 <title>HTML骨架模板</title> 
                 <!--设置坑位-->
                 {*block 'head'}}{*/block}} 
             </head> 
             <body> 
                 <!--设置坑位-->
                 {*block 'content'}}{*/block}} 
             </body> 
         </html>
        
      2. 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}}
        
      3. app.js中引用index.art模板

  8. 模板配置
    1. 向模板中导入变量 template.defaults.imports.变量名 = 变量值;
      1. 可以在模板文件中直接调用外面的方法函数
    2. 设置模板根目录 template.defaults.root = 模板目录
      1. 渲染模板时就不需要再传入路径了,直接传入模板文件名称即可:template('文件名.后缀名', {})
    3. 设置模板默认后缀 template.defaults.extname = '.art'
      1. 渲染模板时传入的文件名称可以不带后缀名了:template('文件名', {})
    4. 代码举例:
      1. 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);
        
      2. 06.art模板

         <!--模板文件中使用外部导入的对象的函数功能-->
         {* dateFormat(time, 'yyyy-mm-dd')}}
        

模板使用总结

  1. js文件中
    1. 下载模板组件
    2. 导入模板组件
    3. 渲染模板
  2. art文件
    1. 编写模板内容

案例

  1. 案例介绍 – 学生档案管理,效果图如下: pic

    1. 目标: 模板引擎应用,强化node.js项目制作流程。
    2. 知识点:http请求响应、数据库、模板引擎、静态资源访问。

服务端制作流程

  1. 建立项目文件夹并生成项目描述文件
    1. 新建项目文件夹Students
    2. 打开命令行进入Students文件夹输入:npm init -y,生成项目描述文件packge.json文件
  2. 创建网站服务器-实现客户端和服务器端通信
    1. 项目根目录下常见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‘字样,说明客户端与浏览器通信成功
      
  3. 连接数据库并根据需求设计学员信息表
    1. 通过命令npm install mongoose下载MongoDB第三方模块
    2. 根目录下新建model文件夹,在里面新建一个connect.js文件,内部封装数据库连接代码
    3. app.js中导入该代码require('./model/connect')
    4. 在model文件夹下,新建user.js用于创建学生这个集合信息,然后导出
    5. 在app.js中导入该集合:const Student = require('./model/user')
    6. 代码部分:
      1. connect.js代码

         // 导入mongoose
         const mongoose = require('mongoose');
         // 连接数据库
         mongoose.connect('mongodb://localhost/playground',{useNewUrlParser: true,useUnifiedTopology: true})
         .then(()=>console.log('数据库连接成功!'))
         .catch(()=>console.log('数据库连接失败!'))
        
      2. 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;
        
  4. 创建路由并实现页面模板呈递
    1. 下载router:npm install router
    2. app.js引入模块:const getRouter = require('router')
    3. app.js中编写路由代码
    4. 下面模板引擎:npm install art-template
    5. 项目根目录下新建views文件夹,在该文件夹下创建模板文件:index.art/list.art
    6. 项目根目录下新建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('服务器启动成功!');
      
  5. 实现静态资源访问
    1. 上面的模板文件index.art/main.art本质就是html文件修改后缀名为art文件,那么这里面分别引用了index.css/main.css静态文件,那么也需要处理
  6. 实现学生信息添加功能
  7. 实现学生信息展示功能
第三方模块 router
  1. 功能:实现路由
  2. 使用步骤:
    1. 下载第三方模块:npm install router
    2. 引入模块:const getRouter = require('router')
    3. 获取路由对象
    4. 调用路由对象提供的方法创建路由
    5. 启用路由,使路由生效
     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)
     })
    
  3. 代码举例: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
  1. 功能:实现静态资源访问服务
  2. 步骤:
    1. 下载:npm install serve-static
    2. 引入serve-static模块获取创建静态资源服务功能的方法
    3. 调用方法创建静态资源服务并指定静态资源服务目录
    4. 启用静态资源服务功能
     //引入serve-static模块
     const serveStatic = require('serve-static') 
     //调用方法创建静态资源服务并指定静态资源服务目录
     const serve = serveStatic('public')
     //启用静态资源服务功能-监听静态资源的访问请求
     server.on('request', () => {
         serve(req, res)
     }) 
     server.listen(3000)
    
  3. 代码举例:

     // 引入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('服务器启动成功!');
    
    1. 这样路由相应的模板就会自动引用public文件夹中的静态文件(css)了

添加学生信息功能步骤分析

  1. 在模板的表单中指定请求地址与请求方式
  2. 为每一个表单项添加name属性
  3. 添加实现学生信息功能路由
  4. 接收客户端传递过来的学生信息
  5. 将学生信息添加到数据库中
  6. 将页面重定向到学生信息列表页面

学生信息列表页面分析

  1. 从数据库中将所有的学生信息查询出来
  2. 通过模板引擎将学生信息和HTML模板进行拼接
  3. 将拼接好的HTML模板响应给客户端

整体代码

  1. 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()
         })
     });
    
  2. model文件夹下的user.js/connect.js代码(略)
  3. public文件夹下的css文件内容(main.css/list.css)(略)
  4. views文件夹下
    1. 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>
      
    2. 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代码优化

  1. 将路由抽取到一个单独文件中去
  2. 项目根目录下新建一个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;
    
  3. 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: 项目入口文件
Table of Contents