5 NodeJS的模块系统

5.1 require导出

  • 执行被夹在模块中的代码
  • 得到被加载中模块的exports导出接口对象
  • require特点
    1. require优先从缓存中加载
    2. require路径
      • ./ 当前目录
      • ../ 上一级目录
      • /xxx根目录下某个文件(几乎不用)
      • d:/a/foo.js 绝对路径(几乎不用)
      • 首位/在这里表示当前文件模块所属磁盘根路径
    3. .js可以省略
  • 加载核心模块的本质也是本地文件
  • 核心模块文件已经被编译到二进制文件中了,我们只需要按照名字加载就可以了

下面的例子,main.js引用了a和b,a又引用了b
main.js

1
2
3
4
require ('./a')
// 优先从缓存中加载
// 有偶遇在a中已经加载过了,这里不会重复加载
require('./b')

a.js

1
console.log('a 被加载了')

b.js

1
console.log('b 被加载了')

结果是只输出一次b 被加载了,说明再次调用b时并未真正调用,而是调用的内存

5.2 exports导出

  • Node中是模块作用于,默认文件中所有的成员只在当前文件模块中有效
  • 对于希望可以被其他模块访问的成员,我们就需要吧这些公开的成员都挂载到exports接口对象中就可以了
  1. 导出多个成员
1
2
3
4
5
6
7
8
exports.a = 123
exports.b = 'hello'
exports.c = function() {
console.log('ccc')
}
exports.d = {
foo:'bar'
}
  1. 导出单个成员
1
module.exports = 'hello'

一下情况会覆盖

1
2
3
4
5
6
7
module.exports = 'hello'

// 这个后者会覆盖前者
moudle.exports = function(x,y) {
return x + y

}

5.3 exports和moudle.exports

我们可以这么理解

  • 在Node中,每个模块内部都有一个自己的module
  • 该moudle对象中,有一个成员叫exports
  • 并且在node中我们定义一个exports = moudle.exports,让这两个变量指向同一个内存块
  • 如果你需要对外导出成员,只需要把导出的成员赋到exports中
  • 默认在代码最后有一句:return moudle.exports
  • 这样谁require我,我就给谁moudle.exports

我们现在有两个js,a.js和b.js

1
2
var  fooExport = require('./b')
console.log(fooExport)

1
2
3
4
5
6
var foo = 'bar'
function add(x,y){
return x + y
}
exports = add //不会被导出
module.exports = foo// 这个才会被导出

5.4 模块加载规则

这里的模块加载主要是第三方模块

  • 凡是第三方模块都需要npm下载下来,只使用require(’包名’)的方式来进行加载
  • 并且没有任何一个第三方包和核心包名称相同
  • 模块加载的心路历程
    1. 先找到当前文件所处目录中的node_modules目录
    2. 找到node_modules/需要的包/package.json文件
    3. 找到node_modules/需要的包/package.json中的main属性
    4. main属性中就记录了需要的包的入口模块
    5. 如果没有main属性就会找node_modules/需要的包下的index.js文件
    6. 如果index.js也没有就会去上一级的node_modules去寻找规则同上
    7. 如果上上上很多级直到当前磁盘的跟目录还没找到则会报错
  • 每一个项目有且只有一个node_modules

5.5 package.json

每个模块(项目)都需要一个package.json文件,该文件配置了一些模块信息以及项目操作(启动,build等)以及包以来
这个文件可以通过npm init来初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"scripts":{
"start": "hexo server",
"clean":"hexo clean",
"build":"hexo generate",
"deploy":"hexo deploy"
},
"hexo": {
"version": "3.7.1"
},
"dependencies": {
"gitment": "0.0.3",
"hexo": "3.2.0",
"hexo-asset-image": "0.0.3",
"hexo-deployer-git": "0.3.1",
"hexo-generator-archive": "0.1.4",
"hexo-generator-category": "0.1.3",
"hexo-generator-index": "0.2.0",
"hexo-generator-json-content": "3.0.1",
"hexo-generator-tag": "0.2.0",
"hexo-renderer-ejs": "0.3.0",
"hexo-renderer-marked": "0.3.0",
"hexo-renderer-stylus": "0.3.1",
"hexo-server": "0.2.0"
}
}

  • scripts该项目的脚本命令,可以自己写
  • devDependencies:打包之前依赖
  • dependencies:打包之后也需要依赖
    其他的简单就不说了
  • 还有package-lock.json是依赖包的依赖
  • ^版本号,代表最低版本号,为了保持版本一致不建议使用

