zoukankan      html  css  js  c++  java
  • Node.js(day4)

    一、一些小问题

    1.文件操作路径和模块读取路径的问题

    • 我们使用fs核心模块系统进行文件操作时一般这样书写路径
    fs.readFile('./views/index.html');//读取views目录下的idnex.html文件
    
    • 我们使用require()进行自定义模块加载时的路径一般这样写
    require('js/main.js');//加载js目录下的main.js
    

    注意区分两者的区别,./的含义是相对路径,代表当前目录。文件操作路径不可以省略,而模块读取路径可以省略。另外如果忽略.写成'/',那么/代表的是磁盘根目录。

    2.让服务器自动重启的第三方模块nodemon

    每次都要更新js后都需要手动重新启动服务器,比较麻烦,我们可以使用nodemon第三方模块来实现服务器自启动。

    • 安装nodemon
    npm insatll --save nodemon -g
    
    • 使用
      在命令行中使用nodemon代替node命令即可:

    当执行的js被执行保存时服务器就会重新启动。

    二、在express中使用art-template

    art-template专门为express配套了第三方模块:express-art-template,可参考官方文档进行使用:express-art-template

    • 安装
    npm insatll --save art-template
    npm insatll --save express-art-template
    
    • 注册模板引擎
    //引入模块
    var express = require('express');
    var template =  require('express-art-template');
    
    var app = express();
    
    //在express中注册模板引擎
    app.engine('.html',template);
    

    使用app.engine(ext,callback)方法来注册模板引擎。callback是使用的模板引擎,ext是文件后缀。即当向页面呈现后缀为.ext的文件时,将使用指定的模板引擎进行渲染之后再呈现视图。

    • 使用
    app.get('/',(req,res) => {
    	res.render('./index.html',{comments:comments});
    });
    

    res.render(view [, locals] [, callback])方法就是上面说的呈现的视图的方法,即将某文件发送到页面上,一般就是html文件。
    view是字符串,表示文件的路径;locals是一个对象,其属性定义了视图中的变量,模板引擎就会在内部根据传入的参数进行渲染;callback(err,html)是一个回调函数,err为错误对象,html为将要呈现是字符串。

    • 注意
      上面的视图资源路径为./index.html,但其实找的是views/index.html。这是express默认的规则,如果没有指定视图资源路径,默认查找views目录下的文件。如果需要自定义视图资源路径,请使用:
    //设置views路径,可以传入单个目录或数组目录,查找时按照数组顺序查找
    app.set('views',dir | dirArr)
    

    三、案例:使用express实现留言板

    1.express替代http

    在之前的文章Node(2)中有一个留言板的案例,之前是使用http核心模块来实现的,现在我们使用express来实现。(html代码在之前的文章中,可自取)

    var express = require('express');
    
    //服务器
    var app = express();
    app.listen(8080,function(){
    	console.log('server running at 8080');
    });
    
    var comments = [];
    
    
    //注册art-template模板引擎
    app.engine('html', require('express-art-template'));
    //配置静态资源
    app.use('/public',express.static('./public'));
    
    
    //处理get请求
    app.get('/',(req,res) => {
    	res.render('./index.html',{comments:comments},function(err,html){
    		res.send(html);
    	});
    });
    app.get('/post',(req,res) => {
    	res.render('./post.html');
    });
    app.get('/addComment',(req,res) => {
    	//获取get提交的参数==>相当于使用url.parse(req.url,true).query;
    	var query = req.query;
    	query.dateTime = new Date().toLocaleString();
    	comments.unshift(query);
    	//重定向==>相当于res.statusCode=302;res.setHeader('Location','/')
    	res.redirect(302,'/');
    });
    

    显而易见,之前的js代码有60余行,现在的js代码只有30余行。值得注意的几个点:

    • 我们在使用http核心模块时,还需要手动引入fs模块,art-template模块。现在使用了express之后不需要再手动引用,因为express内部会自动引用。
    • 之前的页面渲染逻辑是:fs读取html文件 --> 使用art-template的render()对读取的字符串进行渲染 --> 发送到页面。
    • 现在的页面渲染逻辑是:使用app.engine()注册模板引擎 --> 使用res.render()渲染页面并显示。
    • 其实express内部操作和我们之前的没有太大差别,但框架的这种封装特性极大地提高了我们的开发效率,然我们能更专注于业务。
    • 还有简化操作的api比如:req.query()其实就是之前的url.parse(req.url,true).query;;而res.redirect()是简化后的重定向。

    2.post提交表单的处理

    对于get提交,我们使用req.request即可获得提交的参数对象,但express没有提供post提交的参数获取的api。所以如果我们是post提交表单,就需要像之前一样使用req.on('data',backcall(postdata))来接受数据,且接受的postdata数据还需要进行解码,也是十分麻烦。所以这里我们推荐使用另一个第三方插件body-parser

    • 安装 body-parser
    npm install body-parser --save
    
    • 在express中使用body-parser
    //引入模块
    var bodyParser = require('body-parser');
    
    //使用
    app.use(bodyParser.urlencoded({extended:false}));
    app.use(bodyParser.json());
    

    然后使用request.body就能获取到post提交的参数对象。
    这里说一下app.use([path,] callback [, callback...])这一方法,该方法的作用是在指定的路径下安装指定的中间件函数,即每当请求路在是指定路径下时就执行中间件函数。
    其中:
    #参数path为可选参数,默认为/,表示执行根路径,即这个路劲下的所有请求都会执行。
    #callback为中间件函数。如果需要执行多个函数,使用,隔开即可。
    所以,use函数相当于一个过滤器,一般是增加一些属性或功能方便我们使用。
    上面两个中间件函数:

    // 创建 application/json 解析 
    var jsonParser = bodyParser.json() 
    // 创建 application/x-www-form-urlencoded 解析 
    var urlencodedParser = bodyParser.urlencoded({ extended: false })
    

    我们可以理解为,body-parser的这两个方法为response添加了body属性,值为post提交的参数对象。

    四、案例:学生信息CRUD(增删改查)

    1.准备相关文件

    在bootstrap上随便扒的页面:

    • 主页index.html
    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
        <meta name="description" content="">
        <meta name="author" content="">
        <link rel="icon" href="../../favicon.ico">
        <title>Dashboard Template for Bootstrap</title>
        <!-- Bootstrap core CSS -->
       <link href="public/css/bootstrap.min.css" rel="stylesheet">
      </head>
      <body>
        <h2 class="text-center">学生信息表</h2>
        <h2 class="sub-header">
          <button class="btn btn-primary"><a style="color:white;" href="/students/add">添加学生</a></button>
        </h2>
        <table class="table table-striped">
            <thead>
              <tr>
                <th>#Id</th>
                <th>姓名</th>
                <th>性别</th>
                <th>年龄</th>
                <th>爱好</th>
                <th>操作</th>
              </tr>
            </thead>
            <tbody>
              {{each students}}
                <tr>
                  <td>{{$value.id}}</td>
                  <td>{{$value.name}}</td>
                  <td>{{if $value.gender==0}}男{{else}}女{{/if}}</td>
                  <td>{{$value.age}}</td>
                  <td>{{$value.hobbies}}</td>
                  <td><a href="/students/modify?id={{$value.id}}">
                  修改
                  </a>
                  |<a href="/students/delete?id={{$value.id}}">
                  删除
                   </a></td>
                </tr>
              {{/each}}
            </tbody>
          </table>
      </body>
    </html>
    
    • add.html
    <!DOCTYPE html>
    <html>
    <head>
      <title>添加学生</title>
      <meta charset="utf-8">
       <link href="/public/css/bootstrap.min.css" rel="stylesheet">
    </head>
    <body>
      <h1 class="text-center">学生信息</h1>
      <form style="padding-left: 30px;" role='form' method="post" action="/students/add">
        <div class="form-group">
          <label>姓名</label>
          <input type="text" class="form-control" name="name" placeholder="请输入你的大名">
        </div>
         <div class="form-group">
          <label>性别</label>
          <div class="radio-inline">
            <label>
              <input type="radio" name="gender" value="0" checked>
             男
            </label>
          </div>
          <div class="radio-inline">
            <label>
              <input type="radio" name="gender" value="1">
             女
            </label>
          </div>
        </div>
        <div class="form-group">
          <label>年龄</label>
          <input type="number" name="age" min="0" max="800" value="18">
        </div>
        <div class="form-group">
          <label>爱好</label>
          <input type="text" class="form-control" name="hobbies" placeholder="请输入你的爱好">
        </div>
        <button type="submit" class="btn btn-primary">Submit</button>
      </form>
    </body>
    </html>
    
    • bootstrap.min.js自行下载

    2.路由设计

    路由可以理解为收发网络的设备,可以抽象为一个映射关系表。页面的请求和执行的回调函数也是一一对应的,为了方便管理,我们一般会进行的页面请求整体设计,也可以称为路由设计。

    功能 请求方式 url get参数 post参数
    学生信息页 get /students - -
    增加学生页 get /students/add - -
    增加学生 post /students/add - id&name&age&gender&hobbies
    删除学生 get /students/delete id -
    修改学生页 get /students/modify id
    修改学生 post /students/modify - id&name&age&gender&hobbies

    根据路由设计,写出请求结构:

    //主页
    app.get('/',(req,res) => {
    
    });
    //学生信息页
    app.get('/students',(req,res) => {
    
    });
    //增加学生信息页
    app.get('/students/add',(req,res) => {
    
    });
    //增加学生
    app.post('/students/add',(req,res) => {
    
    });
    //删除学生
    app.get('/students/delete',(req,res) => {
    
    });
    //修改学生
    app.post('/students/modify',(req,res) => {
    
    });
    

    一般为了方便管理,也符合更好地设计思路,我们把这些请求单独放到一个js中,让每个js即每个模块都有各自统一的功能,而不是很杂:

    • app.js:程序的入口,负责服务器的创建及相关配置。
    • router.js:路由管理模块,存放路由设计信息。
      那么现在就有一个问题,如果将上面的代码直接放到router.js中,该如何调用?使用exports | module.exports即可。

    3.使用json文件来充当数据库

    既然要实现信息的CRUD,就需要一个数据库,但我们暂不需要使用数据库,使用一个json文件保存学生信息即可。

    • db.json
    {
    	"students":[
    		{"id":1,"name":"Tom","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
    		{"id":2,"name":"Lucy","gender":0,"age":20,"hobbies":"吃饭睡觉打豆豆"},
    		{"id":3,"name":"Michel","gender":0,"age":4,"hobbies":"吃饭睡觉打豆豆"},
    		{"id":4,"name":"Afile","gender":0,"age":33,"hobbies":"吃饭睡觉打豆豆"},
    		{"id":5,"name":"Joky","gender":0,"age":45,"hobbies":"吃饭睡觉打豆豆"}
    	]
    }
    

    测试一下:

    • app.js
    var express = require('express');
    var bodyParser = require('body-parser');
    
    
    var app = express();
    app.listen('8080',() => {
    	console.log('server running at 8080');
    });
    
    //配置
    app.use('/public',express.static('./public'));
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false }))
    
    //路由
    var router = require('./router.js');
    router(app);
    
    • router.js
    module.exports = function(app){
    	var fs = require('fs');
    
    	//主页(直接跳到学生信息页好了)
    	app.get('/',(req,res) => {
    		res.redirect(302,'/students');
    	});
    	//学生信息页
    	app.get('/students',(req,res) => {
    		//使用fs读取json文件
    		fs.readFile('./db.json','utf8',(err,data) => {
    			if(err)
    				return res.status(500).send('500');
    			var students = JSON.parse(data).students;
    			res.render('./index.html',{
    				"students":students
    			});
    		});
    	});
    	//增加学生信息页
    	app.get('/students/add',(req,res) => {
    		res.render('./add.html');
    	});
    	//增加学生
    	app.post('/students/add',(req,res) => {
    		
    	});
    	//删除学生
    	app.get('/students/delete',(req,res) => {
    		res.send('delete ok');
    	});
    	//修改学生
    	app.post('/students/modify',(req,res) => {
    		res.send('modify ok');
    	});
    }
    
    • 效果:

    4.封装操作db.json文件的方法

    在CRUD中我们会多次使用fs来读取文件,为了方便调用,我们有必要对重复性的工作进行封装。

    • student.js
    var fs = require('fs');
    
    /*
    	将students放到db.json中
    */
    function write(students){
    	var db = {
    		"students":students
    	};
    	var dbStr = JSON.stringify(db);
    	fs.writeFile('./db.json',dbStr,'utf8',(err) => {
    		if(err){
    			return false;
    		}
    	});
    }
    
    /*获取全部学生信息
    	return students[]
    */
    function queryAll(callback){
    	fs.readFile('./db.json','utf8',(err,data) => {
    		if(err){
    			callback({});
    		}
    		var students = JSON.parse(data).students;
    		callback(students);
    	});
    }
    
    /*根据id获取一个学生信息
    	return Obj || null
    */
    function queryById(id,callback){
    	queryAll((students) => {
    		var stu = null;
    		students.forEach((value,index) => {
    			if(value.id === id){
    				stu = value;
    				return;
    			}
    		});
    		callback(stu);
    	});
    }
    
    /*增加一个学生
    */
    function add(stu){
    	queryAll((students) => {
    		students.push(stu);
    		write(students);
    	});
    }
    
    /*删除一个学生
    	return stu
    */
    function del(id,callback){
    	queryById(id,(stu) => {
    		if(stu === null){
    			callback(stu);
    			return;
    		}
    		queryAll((students) => {
    			 students = students.filter((value,index) => {
    				return value.id !== id;
    			});
    			write(students);
    			callback(stu);
    		});
    	});
    }
    
    /*修改一个学生
    	return Boolean
    */
    function modify(stu,callback){
    	queryById(stu.id,(flag) => {
    		if(flag === null){
    			callback(false);
    			return;
    		}
    		queryAll((students) => {
    			students = students.map((value,index) => {
    				if(value.id === stu.id){
    					return stu;
    				}else{
    					return value;
    				}
    			});
    			write(students);
    			callback(true);
    		});
    	});
    }
    
    //将方法导出
    exports.queryAll = queryAll;
    exports.queryById = queryById;
    exports.add = add;
    exports.delete = del;
    exports.modify = modify;
    
    • 值得注意的是,由于fs读取文件是异步操作,而我们又需要异步操作的结果,那我们就需要使用回调函数来获取异步操作的结果

    • 修改router.js

    module.exports = function(app){
    	var stuUtil = require('./student.js');
    
    	//主页(直接跳到学生信息页好了)
    	app.get('/',(req,res) => {
    		res.redirect(302,'/students');
    	});
    	//学生信息页
    	app.get('/students',(req,res) => {
    		stuUtil.queryAll((students) => {
    			res.render('./index.html',{
    				"students":students
    			});
    		});
    	});
    	//增加学生信息页
    	app.get('/students/add',(req,res) => {
    		res.render('./add.html');
    	});
    	//增加学生
    	app.post('/students/add',(req,res) => {
    		var stu = req.body;
    		stu.id = Math.floor(Math.random()*1000+1);//产生随机整数
    		stuUtil.add(stu);
    		res.redirect(302,'/students');
    	});
    	//删除学生
    	app.get('/students/delete',(req,res) => {
    		stuUtil.delete(parseInt(req.query.id),(flag) =>{
    			if(flag)
    				res.redirect(302,'/students');
    			else
    				res.send('500');
    		});
    	});
    	//修改学生信息页
    	app.get('/students/modify',(req,res) => {
    		stuUtil.queryById(parseInt(req.query.id),(stu) => {
    			res.render('./modify.html',{"stu":stu});
    		});
    	});
    	//修改学生
    	app.post('/students/modify',(req,res) => {
    		req.body.id = parseInt(req.body.id);
    		stuUtil.modify(req.body,(flag) => {
    			if(flag)
    				res.redirect(302,'/students');
    			else
    				res.send('500');
    		});
    	});
    }
    
    • 效果

    5.使用express提供的路由管理

    具体参考官方文档

    • app.js
    var express = require('express');
    var bodyParser = require('body-parser');
    //路由
    var router = require('./router.js');
    
    var app = express();
    app.listen('8080',() => {
    	console.log('server running at 8080');
    });
    
    //配置
    app.use('/public',express.static('./public'));
    app.engine('html',require('express-art-template'));
    app.use(bodyParser.urlencoded({extended:false}));
    app.use(bodyParser.json());
    app.use(router);//挂载路由
    
    module.exports = app;
    
    • router.js
    var stuUtil = require('./student.js');
    var express = require('express');
    var router = express.Router();
    
    //主页(直接跳到学生信息页好了)
    router.get('/',(req,res) => {
    	res.redirect(302,'/students');
    });
    //学生信息页
    router.get('/students',(req,res) => {
    	stuUtil.queryAll((students) => {
    		res.render('./index.html',{
    			"students":students
    		});
    	});
    });
    //增加学生信息页
    router.get('/students/add',(req,res) => {
    	res.render('./add.html');
    });
    //增加学生
    router.post('/students/add',(req,res) => {
    	var stu = req.body;
    	stu.id = Math.floor(Math.random()*1000+1);//产生随机整数
    	stuUtil.add(stu);
    	res.redirect(302,'/students');
    });
    //删除学生
    router.get('/students/delete',(req,res) => {
    	stuUtil.delete(parseInt(req.query.id),(flag) =>{
    		if(flag)
    			res.redirect(302,'/students');
    		else
    			res.send('500');
    	});
    });
    //修改学生信息页
    router.get('/students/modify',(req,res) => {
    	stuUtil.queryById(parseInt(req.query.id),(stu) => {
    		res.render('./modify.html',{"stu":stu});
    	});
    });
    //修改学生
    router.post('/students/modify',(req,res) => {
    	req.body.id = parseInt(req.body.id);
    	stuUtil.modify(req.body,(flag) => {
    		if(flag)
    			res.redirect(302,'/students');
    		else
    			res.send('500');
    	});
    });
    
    module.exports = router;
    
  • 相关阅读:
    如何修改光圈,焦距等摄像头相关信息
    白平衡是什么?怎么理解白平衡?
    如何在 kernel 和 hal 层读取同一个标志
    相机知识问答
    HDR 拍照模式的原理,实现及应用
    MTK android 工程中如何修改照片详细信息中机型名
    反差式对焦浅析
    android Camera 如何判断当前使用的摄像头是前置还是后置
    android Camera 录像时旋转角度
    android Camera 中添加一种场景模式
  • 原文地址:https://www.cnblogs.com/fzz9/p/9929755.html
Copyright © 2011-2022 走看看