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 目录中的所有资源
文件操作路径:
/*
在文件操作的相对路径中:
./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
*/
把app.js中的app.get和app.post提出来,放在和app.js同一目录下的router.js中
app
//可以链式调用
.get('/',function() {
}) //当以get方法请求/的时候,执行对应的处理函数
.get('/get',function() {
})
.post('/post', function() {
}) //当以post方法请求/post的时候,执行对应的处理函数
三种方式:
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
下载:
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 管理系统
此处用到了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/ 来进行访问');
})
npm install --save body-parser
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/ 来进行访问');
})
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文件时的数据并保存时,网页刷新即可看到修改后的结果
一开始设计好路由表,大概长这样:
以前把所有内容都写在app.js里,现在要提取模块了
app.js 入口模块
职责:
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为例),直接用该模块内封装好的函数即可
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
})
})
})
封装保存学生的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');
})
})
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
})
})
})
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中的数据就会更新,主页会重新渲染,显示为更改后的数据
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')
})
})
这一集再次讲解了回调函数,回调函数是用来拿异步操作的结果的
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);
})
(大概了解了一下,不懂就把视频揉碎了看)
npm5以后加入了这个文件,当安装包的时候,npm都会生成或更新package-lock.json文件。
node_modules
中所有包的信息(版本、下载地址、依赖),这样重新npm install时的速度就可以提升