Danmo的学习之路(express)

文章目录

  • 基本感知(P48)
  • 路径问题(P49)
  • 基本路由(P54)
  • 静态服务(P54)
  • 配置使用模板引擎(P55)
  • express重写留言本(P56)
  • 配置post(P57)
  • 从文件中读取数据(P59)
  • 设计路由(P61)
  • 路由模块的提取(P62)
  • 封装数据操作文件模块
    • find(P64)
    • save(P65)
    • 渲染编辑学生页面(P67)
    • 更新功能-总结(P68)
    • 删除(P69)
  • 封装ajax方法(P72)
  • package-lock文件的作用(P74)

基本感知(P48)

let express = require('express');	//引包

let app = express();	//创建服务器应用程序

// 相当于 server.listen
app.listen(3000, function () {
     
  console.log('app is running at port 3000.')
})

无需判断多重if,不管先后次序,直接使用app.js方法

不需要设置响应内容类型,也不担心中文出现乱码

app.get('/', function (req, res) {
     
  // 在 Express 中可以直接 req.query 来获取查询字符串参数
  console.log(req.query)
  res.send('你好,我是 Express!')
})
app.get('/post', function (req, res) {
     
  res.send('我可以发送评论!')
})

app.get('/hello', function (req, res) {
     
	  res.send(`
		
			
			  	
			    	
			    	Document
			  	
				
			  		

Hello Express!

`
) }) //无需 res.setHeader('Content-Type','text/html;charset=utf-8');

开放某一路径下的资源只需要一句代码:

app.use('/public/', express.static('./public/'))

一旦这么做了,可以直接通过 /public/xxx 的方式(直接输入url)访问 public 目录中的所有资源

路径问题(P49)

文件操作路径:

/*
在文件操作的相对路径中:
./data/a.txt 相对于当前目录
data/a.txt 相对于当前目录
/data/a.txt 绝对路径,当前文件模板所处磁盘根目录(c:/ 或d:/)
*/
fs.readfile('./data/a.txt', function (err, data) {
     
	if(err) {
     
		return console.log(‘读取失败’);
	}
	console.log(data.toString());
})

模块操作路径:只能用相对路径,且不能省略./,否则报错

require('./data/foo.js');

/*
在foo.js中写console.log('foo');
则运行app.js,会输出foo
*/

基本路由(P54)

把app.js中的app.get和app.post提出来,放在和app.js同一目录下的router.js中

app
//可以链式调用
	.get('/',function() {
     })	//当以get方法请求/的时候,执行对应的处理函数
	.get('/get',function() {
     })
	.post('/post', function() {
     }) //当以post方法请求/post的时候,执行对应的处理函数
	

静态服务(P54)

三种方式:

app.use(express.static('./public/'));
//1.不加第一个参数,开放/public/,访问时直接加/public下的文件名,不需要加/public
//比如/public/js/01.js(文件中的目录),当开放了/public/后,访问时url为127.0.0.1/js/01.js

app.use('/public/', express.static('./public/'));
//2.访问时需要在最前面加上/public
//比如/public/js/01.js,当开放了/public/后,访问时url为127.0.0.1/public/js/01.js
//这种方式最常用

app.use('/a/b/', express.static('./public/'));
//2.访问时需要在最前面加上/a/b(相当于给/public 起的别名)
//比如/public/js/01.js,当开放了/public/后,访问时url为127.0.0.1/a/b/js/01.js

配置使用模板引擎(P55)

下载:

npm install --save art-template
npm install --save express-art-template
(第二个包会依赖第一个包)

配置:

app.engine('html', require('express-art-template'));
//第一个参数,表示当渲染以 .html结尾的文件的时候,使用art-template模板引擎
app.get('/', function (req, res) {
     
	res.render('admin.html',{
     
		title:'管理系统'
	});
	//只有配置了模板引擎时,render方法才可以使用
	//res.render('html模板名',{模板数据})
	//第一个参数不能写路径,默认会去项目的views目录查找该模板文件(约定)
})

admin.html:

<h1>admin {
    { title }}h1>

如此一来,渲染后页面会显示:admin 管理系统

express重写留言本(P56)

此处用到了redirect这个API

