后续会不断完善…
重点:
①服务器渲染 — 模板实现
②get和post请求实现
③自定义路由模块
④自定义操作数据模块
⑤回调函数原理代码实现
通过实际项目进行知识点的运用
可利用关键字查询(ctrl+f)->知识点
一.回调函数原理代码实现
因为回调函数的原理单单从实际运用中难以理解所以开头便进行分析
在自定义模块中的读取文件操作是异步的,那么在不利用promise情况下怎么处理异步的操作呢?
如果理解回调函数的原理,那么这个问题的答案便十分简单
1.模拟异步操作
function asynchronization() {
this.setTimeout(function() {
var data = 1;
}, 1000);
}
2.思考怎么可以得到data,以下给出了几种错误情况
function asynchronization() {
this.setTimeout(function() {
var data = 1;
}, 1000);
return data;
}
asynchronization(); //无输出结果
function asynchronization() {
var data = 0;
this.setTimeout(function() {
data = 1;
}, 1000);
return data;
}
asynchronization(); //输出0,相当于获取失败
function asynchronization() {
this.setTimeout(function() {
var data = 1;
return data;
}, 1000);
}
asynchronization(); //无输出结果
3.为什么会输不出data呢
①线程在运行顺序运行时,进入到了asynchronization函数,碰到了setTimeout,这个函数的意义就是1000ms之后执行内部语句,最后就跳出来
②那么拿上面第第三个来进行分析,按照我们的思路,data确确实实赋值了1且return了
③其实我们自己看看,会发现这里面有两个function,可理解为两个函数,再想想return的值是返回给自己所在的function
④ooo,那么就可以理解当我们调用asynchronization函数时为什么不会返回data了
4.解决方法
function asynchronization(callback) {
this.setTimeout(function() {
var data = 1;
callback(data); //相当于function(get){console.log(get)} 其中get是参数
}, 1000);
}
asynchronization(function(get) {
console.log(get);
}); //输出1
5.例子展示
Ajax中的success就像解决方法中的callback
$.ajax({
url: "url",
type: "POST",
data: data,
success: function(get) {
console.log(get)
}
});
6.回调函数的需求
①Node.js作为后台处理的一个服务器平台,其本质依然是Javascript。②Javascript的特点便是非阻塞式的单线程,究其原因在于强大的动态性。假设一个浏览器操作场景,如果使用多条DOM操作页面语句,如果由多个线程去执行的话,页面的操作便会非常杂乱无章,不利于编写人员的控制。
③单线程遇到需要等待操作怎么办捏?不可能为了等一个操作而停下执行代码,这样的效率就非常低。为了解决这个Javascript有个事件队列的机制,当遇到需要等待的操作时,push进事件队列中并将其交给其他部件来操作,例如文件读取硬件,自己则继续向下执行
④如果A语句是读文件操作,B语句是判断文件是否拥有什么字符串的操作,那么B语句要怎么判断A语句到底在执行还是执行失败了,如果A语句什么也不说,B语句就无法执行。此处如果由回调函数就可以解决这个问题
//fs中由的readFile在读取失败时error为错误信息,data为null;成功时error问为null,data为数据
A语句:readFile('filePath',function(error,data){
if(error){
console.log("读取文件失败.")
}else{
B语句
}
})
二.页面
1.首页
2.添加页面
3.修改页面
三.代码实现
1.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="./node_modules/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Custom styles for this template -->
<link href="./public/css/dashboard.css" rel="stylesheet" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#navbar"
aria-expanded="false"
aria-controls="navbar"
>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Student Management System</a>
</div>
<div id="navbar" class="navbar-collapse collapse"></div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active">
<a href="#"
>Student List <span class="sr-only">(current)</span></a
>
</li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="table-responsive">
<a href="/student/new" class="btn btn-success">Add Student</a>
<table class="table table-striped">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>GENDER</th>
<th>AGE</th>
<th>HOBBY</th>
<th>OPERATION</th>
</tr>
</thead>
<tbody>
{{each students}}
<tr>
<td>{{$value.id}}</td>
<td>{{$value.name}}</td>
<td>{{$value.gender}}</td>
<td>{{$value.age}}</td>
<td>{{$value.hobby}}</td>
<td>
<a href="/student/edit?id={{ $value.id }}">编辑</a>
<a href="/student/delete?id={{ $value.id }}">删除</a>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
</div>
</div>
</body>
</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="../node_modules/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Custom styles for this template -->
<link href="../public/css/dashboard.css" rel="stylesheet" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#navbar"
aria-expanded="false"
aria-controls="navbar"
>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Student Management System</a>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active">
<a href="#"
>Student List <span class="sr-only">(current)</span></a
>
</li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3">
<h2 class="sub-header">Add Student</h2>
<div class="table-responsive">
<form action="/student/new" method="post">
<div class="form-group">
<label for="">Name</label>
<input type="text" class="form-control" id="name" name="name" />
</div>
<div class="form-group">
<label>Gender</label>
<div></div>
<label class="radio-inline">
<input
type="radio"
name="gender"
id="inlineRadio"
value="man"
/>
Male
</label>
<label class="radio-inline">
<input
type="radio"
name="gender"
id="inlineRadio"
value="woman"
/>
Female
</label>
</div>
<div class="form-group">
<label for="age">Age</label>
<input type="number" class="form-control" id="age" name="age" />
</div>
<div class="form-group">
<label for="hobby">Hobby</label>
<input
type="text"
class="form-control"
id="hobby"
name="hobby"
/>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</div>
</body>
</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="../node_modules/bootstrap/dist/css/bootstrap.min.css"
rel="stylesheet"
/>
<!-- Custom styles for this template -->
<link href="../public/css/dashboard.css" rel="stylesheet" />
</head>
<body>
<nav class="navbar navbar-inverse navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed"
data-toggle="collapse"
data-target="#navbar"
aria-expanded="false"
aria-controls="navbar"
>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Student Management System</a>
</div>
</div>
</nav>
<div class="container-fluid">
<div class="row">
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active">
<a href="#"
>Student List <span class="sr-only">(current)</span></a
>
</li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3">
<h2 class="sub-header">Edit Student Information</h2>
<div class="table-responsive">
<form action="/student/edit" method="post">
<input type="hidden" name="id" value="{{student.id}}" />
<div class="form-group">
<label for="">Name</label>
<input
type="text"
class="form-control"
id="name"
name="name"
value="{{student.name}}"
/>
</div>
<div class="form-group">
<label>Gender</label>
<div></div>
<label class="radio-inline">
<input
type="radio"
name="gender"
id="inlineRadio"
value="man"
/>
Male
</label>
<label class="radio-inline">
<input
type="radio"
name="gender"
id="inlineRadio"
value="woman"
/>
Female
</label>
</div>
<div class="form-group">
<label for="age">Age</label>
<input
type="number"
class="form-control"
id="age"
name="age"
value="{{student.age}}"
/>
</div>
<div class="form-group">
<label for="hobby">Hobby</label>
<input
type="text"
class="form-control"
id="hobby"
name="hobby"
value="{{student.hobby}}"
/>
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
</div>
</div>
</div>
</div>
</body>
</html>
2.入口文件和自定义模块
//app.js
//模块导入
const express = require("express");
const app = express();
const fs = require("fs");
const router = require("./router");
const bodyParse = require("body-parser");
//服务器模板渲染
app.engine("html", require("express-art-template"));
//公开静态资源
app.use("/node_modules", express.static("node_modules"));
app.use("/public", express.static("public"));
//解析post提交
app.use(bodyParse.urlencoded({ extended: false }));
app.use(bodyParse.json());
//开启网络监听
app.listen(3000, function() {
console.log("listening...");
});
//使用自定义路由模块
app.use(router);
/*
自定义路由器模块知识点
*/
//router.js
// 导入模块
var express = require("express");
var fs = require("fs");
var app = express();
var router = express.Router();
// 自定义模块
var Student = require("./student");
/*
模板渲染知识点: res.render(渲染页面,渲染参数)
*/
// 首页渲染
router.get("/", function(req, res) {
Student.find(function(error, data) {
res.render("index.html", {
students: data
});
});
});
// 添加学生信息页面渲染
router.get("/student/new", function(req, res) {
res.render("new.html");
});
/*
get解析知识点: req拥有的query属性可获得类似http://www.baidu.com/index.php?id=1中的?后面所带的键值对
*/
// 修改学生信息页面渲染
router.get("/student/edit", function(req, res) {
Student.findById(parseInt(req.query.id), function(error, stu) {
if (error) {
return res.status(500).send("Server Error");
}
res.render("edit.html", {
student: stu
});
});
});
/*
post解析知识点: req拥有的body属性可解析form中action中提交的参数,利用body属性前需要导入body-parse模块(官方模块)
*/
//添加学生信息操作
router.post("/student/new", function(req, res) {
Student.save(req.body, function(error) {
if (error) {
return res.status(500).send("Server Error");
}
});
res.redirect("/");
});
// 修改学生信息操作
router.post("/student/edit", function(req, res) {
Student.updateById(req.body, function(error) {
if (error) {
return res.status(500).send("Server Error");
}
});
res.redirect("/");
});
// 删除学生信息操作
router.get("/student/delete", function(req, res) {
Student.deleteById(req.query.id, function(error) {
return res.status(500).send("Server Error");
});
res.redirect("/");
});
// 导出模块提供入口文件使用
module.exports = router;
/*
自定义数据库操作模块
*/
//student.js
//导入模块
var fs = require("fs");
//JSON数据文件路径
var dbPath = "./db.json";
/*
回调函数知识点
*/
//查询操作-渲染首页学生信息
exports.find = function(callback) {
fs.readFile(dbPath, "utf8", function(error, data) {
if (error) {
return callback(error);
}
callback(null, JSON.parse(data).students);
});
};
//查询操作-根据id返回指定student的JSON数据-进入编辑页面
exports.findById = function(id, callback) {
fs.readFile(dbPath, "utf8", function(error, data) {
if (error) {
return callback(error);
}
var students = JSON.parse(data).students;
var stu = students.find(function(item) {
return item.id == id;
});
callback(null, stu);
});
};
// 添加操作-存储到数据文件-提供首页渲染
exports.save = function(student, callback) {
fs.readFile(dbPath, "utf8", function(error, data) {
if (error) {
return callback(error);
}
var students = JSON.parse(data).students;
if (students.length == 0) {
student.id = 1;
} else {
student.id = students[students.length - 1].id + 1;
}
students.push(student);
var fileData = JSON.stringify({
students: students
});
fs.writeFile(dbPath, fileData, function(error) {
if (error) {
callback(error);
}
});
});
};
//修改操作-修改数据文件
exports.updateById = function(student, callback) {
fs.readFile(dbPath, "utf8", function(error, data) {
if (error) {
return callback(error);
}
var students = JSON.parse(data).students;
var stu = students.find(function(item) {
return parseInt(item.id) === parseInt(student.id);
});
for (var key in student) {
stu[key] = student[key];
}
var fileData = JSON.stringify({
students: students
});
fs.writeFile(dbPath, fileData, function(error) {
if (error) {
callback(error);
}
});
});
};
//删除操作
exports.deleteById = function(id, callback) {
fs.readFile(dbPath, "utf8", function(error, data) {
if (error) {
return callback(error);
}
var students = JSON.parse(data).students;
var studentId = students.findIndex(function(item) {
return parseInt(item.id) == parseInt(id);
});
students.splice(studentId, 1);
var fileData = JSON.stringify({
students: students
});
fs.writeFile(dbPath, fileData, function(error) {
if (error) {
callback(error);
}
});
});
};
3.数据文件(还没学到数据库,所以暂时利用数据文件代替)
{
"students": [
{
"name": "黄德渣",
"age": "33",
"hobby": "DNF",
"id": "1",
"gender": "man"
},
{ "name": "渣威",
"gender": "man",
"age": "33",
"hobby": "LOL",
"id": 2 }
]
}