package.json和package-lock.json

npm5以前是不会有package-lock.json这个文件的
npm5以后才加入这个文件
当安装包的时候,npm都会生成或者更新package-lock.json这个文件。

  • npm5以后的版本安装包不需要加–save参数,它会自动保存依赖信息
  • package-lock.json这个文件会报错node_modules中所有包的信息(版本,下载地址)
    • 这样的话重新npm install的时候速度就可以提升
  • 从文件来看,有一个lock称之为锁
    • 这个lock是用来锁定版本的
    • 如果项目依赖了^1.1.1版本
    • 重新install其实会下载最新版本,而不是1.1.1
    • 这个package-lock.json文件的另一个作用就是锁定版本号,防止自动升级

5.6 模块构建npm

npm即代表一个网站,所有js包仓库
也代表一个命令
模块初始化:

npm init (npm init -y 快速生成)

安装所有包

npm install

安装单个包

npm install 包(@版本号) -s 打包之后也依赖 -d 打包之前依赖 -g 全局

删除包

npm uninstall 包(@版本号) -s 打包之后也依赖 -d 打包之前依赖 -g 全局

切换源地址

npm install 包 –registry 路径(修改这次下载)
npm config set registry 路径(永久修改)

查看npm信息

npm config list

安装淘宝镜像

npm install -g cnpm

6 Express框架

express基于 Node.js 平台,快速、开放、极简的 Web 开发框架,
它已经帮我们封装好了很多包,我们直接拿来用就可以了
express安装

npm install express


6.1 HELLO WORLD

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 0 安装
// 1 引用
var express = require('express')
// 2 创建你服务器的应用程序,就是原来的http.createServer
var app = express();
// 当服务器收到get请求的时候,执行处理回调函数
app.get('/',function(req,res){
res.send('hello world');
//传统的方法在这里依然可以使用
//res.write('hello world')
//res.end()
})
// 相当于server.listen
app.listen(3000,function() {
console.log('服务器已启动')
})

6.2 基本路由

用框架之前我们的代码很丑,各种if嵌套,现在我们可以平行的开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var express = require('express')
// 2 创建你服务器的应用程序,就是原来的http.createServer
var app = express();
app.get('/about',function(req,res){
res.send('你好,我是Express');
})
app.get('/',function(req,res){
res.send(`
<DOCTYPE html>
<html lang='en'>
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<h1>hello Express</h1>
</body>
</html>
`);
})
// 相当于server.listen
app.listen(3000,function() {
console.log('服务器已启动')
})

get:get请求处理

1
2
3
app.get('/',function() {
res.send('hello')
})

post:post请求处理

1
2
3
app.post('/',function() {
res.send('hello')
})

6.3 public公开资源

var app = express()
app.use(‘public’,express.static(‘./public/‘))

  • 第一个public相当于别名,你也可以叫a,不过再访问就需要/a/资源了
  • 第二个参数express.static(‘./public/‘),相当于真实的你要公开的资源目录
    我们在上一个代码基础上进行公开资源
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    var express = require('express')
    // 2 创建你服务器的应用程序,就是原来的http.createServer
    var app = express();
    app.use('public',express.static('./public/'))
    // 公开指定目录
    // 只要这样做了,你就可以直接通过/public/xx的方式访问public目录中的所有资源了
    app.get('/about',function(req,res){
    res.send('你好,我是Express');
    })
    app.get('/',function(req,res){
    res.send(`
    <DOCTYPE html>
    <html lang='en'>
    <head>
    <meta charset="UTF-8" />
    <title>Document</title>
    </head>
    <body>
    <h1>hello Express</h1>
    </body>
    </html>
    `);
    })
    // 相当于server.listen
    app.listen(3000,function() {
    console.log('服务器已启动')
    })

6.4 nodemon热更新

对于nodejs我们每次编译之后都需要重启项目,很烦啊。我们可以用nodemon工具来启动node项目,实现热更新

  1. 安装全局 npm install –g nodemon
  2. 用nodemon启动项目 nodemon app.js

6.5 express重写留言板

express提供了一个操作art-template的插件
我们首先安装插件