let express = require('express');
let app = express();
app.use('/public/', express.static('./public/'));
app.engine('html', require('express-art-template'));
let comments = [
  {
     
    name: '淡漠',
    message: '前端加油!',
    dateTime: '2021.3.7 22:59:41'
  },
  {
     
    name: '和谐创新',
    message: '前端加油!',
    dateTime: '2021.3.6 12:37:07'
  },
  {
     
    name: '峻哥',
    message: '前端加油!',
    dateTime: '2021.3.5 08:45:23'
  },
  {
     
    name: '婷婷',
    message: '前端加油!',
    dateTime: '2021.3.4 18:05:11'
  }
]
app.get('/', function (req, res) {
     
  res.render('index.html', {
     
    comments: comments
  })
})
app.get('/post', function (req, res) {
     
  res.render('post.html');
})
app.get('/pinglun', function (req, res) {
     
  let comment = req.query;
  comments.unshift(comment);
  res.redirect('/');
  // 同时修改状态码和重定向
})
app.listen(3000, function() {
     
  console.log('服务器启动成功,可以通过 http://127.0.0.1:3000/ 来进行访问');
})

配置post(P57)

  • 下载中间件:
npm install --save body-parser
  • 修改html文件中表单的method
  • 修改app.js:
let express = require('express');
let bodyParser = require('body-parser');	//引包
let app = express();
app.use('/public/', express.static('./public/'));
app.use(bodyParser.urlencoded({
      extende: false}));	//此处
app.engine('html', require('express-art-template'));
let comments = [
  {
     
    name: '淡漠',
    message: '前端加油!',
    dateTime: '2021.3.7 22:59:41'
  },
  {
     
    name: '和谐创新',
    message: '前端加油!',
    dateTime: '2021.3.6 12:37:07'
  },
  {
     
    name: '峻哥',
    message: '前端加油!',
    dateTime: '2021.3.5 08:45:23'
  },
  {
     
    name: '婷婷',
    message: '前端加油!',
    dateTime: '2021.3.4 18:05:11'
  }
]
app.get('/', function (req, res) {
     
  res.render('index.html', {
     
    comments: comments
  })
})
app.get('/post', function (req, res) {
     
  res.render('post.html');
})
app.post('/post', function (req, res) {
     
  let comment = req.body;	//此处不用query,而是body
  comments.unshift(comment);
  res.redirect('/');
})
app.listen(3000, function() {
     
  console.log('服务器启动成功,可以通过 http://127.0.0.1:3000/ 来进行访问');
})

从文件中读取数据(P59)

db.json文件内容:

