1. MongoDB
mongoDB數(shù)據(jù)庫基本概念
- MongDB是長得最像關(guān)系型數(shù)據(jù)庫的非關(guān)系型數(shù)據(jù)庫
- 數(shù)據(jù)庫 --> 數(shù)據(jù)庫(可以有多個)
- 數(shù)據(jù)表 --> 集合(數(shù)組)[表]
- 表記錄 -->文檔對象
一個數(shù)據(jù)庫中可以有多個數(shù)據(jù)庫胁附,一個數(shù)據(jù)庫中可以有多個集合(數(shù)組),一個集合中可以有多個文檔(表記錄)
{
qq:{
user:[
{name:"張三",age:17},{},{}...
]
}弓候,
taobao:{
user:[
{name:"張三",age:17},{},{}...
]
},
}
- 也就是說你可以任意的往里面存數(shù)據(jù)夸研,沒有結(jié)構(gòu)性這么一說
1.1 關(guān)系型和非關(guān)系型數(shù)據(jù)庫
1.1.1 關(guān)系型數(shù)據(jù)庫
(表就是關(guān)系亥至,或者說表與表之間存在關(guān)系)姐扮。
- 所有的關(guān)系型數(shù)據(jù)庫都需要通過
sql
語言來操作 - 所有的關(guān)系型數(shù)據(jù)庫在操作之前都需要設(shè)計表結(jié)構(gòu)
- 而且數(shù)據(jù)表還支持約束
- 唯一的
- 主鍵
- 默認(rèn)值
- 非空
1.1.2 非關(guān)系型數(shù)據(jù)庫
- 非關(guān)系型數(shù)據(jù)庫非常的靈活
- 有的非關(guān)系型數(shù)據(jù)庫就是key-value對兒
- 但MongDB是長得最像關(guān)系型數(shù)據(jù)庫的非關(guān)系型數(shù)據(jù)庫
- 數(shù)據(jù)庫 --> 數(shù)據(jù)庫
- 數(shù)據(jù)表 --> 集合(數(shù)組)
- 表記錄 -->文檔對象
1.2 安裝
-
下載
安裝
配置環(huán)境變量
此電腦----右鍵屬性----高級系統(tǒng)設(shè)置----高級----環(huán)境變量----設(shè)置path:為bin目錄
- 最后輸入
mongod --version
測試是否安裝成功
1.3 啟動和關(guān)閉數(shù)據(jù)庫
1.3.1 啟動:
- mongodb 默認(rèn)使用執(zhí)行mongod 命令所處盤符根目錄下的/data/db作為自己的數(shù)據(jù)存儲目錄
- 所以在第一次執(zhí)行該命令之前先自己手動新建一個 /data/db
mongod + 回車 => 啟動
如果想要修改默認(rèn)的數(shù)據(jù)存儲目錄,可以:
mongod --dbpath = 數(shù)據(jù)存儲目錄路徑
1.3.2 停止:
在開啟服務(wù)的控制臺睡榆,直接Ctrl+C;
或者直接關(guān)閉開啟服務(wù)的控制臺胀屿。
1.4 連接數(shù)據(jù)庫
連接:
# 該命令默認(rèn)連接本機(jī)的 MongoDB 服務(wù)
mongo
退出:
# 在連接狀態(tài)輸入 exit 退出連接
exit
1.5 基本命令
- 查看數(shù)據(jù)庫列表(數(shù)據(jù)庫中的所有數(shù)據(jù)庫)
show dbs
- 查看當(dāng)前連接的數(shù)據(jù)庫
db
- 切換到指定的數(shù)據(jù)庫宿崭,(如果沒有會新建)
use 數(shù)據(jù)庫名稱
- 查看當(dāng)前目錄下的所有數(shù)據(jù)表
show collections
- 查看表中的詳細(xì)信息
db.表名.find()
- 插入
db.students.insertOne({"name","Jack"})
1.6 Node操作MongoDB數(shù)據(jù)庫
- 使用官方的
MongoDB
包來操作
- 使用第三方包
mongoose
來操作MongoDB數(shù)據(jù)庫)
2. mongoose
2.1 概述
(使用第三方包mongoose
來操作MongoDB數(shù)據(jù)庫)
- 第三方包:
mongoose
基于MongoDB官方的mongoDB
包再一次做了封裝,名字叫mongoose
讹堤,是WordPress項目團(tuán)隊開發(fā)的洲守。
- 官方學(xué)習(xí)文檔:https://mongoosejs.com/docs/index.html
- 讓我們面對這樣的困境, 編寫MongoDB驗證梗醇,轉(zhuǎn)換和業(yè)務(wù)邏輯是非常麻煩的. 所以我們發(fā)明了Mongoose.
Mongoose為模型提供了一種直接的叙谨,基于scheme結(jié)構(gòu)去定義你的數(shù)據(jù)模型手负。它內(nèi)置數(shù)據(jù)驗證虫溜, 查詢構(gòu)建衡楞,業(yè)務(wù)邏輯鉤子等瘾境,開箱即用犬绒。
安裝
npm i mongoose
helloworld
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const Cat = mongoose.model('Cat', { name: String });
const kitty = new Cat({ name: 'Zildjian' });
kitty.save().then(() => console.log('meow'));
2.2 官方指南
(設(shè)計Scheme 發(fā)布Model (創(chuàng)建表))
// 1.引包
// 注意:安裝后才能require使用
var mongoose = require('mongoose');
// 拿到schema圖表
var Schema = mongoose.Schema;
// 2.連接數(shù)據(jù)庫
// 指定連接數(shù)據(jù)庫后不需要存在凯力,當(dāng)你插入第一條數(shù)據(jù)庫后會自動創(chuàng)建數(shù)據(jù)庫
mongoose.connect('mongodb://localhost/test'); //27017
// 3.設(shè)計集合結(jié)構(gòu)(表結(jié)構(gòu))
// 用戶表
var userSchema = new Schema({
username: { //姓名
type: String,
require: true //添加約束咐鹤,保證數(shù)據(jù)的完整性祈惶,讓數(shù)據(jù)按規(guī)矩統(tǒng)一
},
password: {
type: String,
require: true
},
email: {
type: String
}
});
// 4.將文檔結(jié)構(gòu)發(fā)布為模型
// mongoose.model方法就是用來將一個架構(gòu)發(fā)布為 model
// 第一個參數(shù):傳入一個大寫名詞單數(shù)字符串用來表示你的數(shù)據(jù)庫的名稱
// mongoose 會自動將大寫名詞的字符串生成 小寫復(fù)數(shù) 的集合名稱
// 例如 這里會變成users集合名稱
// 第二個參數(shù):架構(gòu)
// 返回值:模型構(gòu)造函數(shù)
var User = mongoose.model('User', userSchema);
2.3 增刪改查
添加數(shù)據(jù)(增)
// 5.通過模型構(gòu)造函數(shù)對User中的數(shù)據(jù)進(jìn)行操作
var user = new User({
username: 'admin',
password: '123456',
email: 'xiaochen@qq.com'
});
user.save(function(err, ret) {
if (err) {
console.log('保存失敗');
} else {
console.log('保存成功');
console.log(ret);
}
});
刪除(刪)
根據(jù)條件刪除所有:
User.remove({
username: 'xiaoxiao'
}, function(err, ret) {
if (err) {
console.log('刪除失敗');
} else {
console.log('刪除成功');
console.log(ret);
}
});
根據(jù)條件刪除一個:
Model.findOneAndRemove(conditions,[options],[callback]);
根據(jù)id刪除一個:
User.findByIdAndRemove(id,[options],[callback]);
更新(改)
更新所有:
User.remove(conditions,doc,[options],[callback]);
根據(jù)指定條件更新一個:
User.FindOneAndUpdate([conditions],[update],[options],[callback]);
根據(jù)id更新一個:
// 更新 根據(jù)id來修改表數(shù)據(jù)
User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', {
username: 'junjun'
}, function(err, ret) {
if (err) {
console.log('更新失敗');
} else {
console.log('更新成功');
}
});
查詢(查)
查詢所有:
// 查詢所有
User.find(function(err,ret){
if(err){
console.log('查詢失敗');
}else{
console.log(ret);
}
});
條件查詢所有:
// 根據(jù)條件查詢
User.find({ username:'xiaoxiao' },function(err,ret){
if(err){
console.log('查詢失敗');
}else{
console.log(ret);
}
});
條件查詢單個:
// 按照條件查詢單個(匹配的第一個)棒搜,查詢出來的數(shù)據(jù)是一個對象({})
// 沒有條件查詢使用findOne方法力麸,查詢的是表中的第一條數(shù)據(jù)
User.findOne({
username: 'xiaoxiao'
}, function(err, ret) {
if (err) {
console.log('查詢失敗');
} else {
console.log(ret);
}
});
3. CRUD案例重寫(MongoDB)
目錄結(jié)構(gòu)
[圖片上傳失敗...(image-fd4d21-1605691557285)]
code
Student.js
var mongoose = require('mongoose')
mongoose.connect('mongodb://localhost/itcast', { useMongoClient: true })
var Schema = mongoose.Schema
var studentSchema = new Schema({
name: {
type: String,
required: true
},
gender: {
type: Number,
enum: [0, 1],
default: 0
},
age: {
type: Number
},
hobbies: {
type: String
}
})
// 直接導(dǎo)出模型構(gòu)造函數(shù)
module.exports = mongoose.model('Student', studentSchema)
Student-fs.js
/**
* student.js
* 數(shù)據(jù)操作文件模塊
* 職責(zé):操作文件中的數(shù)據(jù),只處理數(shù)據(jù)座慰,不關(guān)心業(yè)務(wù)
*
* 這里才是我們學(xué)習(xí) Node 的精華部分:奧義之所在
* 封裝異步 API
*/
var fs = require('fs')
var dbPath = './db.json'
/**
* 獲取學(xué)生列表
* @param {Function} callback 回調(diào)函數(shù)
*/
exports.find = function (callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
callback(null, JSON.parse(data).students)
})
}
/**
* 根據(jù) id 獲取學(xué)生信息對象
* @param {Number} id 學(xué)生 id
* @param {Function} callback 回調(diào)函數(shù)
*/
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)
})
}
/**
* 添加保存學(xué)生
* @param {Object} student 學(xué)生對象
* @param {Function} callback 回調(diào)函數(shù)
*/
exports.save = function (student, callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
var students = JSON.parse(data).students
// 添加 id 游盲,唯一不重復(fù)
student.id = students[students.length - 1].id + 1
// 把用戶傳遞的對象保存到數(shù)組中
students.push(student)
// 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
var fileData = JSON.stringify({
students: students
})
// 把字符串保存到文件中
fs.writeFile(dbPath, fileData, function (err) {
if (err) {
// 錯誤就是把錯誤對象傳遞給它
return callback(err)
}
// 成功就沒錯益缎,所以錯誤對象是 null
callback(null)
})
})
}
/**
* 更新學(xué)生
*/
exports.updateById = function (student, callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
var students = JSON.parse(data).students
// 注意:這里記得把 id 統(tǒng)一轉(zhuǎn)換為數(shù)字類型
student.id = parseInt(student.id)
// 你要修改誰莺奔,就需要把誰找出來
// EcmaScript 6 中的一個數(shù)組方法:find
// 需要接收一個函數(shù)作為參數(shù)
// 當(dāng)某個遍歷項符合 item.id === student.id 條件的時候令哟,find 會終止遍歷屏富,同時返回遍歷項
var stu = students.find(function (item) {
return item.id === student.id
})
// 這種方式你就寫死了狠半,有 100 個難道就寫 100 次嗎神年?
// stu.name = student.name
// stu.age = student.age
// 遍歷拷貝對象
for (var key in student) {
stu[key] = student[key]
}
// 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
var fileData = JSON.stringify({
students: students
})
// 把字符串保存到文件中
fs.writeFile(dbPath, fileData, function (err) {
if (err) {
// 錯誤就是把錯誤對象傳遞給它
return callback(err)
}
// 成功就沒錯衣摩,所以錯誤對象是 null
callback(null)
})
})
}
/**
* 刪除學(xué)生
*/
exports.deleteById = function (id, callback) {
fs.readFile(dbPath, 'utf8', function (err, data) {
if (err) {
return callback(err)
}
var students = JSON.parse(data).students
// findIndex 方法專門用來根據(jù)條件查找元素的下標(biāo)
var deleteId = students.findIndex(function (item) {
return item.id === parseInt(id)
})
// 根據(jù)下標(biāo)從數(shù)組中刪除對應(yīng)的學(xué)生對象
students.splice(deleteId, 1)
// 把對象數(shù)據(jù)轉(zhuǎn)換為字符串
var fileData = JSON.stringify({
students: students
})
// 把字符串保存到文件中
fs.writeFile(dbPath, fileData, function (err) {
if (err) {
// 錯誤就是把錯誤對象傳遞給它
return callback(err)
}
// 成功就沒錯艾扮,所以錯誤對象是 null
callback(null)
})
})
}
Router.js
var fs = require('fs')
var Student = require('./student')
// Express 提供了一種更好的方式
// 專門用來包裝路由的
var express = require('express')
// 1. 創(chuàng)建一個路由容器
var router = express.Router()
// 2. 把路由都掛載到 router 路由容器中
/*
* 渲染學(xué)生列表頁面
*/
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
})
})
})
/*
* 渲染添加學(xué)生頁面
*/
router.get('/students/new', function (req, res) {
res.render('new.html')
})
/*
* 處理添加學(xué)生
*/
router.post('/students/new', function (req, res) {
// 1. 獲取表單數(shù)據(jù)
// 2. 處理
// 將數(shù)據(jù)保存到 db.json 文件中用以持久化
// 3. 發(fā)送響應(yīng)
new Student(req.body).save(function (err) {
if (err) {
return res.status(500).send('Server error.')
}
res.redirect('/students')
})
})
/*
* 渲染編輯學(xué)生頁面
*/
router.get('/students/edit', function (req, res) {
// 1. 在客戶端的列表頁中處理鏈接問題(需要有 id 參數(shù))
// 2. 獲取要編輯的學(xué)生 id
//
// 3. 渲染編輯頁面
// 根據(jù) id 把學(xué)生信息查出來
// 使用模板引擎渲染頁面
// replace
// 字符串模式
// 簡單,但是不支持全局和忽略大小寫問題
// 正則表達(dá)式模式
// 強(qiáng)大酌予,支持全局和忽略大小寫
Student.findById(req.query.id.replace(/"/g, ''), function (err, student) {
if (err) {
console.log(err)
return res.status(500).send('Server error.')
}
res.render('edit.html', {
student: student
})
})
})
/*
* 處理編輯學(xué)生
*/
router.post('/students/edit', function (req, res) {
// 1. 獲取表單數(shù)據(jù)
// req.body
// 2. 更新
// Student.updateById()
// 3. 發(fā)送響應(yīng)
var id = req.body.id.replace(/"/g, '')
Student.findByIdAndUpdate(id, req.body, function (err) {
if (err) {
return res.status(500).send('Server error.')
}
res.redirect('/students')
})
})
/*
* 處理刪除學(xué)生
*/
router.get('/students/delete', function (req, res) {
// 1. 獲取要刪除的 id
// 2. 根據(jù) id 執(zhí)行刪除操作
// 3. 根據(jù)操作結(jié)果發(fā)送響應(yīng)數(shù)據(jù)
var id = req.query.id.replace(/"/g, '')
Student.findByIdAndRemove(id, function (err) {
if (err) {
return res.status(500).send('Server error.')
}
res.redirect('/students')
})
})
// 3. 把 router 導(dǎo)出
module.exports = router
4. 使用Node操作MySQL數(shù)據(jù)庫
文檔:https://www.npmjs.com/package/mysql
安裝:
npm install --save mysql
helloworld
// 引入mysql包
var mysql = require('mysql');
// 1. 創(chuàng)建連接
var connection = mysql.createConnection({
host : 'localhost', //本機(jī)
user : 'me', //賬號root
password : 'secret', //密碼12345
database : 'my_db' //數(shù)據(jù)庫名
});
// 2. 連接數(shù)據(jù)庫 (打開冰箱門)
connection.connect();
// 3. 執(zhí)行數(shù)據(jù)操作(把大象放到冰箱)
connection.query('SELECT * FROM `users` ', function (error, results, fields) {
if (error) throw error;//拋出異常阻止代碼往下執(zhí)行
// 沒有異常打印輸出結(jié)果
console.log('The solution is: ',results);
});
// 4. 關(guān)閉連接 (關(guān)閉冰箱門)
connection.end();
code
var mysql = require('mysql');
// 1. 創(chuàng)建連接
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'root',
database: 'users' // 對不起,我一不小心把數(shù)據(jù)庫名字和表名起成一樣的简僧,你知道就行
});
// 2. 連接數(shù)據(jù)庫 打開冰箱門
connection.connect();
// 3. 執(zhí)行數(shù)據(jù)操作 把大象放到冰箱
connection.query('SELECT * FROM `users`', function (error, results, fields) {
if (error) throw error;
console.log('The solution is: ', results);
});
// connection.query('INSERT INTO users VALUES(NULL, "admin", "123456")', function (error, results, fields) {
// if (error) throw error;
// console.log('The solution is: ', results);
// });
// 4. 關(guān)閉連接 關(guān)閉冰箱門
connection.end();
5. 異步編程
回調(diào)函數(shù)
- 獲取異步函數(shù)作用域內(nèi)部數(shù)據(jù)
不成立的情況下:
function add(x,y){
console.log(1);
setTimeout(function(){
console.log(2);
var ret = x + y;
return ret;
},1000);
console.log(3);
//到這里執(zhí)行就結(jié)束了棉姐,不會i等到前面的定時器,所以直接返回了默認(rèn)值 undefined
}
console.log(add(2,2));
// 結(jié)果是 1 3 undefined 4
使用回調(diào)函數(shù)解決:
回調(diào)函數(shù):通過一個函數(shù)笛洛,獲取函數(shù)內(nèi)部的操作苛让。(根據(jù)輸入得到輸出結(jié)果)
var ret;
function add(x,y,callback){
// callback就是回調(diào)函數(shù)
// var x = 10;
// var y = 20;
// var callback = function(ret){console.log(ret);}
console.log(1);
setTimeout(function(){
var ret = x + y;
callback(ret);
},1000);
console.log(3);
}
add(10,20,function(ret){
console.log(ret);
});
注意:
凡是需要得到一個函數(shù)內(nèi)部異步操作的結(jié)果(setTimeout,readFile,writeFile,ajax,readdir)
這種情況必須通過 回調(diào)函數(shù) (異步API都會伴隨著一個回調(diào)函數(shù))
封裝ajax方法:
基于原生XMLHttpRequest封裝get方法:
var oReq = new XMLHttpRequest();
// 當(dāng)請求加載成功要調(diào)用指定的函數(shù)
oReq.onload = function(){
console.log(oReq.responseText);
}
oReq.open("GET", "請求路徑",true);
oReq.send();
//===============================================
function get(url,callback){
var oReq = new XMLHttpRequest();
// 當(dāng)請求加載成功要調(diào)用指定的函數(shù)
oReq.onload = function(){
//console.log(oReq.responseText);
callback(oReq.responseText);
}
oReq.open("GET", url,true);
oReq.send();
}
get('data.json',function(data){
console.log(data);
});
Promise
callback hell(回調(diào)地獄):
文件的讀取無法判斷執(zhí)行順序(文件的執(zhí)行順序是依據(jù)文件的大小來決定的)(異步api無法保證文件的執(zhí)行順序)
var fs = require('fs');
fs.readFile('./data/a.text','utf8',function(err,data){
if(err){
// 1 讀取失敗直接打印輸出讀取失敗
return console.log('讀取失敗');
// 2 拋出異常
// 阻止程序的執(zhí)行
// 把錯誤信息打印到控制臺
throw err;
}
console.log(data);
});
fs.readFile('./data/b.text','utf8',function(err,data){
if(err){
// 1 讀取失敗直接打印輸出讀取失敗
return console.log('讀取失敗');
// 2 拋出異常
// 阻止程序的執(zhí)行
// 把錯誤信息打印到控制臺
throw err;
}
console.log(data);
});
通過回調(diào)嵌套的方式來保證順序:
var fs = require('fs');
fs.readFile('./data/a.text','utf8',function(err,data){
if(err){
// 1 讀取失敗直接打印輸出讀取失敗
return console.log('讀取失敗');
// 2 拋出異常
// 阻止程序的執(zhí)行
// 把錯誤信息打印到控制臺
throw err;
}
console.log(data);
fs.readFile('./data/b.text','utf8',function(err,data){
if(err){
// 1 讀取失敗直接打印輸出讀取失敗
return console.log('讀取失敗');
// 2 拋出異常
// 阻止程序的執(zhí)行
// 把錯誤信息打印到控制臺
throw err;
}
console.log(data);
fs.readFile('./data/a.text','utf8',function(err,data){
if(err){
// 1 讀取失敗直接打印輸出讀取失敗
return console.log('讀取失敗');
// 2 拋出異常
// 阻止程序的執(zhí)行
// 把錯誤信息打印到控制臺
throw err;
}
console.log(data);
});
});
});
為了解決以上編碼方式帶來的問題(回調(diào)地獄嵌套)枫吧,所以在EcmaScript6新增了一個API:Promise
九杂。
- Promise:承諾例隆,保證
- Promise本身不是異步的,但往往都是內(nèi)部封裝一個異步任務(wù)
基本語法:
// 在EcmaScript 6中新增了一個API Promise
// Promise 是一個構(gòu)造函數(shù)
var fs = require('fs');
// 1 創(chuàng)建Promise容器 resolve:解決 reject:失敗
var p1 = new Promise(function(resolve, reject) {
fs.readFile('./a.text', 'utf8', function(err, data) {
if (err) {
// console.log(err);
// 把容器的Pending狀態(tài)變?yōu)閞ejected
reject(err);
} else {
// console.log(data);
// 把容器的Pending狀態(tài)變?yōu)閞esolve
resolve(1234);
}
});
});
// 當(dāng)p1成功了镰禾,然后就(then)做指定的操作
// then方法接收的function就是容器中的resolve函數(shù)
p1
.then(function(data) {
console.log(data);
}, function(err) {
console.log('讀取文件失敗了', err);
});
鏈?zhǔn)窖h(huán):
封裝Promise的readFile
:
var fs = require('fs');
function pReadFile(filePath) {
return new Promise(function(resolve, reject) {
fs.readFile(filePath, 'utf8', function(err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
const fs = required('fs')
//解決回調(diào)地獄問題,先讀取文件1坞古,再文件2,然后文件3织堂,不用嵌套到函數(shù)里易阳,
//可以return出去吃粒,返回新的Promise實例
getFileByPath('/files/1.txt')
.then(function(data){
console.log(data)
return getFileByPath('/files/2.txt')
})
.then(function(data){
console.log(data)
return getFileByPath('/files/3.txt')
})
.then(function(data){
console.log(data)
})
.catch(function(err){//捕獲錯誤,輸出錯誤信息
console.log(err.message)
})
mongoose所有的API都支持Promise:
// 查詢所有
User.find()
.then(function(data){
console.log(data)
})
注冊:
User.findOne({username:'admin'},function(user){
if(user){
console.log('用戶已存在')
} else {
new User({
username:'aaa',
password:'123',
email:'fffff'
}).save(function(){
console.log('注冊成功');
})
}
})
User.findOne({
username:'admin'
})
.then(function(user){
if(user){
// 用戶已經(jīng)存在不能注冊
console.log('用戶已存在');
}
else{
// 用戶不存在可以注冊
return new User({
username:'aaa',
password:'123',
email:'fffff'
}).save();
}
})
.then(funciton(ret){
console.log('注冊成功');
})
Generator
async函數(shù)
6. 模板引擎
子模板和模板的繼承(模板引擎高級語法)【include黑竞,extend很魂,block】
注意:
模板頁:
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>模板頁</title>
<link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/>
{{ block 'head' }}{{ /block }}
</head>
<body>
<!-- 通過include導(dǎo)入公共部分 -->
{{include './header.html'}}
<!-- 留一個位置 讓別的內(nèi)容去填充 -->
{{ block 'content' }}
<h1>默認(rèn)內(nèi)容</h1>
{{ /block }}
<!-- 通過include導(dǎo)入公共部分 -->
{{include './footer.html'}}
<!-- 公共樣式 -->
<script src="/node_modules/jquery/dist/jquery.js" ></script>
<script src="/node_modules/bootstrap/dist/js/bootstrap.js" ></script>
{{ block 'script' }}{{ /block }}
</body>
</html>
模板的繼承:
header頁面:
<div id="">
<h1>公共的頭部</h1>
</div>
footer頁面:
<div id="">
<h1>公共的底部</h1>
</div>
模板頁的使用:
<!-- 繼承(extend:延伸,擴(kuò)展)模板也layout.html -->
<!-- 把layout.html頁面的內(nèi)容都拿進(jìn)來作為index.html頁面的內(nèi)容 -->
{{extend './layout.html'}}
<!-- 向模板頁面填充新的數(shù)據(jù) -->
<!-- 填充后就會替換掉layout頁面content中的數(shù)據(jù) -->
<!-- style樣式方面的內(nèi)容 -->
{{ block 'head' }}
<style type="text/css">
body{
background-color: skyblue;
}
</style>
{{ /block }}
{{ block 'content' }}
<div id="">
<h1>Index頁面的內(nèi)容</h1>
</div>
{{ /block }}
<!-- js部分的內(nèi)容 -->
{{ block 'script' }}
<script type="text/javascript">
</script>
{{ /block }}
6. 簡易博客案例
目錄結(jié)構(gòu)
.
|——app.js 項目的入口文件
|——controllers
|——models 存儲使用mongoose設(shè)計的數(shù)據(jù)模型
|——node_modules 第三方包
|——package.json 包描述文件
|——package-lock.json 第三方包版本鎖定文件(npm5之后才有)
|——public 公共靜態(tài)資源
|——routes
|——views 存儲視圖目錄
模板頁
路由設(shè)計
路由 | 方法 | get參數(shù) | post參數(shù) | 是否需要登錄 | 備注 |
---|---|---|---|---|---|
/ | get | 渲染首頁 | |||
/register(登錄) | get | 渲染注冊頁面 | |||
/register | post | email,nickname,password | 處理注冊請求 | ||
/login | get | 渲染登陸界面 | |||
/login | post | email,password | 處理登錄請求 | ||
/loginout | get | 處理退出請求 | |||
模型設(shè)計
功能實現(xiàn)
步驟
- 創(chuàng)建目錄結(jié)構(gòu)
- 整合靜態(tài)也-模板頁
- include
- block
- extend
- 設(shè)計用戶登陸,退出窃植,注冊的路由
- 用戶注冊
- 先處理客戶端頁面的內(nèi)容(表單控件的name巷怜,收集表單數(shù)據(jù)延塑,發(fā)起請求)
- 服務(wù)端
- 獲取從客戶端收到的數(shù)據(jù)
- 操作數(shù)據(jù)庫
- 如果有錯,發(fā)送500告訴客戶端服務(wù)器錯了‘
- 其他的根據(jù)業(yè)務(wù)發(fā)送不同的響應(yīng)數(shù)據(jù)
- 登錄
- 退出
7. Express 中間件
中間件的概念
中間件:把很復(fù)雜的事情分割成單個侥涵,然后依次有條理的執(zhí)行芜飘。就是一個中間處理環(huán)節(jié)燃箭,有輸入招狸,有輸出裙戏。
說的通俗易懂點兒厕诡,中間件就是一個(從請求到響應(yīng)調(diào)用的方法)方法。
把數(shù)據(jù)從請求到響應(yīng)分步驟來處理葛作,每一個步驟都是一個中間處理環(huán)節(jié)赂蠢。
var http = require('http');
var url = require('url');
var cookie = require('./expressPtoject/cookie');
var query = require('./expressPtoject/query');
var postBody = require('./expressPtoject/post-body');
var server = http.createServer(function(){
// 解析請求地址中的get參數(shù)
// var obj = url.parse(req.url,true);
// req.query = obj.query;
query(req,res); //中間件
// 解析請求地址中的post參數(shù)
req.body = {
foo:'bar'
}
});
if(req.url === 'xxx'){
// 處理請求
...
}
server.listen(3000,function(){
console.log('3000 runing...');
});
同一個請求對象所經(jīng)過的中間件都是同一個請求對象和響應(yīng)對象虱岂。
var express = require('express');
var app = express();
app.get('/abc',function(req,res,next){
// 同一個請求的req和res是一樣的第岖,
// 可以前面存儲下面調(diào)用
console.log('/abc');
// req.foo = 'bar';
req.body = {
name:'xiaoxiao',
age:18
}
next();
});
app.get('/abc',function(req,res,next){
// console.log(req.foo);
console.log(req.body);
console.log('/abc');
});
app.listen(3000, function() {
console.log('app is running at port 3000.');
});
中間件的分類:
應(yīng)用程序級別的中間件
萬能匹配(不關(guān)心任何請求路徑和請求方法的中間件):
app.use(function(req,res,next){
console.log('Time',Date.now());
next();
});
關(guān)心請求路徑和請求方法的中間件:
app.use('/a',function(req,res,next){
console.log('Time',Date.now());
next();
});
路由級別的中間件
嚴(yán)格匹配請求路徑和請求方法的中間件
get:
app.get('/',function(req,res){
res.send('get');
});
post:
app.post('/a',function(req,res){
res.send('post');
});
put:
app.put('/user',function(req,res){
res.send('put');
});
delete:
app.delete('/delete',function(req,res){
res.send('delete');
});
總
var express = require('express');
var app = express();
// 中間件:處理請求蔑滓,本質(zhì)就是個函數(shù)
// 在express中烫饼,對中間件有幾種分類
// 1 不關(guān)心任何請求路徑和請求方法的中間件
// 也就是說任何請求都會進(jìn)入這個中間件
// 中間件本身是一個方法杠纵,該方法接收三個參數(shù)
// Request 請求對象
// Response 響應(yīng)對象
// next 下一個中間件
// // 全局匹配中間件
// app.use(function(req, res, next) {
// console.log('1');
// // 當(dāng)一個請求進(jìn)入中間件后
// // 如果需要請求另外一個方法則需要使用next()方法
// next();
// // next是一個方法比藻,用來調(diào)用下一個中間件
// // 注意:next()方法調(diào)用下一個方法的時候银亲,也會匹配(不是調(diào)用緊挨著的哪一個)
// });
// app.use(function(req, res, next) {
// console.log('2');
// });
// // 2 關(guān)心請求路徑的中間件
// // 以/xxx開頭的中間件
// app.use('/a',function(req, res, next) {
// console.log(req.url);
// });
// 3 嚴(yán)格匹配請求方法和請求路徑的中間件
app.get('/',function(){
console.log('/');
});
app.post('/a',function(){
console.log('/a');
});
app.listen(3000, function() {
console.log('app is running at port 3000.');
});
錯誤處理中間件
app.use(function(err,req,res,next){
console.error(err,stack);
res.status(500).send('Something broke');
});
配置使用404中間件:
app.use(function(req,res){
res.render('404.html');
});
配置全局錯誤處理中間件:
app.get('/a', function(req, res, next) {
fs.readFile('.a/bc', funtion() {
if (err) {
// 當(dāng)調(diào)用next()傳參后务蝠,則直接進(jìn)入到全局錯誤處理中間件方法中
// 當(dāng)發(fā)生全局錯誤的時候馏段,我們可以調(diào)用next傳遞錯誤對象
// 然后被全局錯誤處理中間件匹配到并進(jìn)行處理
next(err);
}
})
});
//全局錯誤處理中間件
app.use(function(err,req,res,next){
res.status(500).json({
err_code:500,
message:err.message
});
});
內(nèi)置中間件
- express.static(提供靜態(tài)文件)
第三方中間件
- body-parser
- compression
- cookie-parser
- mogran
- response-time
- server-static
- session