npm install –save art-template
npm install –save express-art-template

  • 说明:
    • app.engine('art',require('express-art-template')),第一个参数,表示当渲染以.art结尾的文件的时候(如果是.html则像我这样写成.html)
    • express-art-template是专门用来在Express中把art-template整合到Express中
    • 虽然这里不需要引用art-template,但是也必须先安装,原因就在于express-art-template依赖了art-template
    • express为Response相应对象提供了一个方法:render,render方法默认是不可以使用的,但是如果配置了模板引擎就可以使用了
    • res.render('html模板名',{模板数据}), 第一个参数不能写路径,默认会去项目中的views目录查找该模板文件
    • 也就是说Express有一个约定:开发人员把所有的视图文件都放到views目录中,如果在views中还有一个文件夹(admin),则res.render(‘admin/index.html’)
    • 如果想要修改默认的views目录可以这么写,app.set('views',新的路径)
  • 获取post信息
    • 安装插件 npm install --save body-parser
    • 引包 var bodyParser = require('body-parser')
    • 配置body-parser
      • app.use(bodyParser.urlencoded({extended:false}))
      • app.use(bodyParser.json())
    • 只要加入了这个配置,则在req请求对象上会多出来一个属性:body,也就是说可以直接通过req.body来获取数据
      我们html页面还是之前的页面,只是修改了nodejs,如下
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      var express = require('express')
      // post中间件
      var bodyParser = require('body-parser')
      var app = express()
      app.use('/public/',express.static('./public/'))
      var comments = [
      {
      name:"张三",
      message: '今天天气不错',
      dataTime: '2015-10-16',
      },
      {
      name:"张三2",
      message: '今天天气不错',
      dataTime: '2015-10-16',
      },
      {
      name:"张三3",
      message: '今天天气不错',
      dataTime: '2015-10-16',
      },
      ]
      // 配置使用art-template模板引擎
      // 使用art-template模板引擎
      app.engine('html',require('express-art-template'))
      // 配置body-parser中间件
      app.use(bodyParser.urlencoded({extended:false}))
      app.use(bodyParser.json())

      app.post('/pinglun',function(req,res) {
      var comment = JSON.stringify(req.body,null,2)
      comment.dateTime = '2018-12-12'
      comments.unshift(comment)
      res.redirect('/')
      // 相当于 res.statusCode = 302 ; res.setHeader('Location','/')
      })

      app.get('/',function(req,res) {
      res.render('index.html',{
      comments:comments
      })
      })
      app.get('/pinglun',function(req,res) {
      var comment = req.query
      comment.dateTime = '2018-12-12'
      comments.unshift(comment)
      res.redirect('/')
      // 相当于 res.statusCode = 302 ; res.setHeader('Location','/')
      })
      // 对于post请求我们需要用一个中间件body-parser

      app.get('/post',function(req,res){
      res.send('post page')
      })
      app.listen(3000,function(){
      console.log('runing...')
      })

7 Express管理系统

7.1 系统数据和页面

我们这里新建一个.json来配置数据,成为我们的数据库

db.json

1
2
3
4
5
6
7
8
9
10
{
"students":[
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"},
{"id":1,"name":"张三","gender":0,"age":18,"hobbies":"吃饭睡觉打豆豆"}
]
}

建立HTML列表模板代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>爱好</th>
</tr>
</thead>
<tbody>
{{each students}}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{{ / each }}
</tbody>
</table>

7.2 服务端NodeJs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var fs = require('fs')
var express = require('express')


var app = express()
app.use('node_modules',express.static('./node_modules/'))
app.use('public',express.static('./public/'))

app.engine('html',require('express-art-template'))

app.get('/',function(req,res) {
// readFile的第二个参数是可选的,传入utf-8就是告诉它把读取到的文件直接按照uft-8编码
// 除了这样来转换之外,也可以通过data.toString()的方式
fs.readFile('./db.json','utf8',function(err,data) {
if(err){
return res.status(500).send('server error')
}
console.log(data)
})
res.render('index.html',{
students:Json.pares(data).students
})
})

app.listen(3000,function() {
console.log('runing。。。。')
})

7.3 系统路由

基于系统我们设计的路由列表

请求方法 请求路径 get 参数 post参数 备注
GET /students 渲染列表页面
GET /students/new 渲染添加学生页面
POST /students name,age,gender,bobbies 处理添加学生请求
GET /students/edit id 渲染编辑页面
POST /students/edit id,name,age,gender,bobbies 处理编辑请求
GET /students/delete id 处理删除请求

我们现在想把router路由单独拿出来
app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var express = require('express')
var router = require('./router')

var app = express()
app.use('node_modules',express.static('./node_modules/'))
app.use('public',express.static('./public/'))