"students": [
	{
     "id": 1, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"},
	{
     "id": 2, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"},
	{
     "id": 3, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"},
	{
     "id": 4, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"},
	{
     "id": 5, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"},
	{
     "id": 6, "name": "张三", "gender": 0, "age": 18, "hobbies": "game、music、learning"}
]

html文件内容:

<tbody>
	{
    { each students }}
	<tr>
		<td>{
    { &value.id }}td>
		<td>{
    { &value.name }}td>
		<td>{
    { &value.gender }}td>
		<td>{
    { &value.age }}td>
		<td>{
    { &value.hobbies }}td>
	tr>
	{
    { /each }}
tbody>

app.js文件内容:

let fs = require('fs');
let express = require('express');
let app = express();
app.use('/node_modules/', express.static('./node_modules'));
app.use('/public/', express.static('./public'));
app.engine('html', require('express-art-template'));

fs.readFile('./db.json', 'utf8', function (err, data) {
     
//此处可以加第二个参数(可选)来确定编码方式
	if(err) {
     
		return res.status(500).send('Server error');
	}
	let students = JSON.parse(data).students;
	//从文件里读取到的数据是字符串,这里一定要手动转成对象
	res.render('index.html', {
     
		students: students
	})
})
//老师演示时用到了bootstrap,而它在node_modules里边,因此需要开放这个目录

这样,当修改.json文件时的数据并保存时,网页刷新即可看到修改后的结果

设计路由(P61)

一开始设计好路由表,大概长这样:

Danmo的学习之路(express)_第1张图片

路由模块的提取(P62)

以前把所有内容都写在app.js里,现在要提取模块了

app.js 入口模块
职责:

  • 创建服务
  • 做一些服务相关配置:模板引擎、body-parser解析表单和post请求体、提供静态资源服务
  • 挂载路由
  • 监听端口启动服务

router.js 路由模块
职责:

  • 处理路由
  • 根据不同请求方法,请求路径设置具体的请求处理函数

router.js文件:

let fs = require('fs');	//由于router.js用到了fs的模块的方法,所以还要加载模块
let router = express.Router();	//创建路由容器

router.get('/students', function (req, res) {
     });
module.exports = router;	//把router导出

app.js文件:

let router = require('./router');
router(app);

封装数据操作文件模块

修改补充index.html:

div>
<h2 class="sub-header">Section titleh2>
<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>{
    { $value.id }}td>
        <td>{
    { $value.name }}td>
        <td>{
    { $value.gender }}td>
        <td>{
    { $value.age }}td>
        <td>{
    { $value.hobbies }}td>
        <td>
          <a href="/students/edit?id={
      { $value.id }}">编辑a>
          <a href="/students/delete?id={
      { $value.id }}">删除a>
          
        td>
      tr>
      {
    { /each }}
    tbody>
  table>
div>

find(P64)

数据操作文件模块,职责是:操作文件中的数据,只处理数据,不关心业务

这样就不需要自己写读文件函数(以find为例),直接用该模块内封装好的函数即可

student.js文件:

let fs = require('fs');	
let dbPath = './db.json';

//find:获取学生列表
exports.find = function (callback) {
     
//异步操作,回调函数
	fs.readFile(dbPath, 'utf8', function (err, data) {
     
		if (err) {
     
			return callback(err);
			//文件打开失败时,返回err,而data值为undefined
			//正因为此,只给callback函数err这个参数
		}
		callback(null, JSON.parsed(data).students);
		//文件打开成功时,err值为null,所以第一个参数为null
		//由于parsed()内需要传入字符串,因此需要在readFile处加参数'utf8'
	})
}

router.js文件:

let Student = require('./student');
router.get('/students', function (req, res) {
     
	Student.find(function (err, students) {
     
		if(err) {
     
			return res.status(500).send('Server error');
		}
		res.render('index.html', {
     
			students: students
	})
})
})

save(P65)

封装保存学生的API

student.js:

//save:添加保存学生

/*
将数据保存到db.json文件中用以持久化
	- 先读取出来,转成对象
	- 往对象中push数据
	- 把对象转为字符串
	- 把字符串再次写入文件
*/
exports.save = function (student, callback) {
     
	fs.readFile(dbPath, 'utf8', function (err, data) {
     
		if(err) {
     
			return callback(err)
		}
		let students = JSON.parse(data).students;
		//处理id
		student.id = students[students.length - 1].id + 1;
		//把用户传递的对象保存到数组中
		students.push(student);
		//把对象数据转换为字符串
		let fileData = JSON.stringify({
     
			students: students
		})
		fs.writeFile(dbPath, fileData, function (err) {
     
			if (err) {
     
				//错误就是把错误对象传递给它
				return callback(err);
			}
			callback(null);
		})
	})
}

router.js:

let Student = require('./student');
router.post('/students/new', function (req, res) {
     
	Student.save(req.body, function (err) {
     
		if(err) {
     
			return res.status(500).send('Server error');
		}
		res.redirect('/students');
	})
})

渲染编辑学生页面(P67)

exports.updateById = function (student, callback) {
     
  fs.readFile(dbPath, 'utf8', function (err, data) {
     
    if (err) {
     
      return callback(err);
    }
    let students = JSON.parse(data).students;

    // 把 id 统一转换为数字类型,如果不转换则保存时,id保存为字符串
    // 如果id成了字符串,在第二次编辑时,就会报错
    student.id = parseInt(student.id);
    
    // EcmaScript 6 中的一个数组方法:find
    // 需要接收一个函数作为参数
    // 当某个遍历项符合 item.id === student.id 条件的时候,find 会终止遍历,同时返回遍历项
    let stu = students.find(function (item) {
     
      return item.id === student.id;
    })

    // 遍历拷贝对象,作用类似于↓,但是更简洁
    // stu.name = student.name
    // stu.age = student.age
    
    for (let key in student) {
     
      stu[key] = student[key];
    }

    // 把对象数据转换为字符串
    let fileData = JSON.stringify({
     
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
     
      if (err) {
     
        return callback(err);
      }
      callback(null);
    })
  })
}
router.post('/students/edit', function (req, res) {
     
  // 1. 获取表单数据
  //    req.body
  // 2. 更新
  //    Student.updateById()
  // 3. 发送响应
  Student.updateById(req.body, function (err) {
     
    if (err) {
     
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})
/*
 * 根据 id 获取学生信息对象
 * @param  {Number}   id       学生 id
 * @param  {Function} callback 回调函数
 */
exports.findById = function (id, callback) {
     
  fs.readFile(dbPath, 'utf8', function (err, data) {
     
    if (err) {
     
      return callback(err);
    }
    let students = JSON.parse(data).students;
    let ret = students.find(function (item) {
     
    //此处用到了ES6的数组find方法
      return item.id === parseInt(id);
    })
    callback(null, ret);
  })
}

修改url中的查询字符串的值,如修改127.0.0.1:3000/students/edit?id=1中 id等号后的数字,就可以获取不同的student,可以用 console.log在控制台看到,也可以用模板引擎渲染(视频23:40)

router.get('/students/edit', function (req, res) {
     
  // 1. 在客户端的列表页中处理链接问题(需要有 id 参数)
  // 2. 获取要编辑的学生 id
  // 3. 渲染编辑页面
  //    根据 id 把学生信息查出来
  //    使用模板引擎渲染页面

  Student.findById(parseInt(req.query.id), function (err, student) {
     
  //直接从query获取的id是字符串,需要将其转化为Number
    if (err) {
     
      return res.status(500).send('Server error.');
    }
    //console.log(student);
    res.render('edit.html', {
     
      student: student
    })
  })
})

更新功能-总结(P68)

edit.html(编辑界面):

<form action="/students/edit" method="post">
  
  <input type="hidden" name="id" value="{
      { student.id }}">
  <div class="form-group">
    <label for="">姓名label>
    <input type="text" class="form-control" id="" name="name" required minlength="2" maxlength="10" value="{
      { student.name }}">
  div>
  <div class="form-group">
    <label for="">性别label>
    <div>
      <label class="radio-inline">
        <input type="radio" name="gender" id="" value="0" checked>label>
      <label class="radio-inline">
        <input type="radio" name="gender" id="" value="1">label>
    div>
  div>
  <div class="form-group">
    <label for="">年龄label>
    <input class="form-control" type="number" id="" name="age" value="{
      { student.age }}" required min="1" max="150">
  div>
  <div class="form-group">
    <label for="">爱好label>
    <input class="form-control" type="text" id="" name="hobbies" value="{
      { student.hobbies }}">
  div>
  <button type="submit" class="btn btn-default">Submitbutton>
form>

更新后,点击submit按钮,db.json中的数据就会更新,主页会重新渲染,显示为更改后的数据

删除(P69)

exports.deleteById = function (id, callback) {
     
  fs.readFile(dbPath, 'utf8', function (err, data) {
     
    if (err) {
     
      return callback(err);
    }
    let students = JSON.parse(data).students;

    // findIndex 方法专门用来根据条件查找元素的下标
    //(即根据id值找到要删的元素,再返回该元素的在数组内的下标,便于splice方法删除)
    let deleteId = students.findIndex(function (item) {
     
      return item.id === parseInt(id);
    })

    // 根据下标从数组中删除对应的学生对象
    students.splice(deleteId, 1);

    // 把对象数据转换为字符串
    let fileData = JSON.stringify({
     
      students: students
    })

    // 把字符串保存到文件中
    fs.writeFile(dbPath, fileData, function (err) {
     
      if (err) {
     
        return callback(err);
      }
      callback(null);
    })
  })
}
router.get('/students/delete', function (req, res) {
     
  // 1. 获取要删除的 id
  // 2. 根据 id 执行删除操作
  // 3. 根据操作结果发送响应数据
  Student.deleteById(req.query.id, function (err) {
     
    if (err) {
     
      return res.status(500).send('Server error.')
    }
    res.redirect('/students')
  })
})

封装ajax方法(P72)

这一集再次讲解了回调函数,回调函数是用来拿异步操作的结果的

function get(url, callback) {
     
  var oReq = new XMLHttpRequest();
  // 当请求加载成功之后要调用指定的函数,不用onreadystatechange也可以
  oReq.onload = function () {
     
    // 现在需要得到这里的 oReq.responseText
    callback(oReq.responseText);
  }
  oReq.open("get", url, true);
  oReq.send();
}

get('data.json', function (data) {
     
// 使用回调函数拿到异步操作的结果
  console.log(data);
})

package-lock文件的作用(P74)

(大概了解了一下,不懂就把视频揉碎了看)

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

  • npm5以后的版本安装包不需要加–save参数,它会自动保存依赖信息
  • 当安装包的时候,会自动创建或者是更新package-lock.json文件
  • 该文件会保存node_modules中所有包的信息(版本、下载地址、依赖),这样重新npm install时的速度就可以提升
  • 此外,该文件可以用来锁定版本,重新install一般会下载最新版本,如果希望锁住某个包的老版本,可以写在该文件内,防止自动升级新版

你可能感兴趣的:(js/node.js)