要点:
1、初步体会node基于模块化的编程思想,在node中,模块之间的互不影响,模块与模块之间只有通过module.exports(node为了减少开发人员代码,默认添加一句 var exports = module.export)才能实现数据的共享。
2、自定义模块时,尽量做到一个模块处理一个功能,同时写的鲁棒性越强,越好。
3、最重要的:在函数进行异步操作时,想要获取函数中的值,必须通过回调函数才能获取到。如果想要对函数中的方法的值进行操作,在回调函数中处理。在这个例子中如果回调函数发生错误直接返回错误对象,也不需要进行数据类型的转变,如果正确获取到数据,前一个参数设置为null,后一个参数是返回的数据,既可与错误的返回做区分,也可以返回数据,中间通过逗号隔开,这是现阶段第一种理解。
另外在一种理解是,函数作为参数使用,在调用时输入了一个函数,这个函数就是需要拿到回调函数进行处理的函数,对函数相关的异步操作成功或者失败两种不同的结果,调用两次回调函数传入不同的数据,一种是错误对象,另一种是(null,data)两个参数,第一个参数返回null的错误对象。
这里去掉过第一个参数null,程序运行失败,第二种理解的null的理解不太正确。另外,将正确的callback回调函数前添加return是正确执行的,第一种的理解应该较正确但是不透彻。
4、为了使路由更加简洁直观,通过 express.Router()创建一个路由容器,再将路由挂载到路由容器对象上,最后通过 server.use(router)将路由容器挂载到node创建的服务上。
效果:
依赖:
{
"name": "crub-expre",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"art-template": "^4.13.2",
"body-parser": "^1.18.3",
"bootstrap": "^3.3.7",
"express": "^4.16.4",
"express-art-template": "^1.0.1"
}
}
node入口:
var express = require('express')
var fs = require('fs')
var bodyParser = require('body-parser')
var router = require('./router.js')
// 引入自定义的将路由模块化的包
var app = express()
app.use('/node_modules/',express.static('./node_modules/'))
// 开放固定文件夹资源,前一项参数是监听的url,是否有这段字符,后一个参数是监听对应的文件夹
app.use('/public/',express.static('./public/'))
app.engine('html',require('express-art-template'))
app.use(bodyParser.urlencoded({ extended:false }))
app.use(bodyParser.json())
// 模板引擎的配置和body-parser一定要在将路由模块挂载到服务之前
app.use(router)
// 将router容器挂载到node创建的服务器上
app.listen(3000,function(){
console.log('succeed')
})
路由模块:
var fs = require('fs')
// 核心模块只要在所在的模块中被使用了,就需要重新引入
var express = require('express')
var Shuaiges = require('./shuaiges.js')
// 引入帅哥文件夹的操作模块
var router = express.Router()
// 创建一个express的路由容器
router.get('/',function(req,res){
Shuaiges.find(function(err,shuaiges){
if(err){
return res.status(500).send('not find file')
// 返回的状态码500,表示出错
}
res.render('index.html',{
// 在express框架中,默认在views中寻找文件
shuaiges:shuaiges
})
})
})
// 将路由都挂载到router容器上
router.get('/shuaiges/new',function(req,res){
res.render('new.html')
})
router.post('/shuaiges/new',function(req,res){
Shuaiges.save(req.body,function(err){
if(err){
return res.status(500).send('save fail')
}
res.redirect('/')
// 成功跳到首页
})
})
router.get('/shuaiges/edit',function(req,res){
Shuaiges.findById(parseInt(req.query.id),function(err,shuaige){
// query中的查询字符串需要转换成数字类型,跟定义的时候相同
if(err){
return res.status(500).send('can not edit file')
}
res.render('edit.html',{
shuaige:shuaige
})
})
})
router.post('/shuaiges/edit',function(req,res){
Shuaiges.updata(req.body,function(err){
if(err){
return res.status(500).send('updata fail')
}
res.redirect('/')
})
})
router.get('/shuaiges/delete',function(req,res){
Shuaiges.delete(req.query.id,function(err){
if(err){
return res.status(500).send('delete fail')
}
res.redirect('/')
})
})
module.exports = router
帅哥文件操作模块:
var fs = require('fs')
// 引入文件读取核心模块,这个自定义文件模块,封装操作shuaiges.json的方法,做到类似数据库
filePath = './shuaiDb.json'
// 这里路径就是用来存放帅哥的文件夹
exports.find = function(callback){
fs.readFile(filePath,function(err,data){
if(err){
return callback(err)
// 读取失败,中止代码执行,并返回错误对象
}
callback(null,JSON.parse(data).shuaiges)
// 当错误时,返回错误对象,正确时错误返回null,并且返回数据数组两个信息
// callback回调函数是自定义的,需要区分回调函数返回的数据还是错误信息,如果直接传入
// data无法与前面的err作区分,这里还是需要前面定义一个参数null,既可以作区分,又没有实际数值
})
}
exports.findById = function(id,callback){
fs.readFile(filePath,function(err,data){
if(err){
return callback(err)
}
var shuaiges = JSON.parse(data).shuaiges
var shuaige = shuaiges.find(function(item){
return item.id === parseInt(id)
// .find()是es6方法,遍历数组并返回符合find内传入方法的条件的值
})
callback(null,shuaige)
})
}
exports.save = function(shuaige,callback){
fs.readFile(filePath,function(err,data){
if(err){
return callback(err)
}
var shuaiges = JSON.parse(data).shuaiges
// 将二进制数据转为对象,并操作内部的shuaiges数组
shuaige.id = shuaiges[shuaiges.length -1].id + 1
// 为每个添加进来的帅哥添加一个不重复的id
shuaiges.push(shuaige)
// 将post提交过来的shuaige对象,添加进对象之中数组
var strData = JSON.stringify({
shuaiges:shuaiges
})
// 将shuaiges对象再转换成字符串类型,为了写入文件
fs.writeFile(filePath,strData,function(err){
if(err){
return callback(err)
// 写入文件失败,返回错误对象
}
callback(null)
// 写入成功,返回空的错误对象
})
})
}
exports.updata = function(shuaige,callback){
fs.readFile(filePath,function(err,data){
if(err){
return callback(err)
}
var shuaiges = JSON.parse(data).shuaiges
shuaige.id = parseInt(shuaige.id)
// 存入时,将存入string形式的id改为data类型,防止数据类型不一致造成的问题
var shuaigeOld = shuaiges.find(function(item){
return item.id === parseInt(shuaige.id)
})
// .find()是es6方法,遍历数组并返回符合find内传入方法的条件的值
for(var key in shuaige){
shuaigeOld[key] = shuaige[key]
// 将查询到的shuaige对象替换成新的对象
}
var strData = JSON.stringify({
shuaiges:shuaiges
})
// 将shuaiges对象再转换成字符串类型,为了写入文件
fs.writeFile(filePath,strData,function(err){
if(err){
return callback(err)
}
callback(null)
})
})
}
exports.delete = function(id,callback){
fs.readFile(filePath,function(err,data){
if(err){
return callback(err)
}
var shuaiges = JSON.parse(data).shuaiges
var deleteId = shuaiges.findIndex (function(item){
return item.id === parseInt(id)
})
shuaiges.splice(deleteId,1)
// es6语法,前一位指定开始的位置,后一位指定删除的长度
var strData = JSON.stringify({
shuaiges:shuaiges
})
fs.writeFile(filePath,strData,function(err){
if(err){
return callback(err)
}
callback(null)
})
})
}
主页:
<!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="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</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="#">Overview <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h1 class="page-header">Dashboard</h1>
<div class="row placeholders">
{{ each fruits }}
<div class="col-xs-6 col-sm-3 placeholder">
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAACH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" width="200" height="200" class="img-responsive" alt="Generic placeholder thumbnail">
<h4>{{ $value }}</h4>
<span class="text-muted">Something else</span>
</div>
{{ /each }}
</div>
<h2 class="sub-header">Section title</h2>
<a class="btn btn-success" href="/shuaiges/new">添加学生</a>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>qq</th>
</tr>
</thead>
<tbody>
{{ each shuaiges }}
<tr>
<td>{{ $value.id }}</td>
<td>{{ $value.name }}</td>
<td>{{ $value.qq }}</td>
<td>
<a href="/shuaiges/edit?id={{ $value.id }}">修改</a>
<a href="/shuaiges/delete?id={{ $value.id }}">删除</a>
<!-- url的路径,发出get方式的请求,结合模板引擎 -->
</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/main.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="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</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="/shuaiges">学生管理 <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class="col-sm-offset-3 col-md-offset-2 main">
<h2 class="sub-header">添加学生</h2>
<form action="/shuaiges/edit" method="post">
<!--
用来放一些不希望被用户看见,但是需要被提交到服务端的数据
-->
<input type="hidden" name="id" value="{{ shuaige.id }}">
<div class="form-group">
<label for="">姓名</label>
<input type="text" class="form-control" id="" name="name" required minlength="2" maxlength="10" value="{{ shuaige.name }}">
</div>
<div class="form-group">
<label for="">qq</label>
<input class="form-control" type="number" id="" name="qq" value="{{ shuaige.qq }}" required minlength="2" maxlength="10">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</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="">
<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/main.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="#">Project name</a>
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Dashboard</a></li>
<li><a href="#">Settings</a></li>
<li><a href="#">Profile</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
<input type="text" class="form-control" placeholder="Search...">
</form>
</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="/shuaiges">学生管理 <span class="sr-only">(current)</span></a></li>
<li><a href="#">Reports</a></li>
<li><a href="#">Analytics</a></li>
<li><a href="#">Export</a></li>
</ul> -->
<ul class="nav nav-sidebar">
<li><a href="">Nav item</a></li>
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
<li><a href="">More navigation</a></li>
</ul>
<ul class="nav nav-sidebar">
<li><a href="">Nav item again</a></li>
<li><a href="">One more nav</a></li>
<li><a href="">Another nav item</a></li>
</ul>
</div>
<div class=" col-sm-offset-3 col-md-offset-2 main">
<h2 class="sub-header">添加帅仔</h2>
<form action="/shuaiges/new" method="post">
<div class="form-group">
<label for="">姓名</label>
<input type="text" class="form-control" name="name" required minlength="2" maxlength="10">
</div>
<div class="form-group">
<label for="">qq</label>
<input class="form-control" type="number" id="" name="qq" required minlength="5" minlength="15">
</div>
<button type="submit" class="btn btn-default">提交</button>
</form>
</div>
</div>
</div>
</body>
</html>