app.engine('html',require('express-art-template'))

router(app)

app.listen(3000,function() {
console.log('runing。。。。')
})

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var fs = require('fs')

module.exports = function(app) {
app.get('/students',function(req,res) {
// readFile的第二个参数是可选的,传入utf-8就是告诉它把读取到的文件直接按照uft-8编码
// 除了这样来转换之外,也可以通过data.toString()的方式
fs.readFile('./db.json','utf8',function(err,data) {
if(err){
return res.status(500).send('server error')
}
console.log(data)
})
res.render('index.html',{
students:Json.pares(data).students
})
})

app.get('/students/new',function(req,res) {})

app.post('/students',function(req,res) {})

app.get('/students/edit',function(req,res) {})

app.post('/students/edit',function(req,res) {})

app.get('/students/delete ',function(req,res) {})
}

对于上面的代码,Express提供了一种更好的方式专门用来包装路由的

var router = express.Router()

app.js

  • 作为app.js职责
    • 创建服务
    • 做一些相关配置
    • 模板引擎
    • body-parser解析表单post请求体
    • 提供静态资源服务
    • 挂载路由
    • 监听端口服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var express = require('express')
var router = require('./router')

var app = express()
app.use('node_modules',express.static('./node_modules/'))
app.use('public',express.static('./public/'))

app.engine('html',require('express-art-template'))

// 把路由容器挂在到APP服务中
app.use(router)

app.listen(3000,function() {
console.log('runing。。。。')
})

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var fs = require('fs')
// 1. 创建一个路由容器
var router = express.Router()

// 2. 把路由都挂载到router路由容器中
router.get('/students',function(req,res) {
// readFile的第二个参数是可选的,传入utf-8就是告诉它把读取到的文件直接按照uft-8编码
// 除了这样来转换之外,也可以通过data.toString()的方式
fs.readFile('./db.json','utf8',function(err,data) {
if(err){
return res.status(500).send('server error')
}
console.log(data)
})
res.render('index.html',{
students:Json.pares(data).students
})
})

router.get('/students/new',function(req,res) {
res.render('new.html')
})

router.post('/students/new',function(req,res) {})

router.get('/students/edit',function(req,res) {})

router.post('/students/edit',function(req,res) {})

router.get('/students/delete ',function(req,res) {})

// 3. 导出router
module.exports = router;

7.4 添加页面

在上面HTML基础上我们需要添加一个《添加》按钮来跳转到添加页面

列表页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<h2 class="sub-header">Section title</h2>
<a class="btn btn-success" href="/students/new">添加学生</a>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>爱好</th>
</tr>
</thead>
<tbody>
{{each students}}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
{{ / each }}
</tbody>
</table>
</div>

添加学生页面
new.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h2 class="sub-header">添加学生</h2>
<form action="/students/new" method="post">
<div class="form-group">
<label for="">姓名</label>
<input type="text" class="form-control" id="" name="name" placeholder="请输入姓名">
</div>
<div class="form-group">
<label for="">性别</label>
<label class="radio-inline">
<input class="radio" name="gender" id="inlineRadio1" value="0">
</label>
<label class="radio-inline">
<input class="radio" name="gender" id="inlineRadio1" value="1">
</label>
</div>
<div class="form-group">
<label for="">年龄</label>
<input type="number" class="form-control" id="" name="age" placeholder="请输入年龄">
</div>
<div class="form-group">
<label for="">爱好</label>
<input type="text" class="form-control" id="" name="hobbies">
</div>
<button type="submit" class="btn btn-default">添加</button>
</form>
</div>

app.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
var express = require('express')
var router = require('./router')
var bodyParser = require('body-parser')

var app = express()
app.use('node_modules',express.static('./node_modules/'))
app.use('public',express.static('./public/'))

app.engine('html',require('express-art-template'))

// 配置模板引擎和body-parser一定要在app.use(router)挂在路由之前
// parse application/x-www-forn-urlencoded
app.use(bodyParser.urlencode({extended:false}))
// parse application/json
app.use(bodyParser.json())

// 把路由挂在到app下
router(app)

app.listen(3000,function() {
console.log('runing。。。。')
})

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var fs = require('fs')
var router = express.Router()

router.get('/students',function(req,res) {
fs.readFile('./db.json','utf8',function(err,data) {
if(err){
return res.status(500).send('server error')
}
console.log(data)
})
res.render('index.html',{
students:Json.pares(data).students
})
})

router.get('/students/new',function(req,res) {
res.render('new.html')
})

