Nodejs_04
4.1 文件操作路径已经模块标识中的路径
文件操作中的相对路径可以省略 ./
fs.readFile('data/a.txt',function{
if(err){
return console.log('读取失败')
}
console.log(data.toString())
})
模块加载中的,相对路径中的./不能省略
require('./data/foo.js')
结果会先执行foo.js中的内容再输出a.txt的data,因为所有文件操作的API都是异步执行的
4.2 express helloworld
安装
npm init --y
npm install --save express
//or 简写
npm i -S express
hello world
var express = require('express')
//1. 创建app
var app = express()
app.get('/',function(req,res){
//res.end('hello world')
res.send('hello world')
})
app.listen(3000,function(){
console.log('express app is running')
})
4.3 修改完代码让服务器自动重启
我们可以使用一个第三方命令行工具, nodemon
来邦之我们解决频繁修改代码重启服务器问题nodemon
是一个基于Node.js开发的第三方命令行工具,我们使用的时候需要独立安装
npm install --global nodemon
安装完毕后使用
node app.js
#安装之后只需要输入
nodemon app.js
只要是通过nodemon app.js
启动的服务器,则它会监视你的文件变化,当文件发生变化的时候,自动会帮你重启服务器
4.4 基本路由
路由器(由一根线(WAN)连进来的网络分发到多根线(LAN)中)
路由就是一张表,这个表里面有居然提的映射关系
路由器
- 请求方法
- 请求路径
- 请求处理函数
get
//当你以GET方法请求 / 的时候,执行对应的处理函数
app.get('/',function(req,res){
res.send('hello world')
})
post
//当你以POST方法请求 / 的时候,指定对应的处理函数
app.post('/',function(req,res){
res.send('Got a POST request')
})
4.5 静态服务
var express = require('express')
var app = express()
//当以/public/开头的时候,去./public/目录中找对应资源
app.use('/public/',express.static('./public/'))
//当省略第一个参数的时候,则可以通过省略/public的方式来访问
app.use(express.static('./public/'))
//就比如说要访问./public/login,就直接通过http://127.0.0.1:3000/login就可以访问了,加上public反而不可以
app.use('/a/',express.static('./public/'))
//比如要访问./public/login,就只能通过http://127.0.0.1:3000/a/login来访问
app.get('/',function(req,res){
res.send('hello world')
})
app.listen(3000,function(){
console.log('express app is running')
})
如果在文件中有两个
app.use(express.static('./public2/'))
app.use(express.static('./public/'))
//那么在执行的时候只会执行第一个行
//如果在第一行的public2中没有指定文件那么再在第二行的路径中找是否有文件,再执行
app.use('/public/',express.static('./public/'))
app.use('/public2/',express.static('./public2/'))
//如果是这样写就可以通过url中输入的名字进行区分
4.6 在express中配置art-template模板引擎
var express = require('express')
var app = express()
app.use('/public/',express.static('./public/'))
//配置使用art-template模板引擎
//第一个参数表示,当渲染以.art结尾的文件的时候,使用art-template模板引擎,虽然这里不需要加载art-template但是也必须安装,因为express-art-tempalte依赖了art-template
app.engine('html',require('express-art-template'))
//express为response响应对象提供了一个方法:render
//res.render('html模板名',{模板数据})
//第一个参数不能写路径,默认会去项目中的views目录查找该模板文件
//也就是说express有一个约定:开发人员把所有的试图文件都放在views目录中
//如果想要修改view目录也可以
//app.set('views',render函数的默认路径)
app.get('/',function(req,res){
res.render('404.html')
})
app.get('/admin',function(req,res){
res.render('admin/index.html',{
title:'管理系统'
})
})
app.get('/',function(req,res){
res.sned('/ page')
})
app.get('/post',function(req,res){
res.send('post page')
})
app.listen(3000,function(){
console.log('running...')
})
npm install --save art-template
npm install --save express-art-template
配置
app.engine('html',require('express-art-template'))
使用
app.get('/',function(req,res){
res.render('index.html',{
title:'hello world'
})
})
如果希望修改默认的views
渲染存储目录可以
app.set('views',目录路径)
4.7 利用express重写留言板案例
var express = require('express')
var app = express()
app.use('/public/',express.static('./public/'))
app.engine('html',require('express-art-template'))
var comments = [{
name: '张三',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三2',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三3',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三4',
message: '今天天气不错!',
dateTime: '2015-10-16'
},
{
name: '张三5',
message: '今天天气不错!',
dateTime: '2015-10-16'
}
]
app.get('/',function(req,res){
res.render('index.html',{
comments:comments
})
})
app.get('/post',function(req,res){
res.render('post.html')
})
//当以POST请求/post的时候,执行指定的处理函数
//这样的话我们就可以利用不同的请求方法让一个请求路径使用多次
app.post('/post',function(req,res){
//console.log('收到表单post请求了')
//req.query只能拿到get参数
var comment = req.body
comment.dateTime = '2017-11-5 10:58:51'
comments.unshift(comment)
//res.send
//res.redirect
//这些方法Express会自动结束响应
res.redirect('/')
//res.statusCode = 302
//res.setHeader('Location','/')
})
//app.get('/pinglun',function(req,res){
// var comment = req.query
// comments.datetime = '2017-11-5 10:58:51'
// res.statusCode = 302
// res.setHeader('Location','/')
// console.log(req.query)
//})
app.listen(3000,function(){
console.log('running...')
})
4.8 在express中如何获取post的数据
在express中没有内置获取表单POST请求体的内置API
安装body parser插件
npm install body-parser --save
配置
var express = require('express')
//0.引包
var bodyParser = require('body-parser')
var app = express()
//配置 body-parser
//只要加入这个配置,则在req请求对象上会多出来一个属性:body
//也即是书欧尼可以直接通过req.body来获取表单POST请求体数据
//配置body-parser中间件(插件,专门用来解析表单POST请求体 )
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.use(function(req,res){
res.serHeader('Content-Type','text/plain')
res.write('you posted:\n')
//可以通过req.body来获取表单POST请求题数据
res.end(JSON.stringify(req.body,null,2))
})
接收到req.body的显示
4.9 crud起步
getbootstrap.com
getbootstrap.com中文网
模板
4.10 路由设计
请求方法 | 请求路径 | get参数 | post参数 | 备注 |
---|---|---|---|---|
GET | /students | 渲染首页 | ||
GET | /students/new | 渲染添加学生页面 | ||
POST | /students | name,age,gender,hobbies | 处理添加学生请求 | |
GET | /students/edit | id | 渲染编辑页面 | |
POST | /students/edit | di,name,age,gender,hobbies | 处理编辑请求 | |
GET | /students/delete | id | 处理删除请求 |
Mac用option可以多行编辑
/**
app.js 入门模块
职责:
创建服务
做一些服务相关配置
模板引擎
body-parser解析表单post请求体
提供静态资源服务
挂载路由
监听端口启动服务
**/
var express = require('express')
var router = require('./router.js')
var app = express()
var fs = require('fs')
var bodyParser = require('body-parser')
//配置模板引擎和body-parser一定要在app.use(router)挂载路由之前
app.use(bodyParser.urlencoded({extended:false}))
app.use(bodyParser.json())
app.engine('html',require('express-art-template'))
app.use('/node_modules/',express.static('./node_modules/'))
app.use('/public/',express.static('./public/'))
//把路由器挂载在app上
app.use(router)
app.listen(8000,function(){
console.log('running 3000')
})
/**
router.js路由模块
职责:
处理路由
根据不同的请求方法+请求路径设置具体的请求函数
把路由都挂载到router路由容器中
模块职责要单一
我们划分模块的目的就是为了增强项目代码效率
**/
var fs = require('fs')
var Student = require('./student')
var express = require('express')
var router = express.Router()
//创建一个路由容器,把需要的函数都挂载在router上,最后把router导出
router.get('/',function(req,res){
//readFile的第二个参数是可选的,传入utf-8就是告诉他,把读取到的文件直接按照utf-8编码
fs.readFile('./db.json','utf-8',function(err,data){
if (err) {
return res.status(500).send('Server error')
}
var students = JSON.parse(data).students
res.render('index.html',{
fruits:[
'苹果',
'香蕉',
'橘子'
],
students: students
})
})
})
router.get('/students',function(req,res){
Student.find(function(err,students){
if (err){
return res.status(500).send('Server error.')
}
res.render('index.html',{
fruits:[
'苹果',
'香蕉',
'橘子'
],
students: students
})
})
})
router.get('/students/new',function(req,res){
res.render('new.html')
})
router.get('/students/edit',function(req,res){
})
router.get('/students/delete',function(req,res){
})
router.post('/students/new',function(req,res){
//1.获取表单数据
//2.处理
//讲数据保存到db.json文件中用以持久化
//3.发送响应
//怎么改文件里面的数据
//先把文件里的字符串读取出来,转成对象
//然后往对象中push数据
//然后把对象转成字符串
//然后把字符串再次写入文件
//
Student.save(req.body,function(err){
if (err){
return res.status(500).send('Server error')
}
res.redirect('/students')
})
})
router.post('/students/edit',function(req,res){
})
module.exports = router
设计操作文件的API
var fs = require('fs')
var dbPath = './db.json'
/**
数据操作文件模块
职责:操作文件中的数据,只处理数据,不关心业务
*/
/**
获取所有学生列表
callback中的参数
第一个参数是error
成功是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)
}
callback(null)
})
})
}
/**
删除学生
*/
exports.delete = function(){
}
/**
更新学生
*/
exports.updateById = function(student, callback){
fs.readFile(dbPath, 'utf8', function(err,data){
if (err){
return callback(err)
}
var students = JSON.parse(data).students
//你要修改谁就要先把谁找出来
//EcmaScript 6 中的一个数组方法:find
//需要接收一个函数作为参数
//当某个遍历项符合 item.id === student.id 条件的时候,find会终止遍历,同时返回遍历项
var stu = students.find(function(item){
return item.id === 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)
}
callback(null)
})
})
}
4.11 封装异步API
function fn(callback){
//相当于有
//var callback = data
setTimeout(function(){
var data = 'hello'
callback(data)
}, 1000)
//var data = hello
//return data
}
//调用fn,得到内部的data
//因为是异步的,程序不会等待fn()执行结束就会执行后面的代码
//凡是要得到一个函数中异步操作结果,则必须通过回调函数来获取
fn(function(data){
console.log(data)
})
如何理解回调函数