router.post('/students/new',function(req,res) {
// 1.获取表单数据
// 2.将数据保存到db.json文件中以持久化
// 先读取出来转换成对象
// 往对象中push数据
// 把对象转换为字符串
// 把字符串再次写入文件
// 3.发送相应
})

router.get('/students/edit',function(req,res) {})

router.post('/students/edit',function(req,res) {})

router.get('/students/delete ',function(req,res) {})

// 3. 导出router
module.exports = router;

为了下面讲解方便,我们先来说一下回调函数

7.5 回调函数

1
2
3
4
5
6
7
8
function fn() {
setTimeout(function() {
var data = 'hello'
return data
},1000)
}

console.log(fn())

通过以上代码我们输出的结果是undifand
原因是异步,我们已经输出结果的时候data还没有赋值,里面的异步方法还没有调用
如果我们需要获取其中的一步操作,必须通过回调函数来获取

1
2
3
4
5
6
7
8
9
10
function fn(callback) {
setTimeout(function() {
var data = 'hello'
callback(data)
},1000)
}

fn(function(data) {
console.log(data)
})

这里的fn参数就是一个函数(回调函数callback),在异步延迟之后调用(callback(data))

7.6 文件操作提取

为了我们代码高重用性,我们把对文件(数据)的操作单独提取出来
它的职责是只对文件操作,并不关心业务如何

students.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
var fs = require('fs')


// 定义文件路径
var dbPath = './db.json'
/**
* 获取所有学生列表
* callback中的参数
* 第一个参数是err
* 成功是null
* 错误对象是错误对象
* 第二个参数是结果
* 成功是数组
* 错误是undefined
* return[]
*/
exports.find = function(callback) {
fs.readFile(dbPath,'utf8',function(err,data) {
if(err){
return callback(err)
}
callback(null,Json.parse(data).students)
})
}

/**
* 添加保存学生
*/

exports.save = function(student, callback) {
fs.readFile(dbPath,'utf8',function(err,data) {
if(err){
return callback(err)
}
var students = JSON.parse(data).students
// 处理id唯一不重复
student.id = students[students.length - 1].id + 1;
// 把用户传递的对象数据保存到数组中
students.push(student)
// 把对象数据转换成为字符串
var fileData = Json.stringify({
students:students
})
// 把字符串保存到文件中
fs.writeFile(dbPath,fileData,function(err) {
if(err) {
// 错误就把错误对象传递给前面
return callback(err)
}
// 成功就没有错,所以错误对象是null
callback(null)
})
})
}

/**
* 查询特定学生
*/
exports.findById = function(id,callback){
fs.readFile(dbPath,'utf8',function(err,data) {
if(err){
return callback(err)
}
var students = Json.parse(data).students
var ret = students.find(function(item) {
return item.id === parseInt(id)
})
callback(null,ret)
})
}

/**
* 更新学生
*/

exports.updateById = function(student, callback) {
fs.readFile(dbPath,'utf8',function(err,data) {
if(err){
return callback(err)
}
var students = Json.parse(data).students
// 把student.id统一转换成数字类型
student.id = parseInt(student.id)
// 需要修改那个,就把哪个找出来
// ES6中的一个数组方法find
// 需要接受一个函数作为参数
// 当某个便利符合 item.id === student.id 条件的时候find会终止便利,同时返回遍历到的实例
var stu = students.find(function(item) {
// 保证写入到文件中的id类型为数字
return itme.id === parseInt(student.id)
})
// 遍历拷贝对象
for(var key in student){
stu[key] = student[key]
}
// 把对象数据转换成字符串
var fileData = JSON.stringify({
students:students
})
// 把字符串保存到文件中
fs.writeFile(dbPath,fileData,function(err) {
if(err) {
// 错误就把错误对象传递给前面
return callback(err)
}
// 成功就没有错,所以错误对象是null
callback(null)
})
})
}

/**
* 删除学生
*/

exports.deleteById = function(student, callback) {
fs.readFile(dbPath,'utf8',function(err,data) {
if(err){
return callback(err)
}
var students = Json.parse(data).students

//findInex方法专门用来根据条件查询元素的下标
var deleteId = students.findIndex(function(item) {
return item.id === parseInt(id)
})
// 根据下标从数组中删除对应的学生对象
students.splice(deleteId)
// 把对象数据转换成字符串
var fileData = JSON.stringify({
students:students
})
// 把字符串保存到文件中
fs.writeFile(dbPath,fileData,function(err) {
if(err) {
// 错误就把错误对象传递给前面
return callback(err)
}
// 成功就没有错,所以错误对象是null
callback(null)
})
})
}

router.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
var fs = require('fs')
var router = express.Router()
var Student = require('./students')

router.get('/students',function(req,res) {
Student.find(function(err,students) {
if(err){
return res.status(500).send('服务器错误')
}
students:students
})
})

router.get('/students/new',function(req,res) {
res.render('new.html')
})

router.post('/students/new',function(req,res) {
// 1.获取表单数据
// 2.将数据保存到db.json文件中以持久化
// 先读取出来转换成对象
// 往对象中push数据
// 把对象转换为字符串
// 把字符串再次写入文件
// 3.发送相应
Student.save(req.body,function(err) {
if(err) {
return res.status(500).send('Server error')
}
res.redirect('/students')
})
})

router.get('/students/edit',function(req,res) {
// 1.在客户端的列表中处理连接问题(需要有id参数)
// 2.获取要编辑的学生id
// 3.渲染编辑页面
// 根据id把学生信息查询出来
// 使用模板引擎渲染页面
Student.findById(req.query.id,function(err,student) {
if(err){
return res.status(500).send('Server error')
}
res.render('edit.html',{
student:student
})
})
})

router.post('/students/edit',function(req,res) {
// 1. 获取表单数据
// req.body
// 2. 更新
// Student.updateById()
// 3.发送响应
Student.updateById(req.body,function(err,student) {
if(err){
return res.status(500).send('Server error')
}
res.redirect('/')
})
})

router.get('/students/delete ',function(req,res) {
// 1.获取要删除的id
// 2.根据id执行删除操作
// 3.根据操作结果发送相应数据
Student.deleteById(id,function(err) {
if(err){
return res.status(500).send('Server error')
}
res.redirect('/students')
})
})

// 3. 导出router
module.exports = router;

列表页面(添加一个编辑按钮和隐藏域)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<h2 class="sub-header">Section title</h2>
<a class="btn btn-success" href="/students/new">添加学生</a>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>姓名</th>
<th>性别</th>
<th>年龄</th>
<th>爱好</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{{each students}}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<a href="/students/edit?id={{$value.id}}">编辑</a>
<a href="/students/delete?id={{$value.id}}">删除</a>
</td>
</tr>
{{ / each }}
</tbody>
</table>
</div>

修改学生页面(添加个隐藏域,然后就是给表单中的input添加默认值)
edit.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<h2 class="sub-header">修改学生</h2>
<form action="/students/new" method="post">
<!--用来存放一些不希望客户看到的,但是需要被提交到服务器端的数据-->
<input type="hidden" value="{{student.id}}" name="id">
<div class="form-group">
<label for="">姓名</label>
<input type="text" class="form-control" id="" name="name" placeholder="请输入姓名" value="{{student.name}}">
</div>
<div class="form-group">
<label for="">性别</label>
<label class="radio-inline">
<input class="radio" name="gender" id="inlineRadio1" value="0">
</label>
<label class="radio-inline">
<input class="radio" name="gender" id="inlineRadio1" value="1">
</label>
</div>
<div class="form-group">
<label for="">年龄</label>
<input type="number" class="form-control" id="" name="age" placeholder="请输入年龄" value="{{student.age}}">
</div>
<div class="form-group">
<label for="">爱好</label>
<input type="text" class="form-control" id="" name="hobbies" value="{{student.hobbies}}">
</div>
<button type="submit" class="btn btn-default">添加</button>
</form>
</div>

7.7 总结一下编写的步骤

  • 处理模板
  • 配置开放静态资源
  • 配置模板引擎
  • 简单路由:/students渲染静态页面出来
  • 路由设计
  • 提取路由模块
  • 由于接下来一些列业务都需要处理数据文件,所以我们自己封装students.js
  • 先写好student.js文件结构
    • 查询所有学生列表
    • findById
    • save
    • updataById
    • delereById
  • 实现具体功能
    • 通过路由接收请求
    • 接收请求中的数据(get,post)
      • req.query
      • req.body
    • 调用数据操作API处理数据
    • 根据操作结果给客户的发送响应
  • 业务功能属性
    • 列表
    • 添加
    • 编辑
    • 删除

最后更新: 2018年12月24日 10:09

原始链接: http://linjiad.github.io/2018/12/16/NodeJs2/

× 请我吃糖~
打赏二维码