#?Node課程大綱V10(一周)
##?目錄
*?1?day
-?基本介紹
-?環(huán)境配置(node?nvm,cnpm)
-?REPL?環(huán)境(命令行)?運(yùn)行js代碼
-?js文件執(zhí)行
-?nodemon實(shí)時(shí)監(jiān)聽
-?模塊/包與commonjs?規(guī)范
????+?內(nèi)置
????+?第三方
????+?自定義
-?內(nèi)置模塊詳解
????+?Url
????????1.?url?介紹
????????2.?parse
????????3.?format
????????4.?
????+?Query?String
????????1.?parse
????????2.?stringfly
????????3.?escape
????????4.?unescape
????+?Http(爬蟲)
????????1.?get
????????2.?requst
????????3.?cheerio
????+?Event
????+?Fs?文件操作
????+?Stream?
-?作業(yè):
????1.?遍歷目錄樹
????2.?爬取網(wǎng)站圖片并下載
????3.?爬取網(wǎng)站音樂并下載
????4.?封裝發(fā)送郵件驗(yàn)證碼模塊
*?2day
-?服務(wù)器代理跨域原理
-?nodejs創(chuàng)建后端路由
-?nodejs參數(shù)
-?node中的異步處理
-?npmscript
-?scocket?實(shí)時(shí)通信
????+?net
????+?websocket
????+?socket.io
-?作業(yè):
????1.?實(shí)現(xiàn)聊天室
????2.?實(shí)現(xiàn)廣播中獎信息
*?3day
-?express基本使用
-?express?路由
-?express?常用插件
-?ejs?模板使用
-?搭建web服務(wù)器(靜態(tài)路徑)
-?搭建api服務(wù)
-?api測試工具?postman
-?作業(yè):
????+?實(shí)現(xiàn)文件系統(tǒng)版注冊登錄
????+?實(shí)現(xiàn)數(shù)據(jù)庫注冊登錄
*?4day?
-?mongodb?環(huán)境配置安裝
-?mongoose?操作mongod數(shù)據(jù)庫
-?常用的服務(wù)器緩存redis
-?自動化測試
-?作業(yè)
????+?數(shù)據(jù)緩存郵箱驗(yàn)證登錄
*?5day
-?token?驗(yàn)證
-?cookie+session?用戶鑒權(quán)
-?圖片上傳實(shí)現(xiàn)
-?jwt+驗(yàn)證碼實(shí)現(xiàn)注冊登錄
##?Node?簡介
###?客戶端的JavaScript是怎樣的
-?什么是?JavaScript这弧?
??+是一個(gè)腳本語言
??+運(yùn)行在瀏覽器(瀏覽器的js解析內(nèi)核??v8)
??+實(shí)現(xiàn)用戶的交互?(interactive)
??+?變量??賦值???循環(huán)?邏輯?判斷?分支?對象???函數(shù)贮勃。。裕便。请垛。
??+?dom?操作
??+?bom?操作
??+?ajax?
-?JavaScript?的運(yùn)行環(huán)境催训?
??+瀏覽器內(nèi)核解析內(nèi)核???es6??
-?瀏覽器中的?JavaScript?可以做什么?
-?瀏覽器中的?JavaScript?不可以做什么宗收?(不安全)
??+訪問數(shù)據(jù)庫
??+不能對文件進(jìn)行操作????
??+對os?進(jìn)行操作
??+原因?是不安全??和瀏覽器運(yùn)行機(jī)制有關(guān)
-?在開發(fā)人員能力相同的情況下編程語言的能力取決于什么漫拭?
??+cordova?hbuilder????平臺??platform
??+java???java虛擬機(jī)????????(運(yùn)行平臺)
??+php????php虛擬機(jī)
??+c#?????.net?framework???mono
??+js?????解析內(nèi)核??chrome?v8?
-?JavaScript?只可以運(yùn)行在瀏覽器中嗎?
??+不是
###?為什么是JavaScript
+?node?js?不是因?yàn)閖s?產(chǎn)生的
+?node?選擇了js
+?Ryan?dahl
+?2009??2?月份?node有想法
+?2009??5?月份?githup?開源
+?2009??11月份??jsconf??講解推廣node
+?2010年底????被xxx公司收購
+?2018??發(fā)布有重大bug
+?npm?
+?githup?世界上最大的同性交友網(wǎng)站???碼云??
###?what?is?node?混稽?
-?Node.js?是一個(gè)基于Chrome?V8?引擎的JavaScript運(yùn)行環(huán)境
-?Node.js使用了一個(gè)事件驅(qū)動采驻、非阻塞式I/O的模型,使其輕量又高效
-?Node.js的包管理工具npm,是全球最大的開源庫生態(tài)系統(tǒng)
-?官網(wǎng)?http://nodejs.cn/
-?npm??插件官網(wǎng):https://www.npmjs.com/
##?環(huán)境配置
###?Node的安裝
-?安裝包安裝
????+?官網(wǎng)下載對應(yīng)的安裝包
????+?一路next
-?nvm安裝(有一個(gè)類似的工具:n)
????+?Node?Version?Manager(Node版本管理工具)
????+?由于以后的開發(fā)工作可能會在多個(gè)Node版本中測試审胚,而且Node的版本也比較多,所以需要這么款工具來管理
????+
###?相關(guān)版本
-?node版本常識??
??+?偶數(shù)版本為穩(wěn)定版??(0.6.x?礼旅,0.8.x?膳叨,0.10.x)
??+?奇數(shù)版本為非穩(wěn)定版(0.7.x?,0.9.x?痘系,0.11.x)
??+?LTS(Long?Term?Support)
??+?[LTS和Current區(qū)別](https://blog.csdn.net/u012532033/article/details/73332099)
-?操作方式:
??+?重新下載最新的安裝包菲嘴;
??+?覆蓋安裝即可;
-?問題:
??+?以前版本安裝的很多全局的工具包需要重新安裝
??+?無法回滾到之前的版本
??+?無法在多個(gè)版本之間切換(很多時(shí)候我們要使用特定版本)
###?Windows下常用的命令行操作
-?切換當(dāng)前目錄(change?directory):cd
-?創(chuàng)建目錄(make?directory):mkdir
-?查看當(dāng)前目錄列表(directory):dir
??+?別名:ls(list)
-?清空當(dāng)前控制臺:cls
??+?別名:clear
-?刪除文件:del
??+?別名:rm
>?注意:所有別名必須在新版本的?PowerShell?(linux系統(tǒng))中使用
###?常見問題
-?Python環(huán)境丟失
?+?Node中有些第三方的包是以C/C++源碼的方式發(fā)布的汰翠,需要安裝后編譯,確保全局環(huán)境中可以使用python命令,python?版本推薦2.7.0
-?環(huán)境變量丟失
?+?部分電腦安裝完畢之后沒有環(huán)境變量需要手動配置
?+?Windows中環(huán)境變量分為系統(tǒng)變量和用戶變量
?+?環(huán)境變量的變量名是不區(qū)分大小寫的
?+?PATH?變量:只要添加到?PATH?變量中的路徑龄坪,都可以在任何目錄下
?+?目的可以在任何地方調(diào)起node命令
?>
##模塊,包?commonjs
###?commonjs規(guī)范
前端模塊化:AMD,CMD,Commonjs
Node?應(yīng)用由模塊組成,采用?CommonJS?模塊規(guī)范奴璃。
#####?定義module
每個(gè)文件就是一個(gè)模塊悉默,有自己的作用域。在一個(gè)文件里面定義的變量苟穆、函數(shù)抄课、類,都是私有的雳旅,對其他文件不可見跟磨。
#####?暴露接口
?CommonJS規(guī)范規(guī)定,每個(gè)模塊內(nèi)部攒盈,module變量代表當(dāng)前模塊抵拘。這個(gè)變量是一個(gè)對象,它的exports屬性(即module.exports)是對外的接口型豁。加載某個(gè)模塊僵蛛,其實(shí)是加載該模塊的module.exports屬性。
?```javascript
??var?x?=?5;
??var?addX?=?function?(value)?{
????return?value?+?x;
??};
??module.exports.x?=?x;
??module.exports.addX?=?addX;
?```
#####?引用
?require方法用于加載模塊迎变。
?```
??var?example?=?require('./example.js');
??console.log(example.x);?//?5
??console.log(example.addX(1));?//?6
?```
###?模塊的分類
*?內(nèi)置模塊
```
??const?process?=?require('process')
??const?path?=?require('path')
??console.log(process.version)
??console.log(path.resolve('../'))
```
*?第三方模塊
```
????const?request=require("request");
??console.log(request)
??request.get('http://api.douban.com/v2/movie/in_theaters',?(err,?response,?body)?=>?{
????if?(!err)?{
??????//?console.log(body);
??????console.log(JSON.parse(body))
????}?else?{
??????console.log(err);
????}
??})
```
*?自定義模塊
###npm?使用入門
?官網(wǎng):[https://www.npmjs.com/](https://www.npmjs.com/)
?安裝:無需安裝
?查看當(dāng)前版本:
?```
?$?npm?-v
?```
?更新:
?```
?$?npm?install?npm@latest?-g
?```
?初始化工程
?```
?$?npm?init
?$?npm?init?--yes?默認(rèn)配置
?```
?安裝包
?使用npm?install會讀取package.json文件來安裝模塊充尉。安裝的模塊分為兩類
?dependencies和devDependencies,分別對應(yīng)生產(chǎn)環(huán)境需要的安裝包和開發(fā)環(huán)境需要的安裝包衣形。
?```
?$?npm?install
?$?npm?install?<package_name>?
?$?npm?install?<package_name>?--save
?$?npm?install?<package_name>?--save-dev
?```
?更新模塊
?```
?$?npm?update
?```
?卸載模塊
?```
?$?npm?uninstall?<package_name>
?$?npm?uninstall?--save?lodash
?```
配置npm源
*?臨時(shí)使用,?安裝包的時(shí)候通過--registry參數(shù)即可
????$?npm?install?express?--registry?https://registry.npm.taobao.org
*?全局使用
??```
????$?npm?config?set?registry?https://registry.npm.taobao.org
????//?配置后可通過下面方式來驗(yàn)證是否成功
????npm?config?get?registry
????//?或
????npm?info?express
??```
*?cnpm?使用
????```
???????//?安裝cnpm
??????npm?install?-g?cnpm?--registry=https://registry.npm.taobao.org
??????//?使用cnpm安裝包
??????cnpm?install?express
????```
>
###?常用的內(nèi)置模塊
node?常用內(nèi)置api??
(1)?URL?網(wǎng)址解析
????解析URL相關(guān)網(wǎng)址信息
????url.parse(urlString[,?parseQueryString[,?slashesDenoteHost]])
????url.format(urlObject)
????url.resolve(from,?to)
(2)?QueryString?參數(shù)處理
????querystring.escape(str)
????querystring.unescape(str)
????querystring.parse(str[,?sep[,?eq[,?options]]])
????querystring.stringify(obj[,?sep[,?eq[,?options]]])
(3)?HTTP?模塊概要
??http.createServer([options][,?requestListener])
??http.get(options[,?callback])
??簡易的爬蟲
????代理跨域處理
(4)?事件?events?模塊
(5)?文件fs模塊
????打印目錄樹
(6)?Stream?流模塊?
????歌詞播放
????音樂下載
(8)?request?方法
2驼侠、Node.js?基礎(chǔ)應(yīng)用
1、應(yīng)用?HTTP?模塊編寫一個(gè)小爬蟲工具
(1)?利用爬蟲獲取“拉勾網(wǎng)”首頁列表數(shù)據(jù)?
(2)?通過?npm?安裝?cheerio?模塊獲得數(shù)據(jù)
2谆吴、后端表單的提交
要求:
(1)?應(yīng)用?request?post?模擬提交表單
###?文件讀取
Node中文件讀取的方式主要有:
>?fs.readFile(file[,?options],?callback(error,?data))
```javascript
fs.readFile('c:\\demo\1.txt',?'utf8',?(err,?data)?=>?{
??if?(err)?throw?err;
??console.log(data);
});
```
>?fs.readFileSync(file[,?options])
```javascript
try?{
??const?data?=?fs.readFileSync('c:\\demo\1.txt',?'utf8');
??console.log(data);
}?catch(e)?{
??//?文件不存在倒源,或者權(quán)限錯(cuò)誤
??throw?e;
}
```
>?fs.createReadStream(path[,?options])
```javascript
const?stream?=?fs.createReadStream('c:\\demo\1.txt');
let?data?=?''
stream.on('data',?(trunk)?=>?{
??data?+=?trunk;
});
stream.on('end',?()?=>?{
??console.log(data);
});
```
>?*由于Windows平臺下默認(rèn)文件編碼是GBK,在Node中不支持句狼,可以通過[iconv-lite](https://github.com/ashtuchkin/iconv-lite)解決*
###?Readline模塊逐行讀取文本內(nèi)容
```javascript
const?readline?=?require('readline');
const?fs?=?require('fs');
const?rl?=?readline.createInterface({
??input:?fs.createReadStream('sample.txt')
});
rl.on('line',?(line)?=>?{
??console.log('Line?from?file:',?line);
});
```
###?文件寫入
Node中文件寫入的方式主要有:
>?fs.writeFile(file,?data[,?options],?callback(error))
```javascript
fs.writeFile('c:\\demo\a.txt',?new?Date(),?(error)?=>?{
??console.log(error);
});
```
>?fs.writeFileSync(file,?data[,?options])
```javascript
try?{
??fs.writeFileSync('c:\\demo\a.txt',?new?Date());
}?catch?(error)?{
??//?文件夾不存在笋熬,或者權(quán)限錯(cuò)誤
??console.log(error);
}
```
>?fs.createWriteStream(path[,option])
```javascript
var?streamWriter?=?fs.createWriteStream('c:\\demo\a.txt');
setInterval(()?=>?{
??streamWriter.write(`${new?Date}\n`,?(error)?=>?{
????console.log(error);
??});
},?1000);
```
###node中的異步操作
-?fs模塊對文件的幾乎所有操作都有同步和異步兩種形式
-?例如:readFile()?和?readFileSync()
-?區(qū)別:
??+?同步調(diào)用會阻塞代碼的執(zhí)行,異步則不會
??+?異步調(diào)用會將讀取任務(wù)下達(dá)到任務(wù)隊(duì)列腻菇,直到任務(wù)執(zhí)行完成才會回調(diào)
??+?異常處理方面胳螟,同步必須使用?try?catch?方式苫拍,異步可以通過回調(diào)函數(shù)的第一個(gè)參數(shù)
```javascript
console.time('sync');
try?{
??var?data?=?fs.readFileSync(path.join('C:\\Users\\iceStone\\Downloads',?'H.mp4'));
??//?console.log(data);
}?catch?(error)?{
??throw?error;
}
console.timeEnd('sync');
console.time('async');
fs.readFile(path.join('C:\\Users\\iceStone\\Downloads',?'H.mp4'),?(error,?data)?=>?{
??if?(error)?throw?error;
??//?console.log(data);
});
console.timeEnd('async');
```
#####??promise?對象的使用
參考資料:[JavaScript??Promise迷你書](http://liubin.org/promises-book/#what-is-promise)
*?what?is?Promise?*
Promise是抽象異步處理對象以及對其進(jìn)行各種操作的組件。Promise并不是從JavaScript中發(fā)祥的概念旺隙。
Promise最初被提出是在?E語言中绒极,?它是基于并列/并行處理設(shè)計(jì)的一種編程語言。
現(xiàn)在JavaScript也擁有了這種特性蔬捷,這就是JavaScript?Promise
使用了回調(diào)函數(shù)的異步處理
```javascript
----
getAsync("fileA.txt",?function(error,?result){
????if(error){//?取得失敗時(shí)的處理
????????throw?error;
????}
????//?取得成功時(shí)的處理
});
----
<1>?傳給回調(diào)函數(shù)的參數(shù)為(error對象垄提,?執(zhí)行結(jié)果)錯(cuò)誤優(yōu)先處理
```
使用了回調(diào)函數(shù)的異步處理
```javascript
----
var?promise?=?getAsyncPromise("fileA.txt");?
promise.then(function(result){
????//?獲取文件內(nèi)容成功時(shí)的處理
}).catch(function(error){
????//?獲取文件內(nèi)容失敗時(shí)的處理
});
----
<1>?返回promise對象
```
*?創(chuàng)建Promise對象?*
```
var?promise?=?new?Promise(function(resolve,?reject)?{
????//?異步處理
????//?處理結(jié)束后、調(diào)用resolve?或?reject
????resolve('成功處理')
????reject('錯(cuò)誤處理')
});
```
*?使用實(shí)例?*
1.?創(chuàng)建一個(gè)priomise?對象并返回`new?Promise(fn)`
2.?在fn?中指定異步等處理
??*?處理結(jié)果正常的話周拐,調(diào)用`resolve(處理結(jié)果值)`
??*?處理結(jié)果錯(cuò)誤的話铡俐,調(diào)用?`reject(Error對象)`
```javascript
function?asyncFunction()?{
????return?new?Promise(function?(resolve,?reject)?{
????????setTimeout(function?()?{
????????????resolve('Async?Hello?world');
????????},?16);
????});
}
asyncFunction().then(function?(value)?{
????console.log(value);????//?=>?'Async?Hello?world'
}).catch(function?(error)?{
????console.log(error);
});
```
*?Promise的狀態(tài)
用new?Promise?實(shí)例化的promise對象有以下三個(gè)狀態(tài)。
*?**"has-resolution"?-?Fulfilled**???resolve(成功)時(shí)妥粟。
*?**"has-rejection"?-?Rejected**?????reject(失敗)時(shí)
*?**"unresolved"?-?Pending**???既不是resolve也不是reject的狀態(tài)审丘。也就是promise對象剛被創(chuàng)建后的初始化狀態(tài)等
promise對象的狀態(tài),從Pending轉(zhuǎn)換為Fulfilled或Rejected之后勾给,?這個(gè)promise對象的狀態(tài)就不會再發(fā)生任何變化滩报。
也就是說,Promise與Event等不同播急,在.then?后執(zhí)行的函數(shù)可以肯定地說只會被調(diào)用一次脓钾。
另外,F(xiàn)ulfilled和Rejected這兩個(gè)中的任一狀態(tài)都可以表示為Settled(不變的)桩警。
Settled
resolve(成功)?或?reject(失敗)可训。
從Pending和Settled的對稱關(guān)系來看,Promise狀態(tài)的種類/遷移是非常簡單易懂的捶枢。
當(dāng)promise的對象狀態(tài)發(fā)生變化時(shí)握截,用.then?來定義只會被調(diào)用一次的函數(shù)。
###?路徑模塊
在文件操作的過程中烂叔,都必須使用物理路徑(絕對路徑)谨胞,path模塊提供了一系列與路徑相關(guān)的?API
```javascript
console.log('join用于拼接多個(gè)路徑部分,并轉(zhuǎn)化為正常格式');
const?temp?=?path.join(__dirname,?'..',?'lyrics',?'./友誼之光.lrc');
console.log(temp);
console.log('獲取路徑中的文件名');
console.log(path.basename(temp));
console.log('獲取路徑中的文件名并排除擴(kuò)展名');
console.log(path.basename(temp,?'.lrc'));
console.log('====================================');
console.log('獲取不同操作系統(tǒng)的路徑分隔符');
console.log(process.platform?+?'的分隔符為?'?+?path.delimiter);
console.log('一般用于分割環(huán)境變量');
console.log(process.env.PATH.split(path.delimiter));
console.log('====================================');
console.log('獲取一個(gè)路徑中的目錄部分');
console.log(path.dirname(temp));
console.log('====================================');
console.log('獲取一個(gè)路徑中最后的擴(kuò)展名');
console.log(path.extname(temp));
console.log('====================================');
console.log('將一個(gè)路徑解析成一個(gè)對象的形式');
const?pathObject?=?path.parse(temp);
console.log(pathObject);
console.log('====================================');
console.log('將一個(gè)路徑對象再轉(zhuǎn)換為一個(gè)字符串的形式');
//?pathObject.name?=?'我終于失去了你';
pathObject.base?=?'我終于失去了你.lrc';
console.log(pathObject);
console.log(path.format(pathObject));
console.log('====================================');
console.log('獲取一個(gè)路徑是不是絕對路徑');
console.log(path.isAbsolute(temp));
console.log(path.isAbsolute('../lyrics/愛的代價(jià).lrc'));
console.log('====================================');
console.log('將一個(gè)路徑轉(zhuǎn)換為當(dāng)前系統(tǒng)默認(rèn)的標(biāo)準(zhǔn)格式长已,并解析其中的./和../');
console.log(path.normalize('c:/develop/demo\\hello/../world/./a.txt'));
console.log('====================================');
console.log('獲取第二個(gè)路徑相對第一個(gè)路徑的相對路徑');
console.log(path.relative(__dirname,?temp));
console.log('====================================');
console.log('以類似命令行cd命令的方式拼接路徑');
console.log(path.resolve(temp,?'c:/',?'./develop',?'../application'));
console.log('====================================');
console.log('獲取不同平臺中路徑的分隔符(默認(rèn))');
console.log(path.sep);
console.log('====================================');
console.log('允許在任意平臺下以WIN32的方法調(diào)用PATH對象');
//?console.log(path.win32);
console.log(path?===?path.win32);
console.log('====================================');
console.log('允許在任意平臺下以POSIX的方法調(diào)用PATH對象');
console.log(path?===?path.posix);
```
###?express?
---
官網(wǎng):[http://www.expressjs.com.cn/](http://www.expressjs.com.cn/)
####?express?環(huán)境搭建
安裝
```bash
??$?npm?install?express?--save
```
快速開始
```javascript
??const?express?=?require('express')
????const?app?=?express()
????app.get('/',?(req,?res)?=>?res.send('Hello?World!'))
????app.listen(3000,?()?=>?console.log('Example?app?listening?on?port?3000!'))
```
####?express?路由配置
```javascript
let?express=require('express')
let?router=express.Router()
//?該路由使用的中間件
router.use((req,res,next)=>{
???next()
});
//?定義網(wǎng)站主頁的路由
router.post('/addFood',?function(req,?res)?{
??console.log('hahaha')
??//?res.send('這里是admin的登錄');
});
//?定義?about?頁面的路由
router.post('/regist',?function(req,?res)?{
??res.send('這里是admin的注冊側(cè)');
});
module.exports?=?router;
app.use('/admin',admin)
```
####?傳遞數(shù)據(jù)的獲取
??get
??req.query
??post
??req.body
??body-parser
??設(shè)置中文格式
??res.set('Content-Type','text/plain,charset=utf8')
####?請求模擬工具?postman
####?靜態(tài)資源配置
```javascript
app.use(express.static('public'))
app.use('/static',?express.static('public'))
app.use('/static',?express.static(path.join(__dirname,?'public')))
```
##?mongod
###?安裝配置
>?在`Mongodb`官網(wǎng)下載最新版本的[Mongodb下載地址](https://cloud.mongodb.com/)
>?
>?下載`msi`的`window`安裝包畜眨,可以裝到C盤或者D盤目錄下
>?
>?#?配置
>?由于我是安裝在D盤的環(huán)境下
>?
>?```shell
>?D:\Program?Files?(x86)\MongoDB\Server\3.2\bin
>?```
>?
>?所以在bin文件夾下找到mongod.exe命令昼牛,然后通過管理員執(zhí)行`mongod?--dbpath?x路徑x`术瓮,路徑可以是任何地方,我這里選擇在D盤的MongoDB目錄下贰健,當(dāng)然路徑不要包含特殊的字符串胞四,比如`Program?Files?(x86)`也不行
>?
>?```shell
>?mongod?--dbpath?D:\mongodb\data\db
>?```
>?
>?
>?
>?#?命令行
>?經(jīng)過上面的配置之后,就可以返回bin目錄下找到`mongo.exe`命令伶椿,并管理員下執(zhí)行辜伟,就可以出現(xiàn)mongodb的命令行模式
>?
>?```shell
>?D:\Program?Files?(x86)\MongoDB\Server\3.2\bin
>?```
>?
>?
>?
>?然后就可以使用下面的命令來測試了
>
###?mongod
>?```js
>?db.help()//幫助
>?db.stats()//統(tǒng)計(jì)
>?```
>?
>?#?顯示數(shù)據(jù)庫
>?```js
>?show?dbs
>?```
>?
>?檢查當(dāng)前選擇的數(shù)據(jù)庫
>?
>?```js
>?db
>?```
>?
>?#?添加數(shù)據(jù)庫
>?**數(shù)據(jù)庫名**為數(shù)據(jù)庫創(chuàng)建的名字氓侧,使用該命令后會默認(rèn)切換到對應(yīng)的數(shù)據(jù)庫,并且在數(shù)據(jù)庫中添加選項(xiàng)导狡,數(shù)據(jù)庫信息才顯示约巷,如果默認(rèn)就有該數(shù)據(jù)庫,那就是切換到對應(yīng)的數(shù)據(jù)庫里面
>?
>?```js
>?use?數(shù)據(jù)庫名
>?```
>?
>?#?刪除數(shù)據(jù)庫
>?先切換到對應(yīng)的數(shù)據(jù)庫旱捧,然后再執(zhí)行`db.dropDatabase()`刪除該數(shù)據(jù)庫
>?
>?```js
>?use?數(shù)據(jù)庫名
>?//switched?to?db?數(shù)據(jù)庫名
>?db.dropDatabase()
>?```
>?
>?#?顯示集合
>?用一下命令可以檢查創(chuàng)建的集合
>?
>?```js
>?show?collections
>?```
>?
>?#?添加集合
>?在創(chuàng)建完數(shù)據(jù)庫之后独郎,我們就可以創(chuàng)建集合
>?
>?```js
>?db.createCollection(集合名字name,設(shè)置參數(shù)options[對象類型])
>?```
>?
>?**name**是要創(chuàng)建的集合的名稱枚赡。?**options**是一個(gè)文檔氓癌,用于指定集合的配置
>?
>?參數(shù)??類型??描述
>?name??String??要創(chuàng)建的集合的名稱
>?options?Document??(可選)指定有關(guān)內(nèi)存大小和索引的選項(xiàng)
>?**options**參數(shù)是可選的,因此只需要指定集合的名稱贫橙。?以下是可以使用的選項(xiàng)列表:
>?
>?字段??類型??描述
>?capped??Boolean?(可選)如果為true贪婉,則啟用封閉的集合。上限集合是固定大小的集合卢肃,它在達(dá)到其最大大小時(shí)自動覆蓋其最舊的條目疲迂。?如果指定true,則還需要指定size參數(shù)莫湘。
>?autoIndexId?Boolean?(可選)如果為true鬼譬,則在_id字段上自動創(chuàng)建索引。默認(rèn)值為false逊脯。
>?size??數(shù)字??(可選)指定上限集合的最大大小(以字節(jié)為單位)优质。?如果capped為true,那么還需要指定此字段的值军洼。
>?max?數(shù)字??(可選)指定上限集合中允許的最大文檔數(shù)巩螃。
>?由于**option**是可選,我們也可以不帶配置項(xiàng)創(chuàng)建集合
>?
>?```js
>?db.createCollection("mycollection")
>?```
>?
>?#?刪除集合
>?`db.collection.drop()`用于從數(shù)據(jù)庫中刪除集合
>?
>?```js
>?db.集合名.drop()
>?```
>?
>?比如我們可以測試以下操作
>?
>?```js
>?db.createCollection("wscats")//創(chuàng)建名為wscats的集合
>?show?collections//顯示該數(shù)據(jù)庫所有集合???wscats
>?db.wscats.drop()//刪除名為wscats的集合
>?```
>?
>?#?查看文檔
>?最簡單查看文檔的方法就是`find()`匕争,會檢索集合中所有的文檔結(jié)果
>?
>?```js
>?db.集合名.find()
>?```
>?
>?要以格式化的方式顯示結(jié)果避乏,可以使用`pretty()`方法。
>?
>?```js
>?db.集合名.find().pretty()
>?```
>?
>?##?1.固值尋找
>?尋找age集合里面所有含有屬性值為wscats的文檔結(jié)果甘桑,相當(dāng)于`where?name?=?'wscats'`
>?
>?```js
>?db.age.find({name:"wscats"})
>?```
>?
>?##?2.范值尋找
>?操作??語法??示例??等效語句
>?相等??{:}?`db.age.find({"name":"wscats"}).pretty()`?where?name?=?'wscats'
>?小于??{:{$lt:}}?`db.age.find({"likes":{$lt:50}}).pretty()`??where?likes?<?50
>?小于等于??{:{$lte:}}??`db.age.find({"likes":{$lte:50}}).pretty()`?where?likes?<=?50
>?大于??{:{$gt:}}?`db.age.find({"likes":{$gt:50}}).pretty()`??where?likes?>?50
>?大于等于??{:{$gte:}}??`db.age.find({"likes":{$gte:50}}).pretty()`?where?likes?>=?50
>?不等于?{:{$ne:}}?`db.age.find({"likes":{$ne:50}}).pretty()`??where?likes?!=?50
>?##?3.AND和OR尋找
>?###?AND
>?在find()方法中拍皮,如果通過使用`,`將它們分開傳遞多個(gè)鍵跑杭,則mongodb將其視為**AND**條件铆帽。?以下是AND的基本語法
>?
>?尋找`_id`為1并且`name`為wscats的所有結(jié)果集
>?
>?```js
>?db.age.find(
>????{
>???????$and:?[
>??????????{"_id":?1},?{"name":?"wscats"}
>???????]
>????}
>?)
>?```
>?
>?###?OR
>?在要根據(jù)OR條件查詢文檔,需要使用`$or`關(guān)鍵字德谅。以下是OR條件的基本語法
>?
>?尋找`name`為corrine或者`name`為wscats的所有結(jié)果集
>?
>?```js
>?db.age.find(
>????{
>???????$or:?[
>??????????{"name":?"corrine"},?{“name“:?"wscats"}
>???????]
>????}
>?)
>?```
>?
>?###?AND和OR等結(jié)合
>?相當(dāng)于語句`where?title?=?"wscats"?OR?(?title?=?"corrine"?AND?_id?<?5)`
>?
>?```js
>?db.age.find({
>???$or:?[{
>?????"title":?"wscats"
>???},?{
>?????$and:?[{
>???????"title":?"corrine"
>?????},?{
>???????"_id":?{
>?????????$lte:?5
>???????}
>?????}]
>???}]
>?})
>?```
>?
>?#?插入文檔
>?文檔的數(shù)據(jù)結(jié)構(gòu)和JSON基本一樣爹橱。
>?所有存儲在集合中的數(shù)據(jù)都是BSON格式。
>?BSON是一種類json的一種二進(jìn)制形式的存儲格式,簡稱**Binary?JSON**窄做。
>?
>?要將數(shù)據(jù)插入到mongodb集合中愧驱,需要使用mongodb的`insert()`或`save()`方法慰技。
>?
>?```js
>?db.集合名.insert(document)
>?```
>?
>?比如我們可以插入以下數(shù)據(jù)
>?
>?```js
>?db.wscats.insert({
>????_id:?100,
>????title:?'MongoDB?Tutorials',?
>????description:?'node_tutorials',
>????by:?'Oaoafly',
>????url:?'https://github.com/Wscats/node-tutorial',
>????tags:?['wscat','MongoDB',?'database',?'NoSQL','node'],
>????num:?100,
>?})
>?```
>?
>?也可以支持插入多個(gè),注意傳入的是數(shù)組形式
>?
>?```js
>?db.wscats.insert([{
>????_id:?100,
>????title:?‘Hello’
>?},{
>????_id:?101,
>????title:?‘World’
>?}])
>?```
>?
>?在插入的文檔中组砚,如果不指定_id參數(shù)吻商,那么mongodb會為此文檔分配一個(gè)唯一的ObjectId
>?要插入文檔,也可以使用`db.post.save(document)`糟红。如果不在文檔中指定_id手报,那么`save()`方法將與`insert()`方法一樣自動分配ID的值。如果指定_id改化,則將以save()方法的形式替換包含**_id**的文檔的全部數(shù)據(jù)掩蛤。
>?
>?```js
>?db.wscats.save({
>????_id:?111,
>????title:?'Oaoafly?Wscats',?
>?})
>?```
>?
>?#?更新文檔
>?##?1.update()方法
>?尋找第一條title為wscats的值,并且更新值title為corrine和age為12
>?
>?```js
>?db.age.update({
>???'title':?'wscats'
>?},?{
>???$set:?{
>?????'title':?'corrine',
>?????'age':?12
>???}
>?})
>?```
>?
>?默認(rèn)情況下陈肛,mongodb只會更新一個(gè)文檔揍鸟。要更新多個(gè)文檔,需要將參數(shù)`multi`設(shè)置為true句旱,還可以配合find方法里面的各種復(fù)雜條件判斷來篩選結(jié)果阳藻,然后更新多個(gè)文檔
>?
>?尋找所有title為wscats的值,并且更新值title為corrine和age為12
>?
>?```js
>?db.age.update({
>???'title':?'wscats'
>?},?{
>???$set:?{
>?????'title':?'corrine',
>?????'age':?12
>???}
>?},?{
>???multi:?true
>?})
>?```
>?
>?##?2.save()方法
>?將`_id`主鍵為3的文檔谈撒,覆蓋新的值腥泥,注意`_id`為必傳
>?
>?```
>?db.age.save({
>???'_id':3,
>???'title':?'wscats'
>?})
>?```
>?#?刪除文檔
>?刪除主鍵`_id`為3的文檔,默認(rèn)是刪除多條
>?
>?```js
>?db.age.remove({
>???'_id':3
>?})
>?```
>?
>?建議在執(zhí)行`remove()`函數(shù)前先執(zhí)行`find()`命令來判斷執(zhí)行的條件是否正確
>?
>?如果你只想刪除第一條找到的記錄可以設(shè)置**justOne**為1啃匿,如下所示
>?
>?```js
>?db.age.remove({...},1)
>?```
>?
>?全部刪除
>?
>?```js
>?db.age.remove({})
>?```
>?
>?#?Limit與Skip方法
>?##?Limit
>?如果你需要在mongodb中讀取指定數(shù)量的數(shù)據(jù)記錄蛔外,可以使用mongodb的Limit方法,`limit()`方法接受一個(gè)數(shù)字參數(shù)溯乒,該參數(shù)指定從mongodb中讀取的記錄條數(shù)夹厌。
>?
>?```js
>?db.age.find().limit(數(shù)量)
>?```
>?
>?##?Skip
>?我們除了可以使用`limit()`方法來讀取指定數(shù)量的數(shù)據(jù)外,還可以使用`skip()`方法來跳過指定數(shù)量的數(shù)據(jù)裆悄,skip方法同樣接受一個(gè)數(shù)字參數(shù)作為跳過的記錄條數(shù)矛纹。
>?
>?```js
>?db.age.find().limit(數(shù)量).skip(數(shù)量)
>?//skip()方法默認(rèn)值為0
>?```
>?
>?所以我們在實(shí)現(xiàn)分頁的時(shí)候就可以用limit來限制每頁多少條數(shù)據(jù)(一般固定一個(gè)值),用skip來決定顯示第幾頁(一個(gè)有規(guī)律變動的值)
>?
>?#?排序
>?在mongodb中使用使用`sort()`方法對數(shù)據(jù)進(jìn)行排序光稼,`sort()`方法可以通過參數(shù)指定排序的字段或南,并使用1和-1來指定排序的方式,其中1為升序排列艾君,而-1是用于降序排列采够。
>?
>?1?升序排列
>?-1??降序排列
>?```js
>?db.集合名.find().sort({鍵值(屬性值):1})
>?```
>?
>?把`age`集合表重新根據(jù)`_id`主鍵進(jìn)行降序排列
>?
>?```js
>?db.age.find().sort({
>???"_id":?-1
>?})
>?```
>?
>?#?Node.js連接
>?安裝mongodb的模塊
>?
>?```js
>?npm?install?mongodb
>?```
>?
>?##?1.連接數(shù)據(jù)庫
>?```js
>?var?MongoClient?=?require('mongodb').MongoClient;
>?//結(jié)尾是選擇數(shù)據(jù)庫名
>?var?DB_CONN_STR?=?'mongodb://localhost:27017/wscats';
>?MongoClient.connect(DB_CONN_STR,?function(err,?db)?{
>???console.log("連接成功!");
>?});
>?```
>?
>?##?2.查詢數(shù)據(jù)
>?注意查詢回來的結(jié)果需要toArray來遍歷處理
>?
>?```js
>?var?MongoClient?=?require('mongodb').MongoClient;
>?var?DB_CONN_STR?=?'mongodb://localhost:27017/wscats';
>?
>?MongoClient.connect(DB_CONN_STR,?function(err,?db)?{
>???console.log("連接成功腻贰!");
>???//選中age集合吁恍,并用find方法把結(jié)果集拿回來進(jìn)行處理
>???db.collection("age").find({title:?"cba"}).toArray(function(err,?result)?{
>?????if?(err)?{
>???????console.log('Error:'?+?err);
>???????return;
>?????}
>?????console.log(result);
>???});
>?});
>?```
>?
>?經(jīng)過測試扒秸,讀取大于100條的時(shí)候會出現(xiàn)報(bào)錯(cuò)[官網(wǎng)解釋](https://docs.mongodb.com/manual/tutorial/iterate-a-cursor/#cursor-batches)播演,可以嘗試用`forEach`代替
>?
>?```js
>?db.collection('pokemon').find({})
>???.forEach(function(item){
>???????console.log(item)
>???})
>?```
>?
>?###?查詢ID
>?查詢自動生成的`ObjectId`
>?
>?```js
>?var?ObjectId?=?require('mongodb').ObjectId;
>?let?_id?=?ObjectId("5bcae50ed1f2c2f5e4e1a76a");
>?db.collection('xxx').find({
>?????"_id":?_id
>?}).forEach(function?(item)?{
>?????console.log(item)
>?})
>?```
>?
>?##?3.插入數(shù)據(jù)
>?insert函數(shù)第一個(gè)參數(shù)是需要插入的值(可以一個(gè)也可以多個(gè))冀瓦,第二個(gè)參數(shù)是接受一個(gè)回調(diào)函數(shù),當(dāng)值插入成功后回返回插入值得一些關(guān)鍵信息写烤,比如`_id`
>?
>?```js
>?var?MongoClient?=?require('mongodb').MongoClient;
>?var?DB_CONN_STR?=?'mongodb://localhost:27017/wscats';
>?
>?MongoClient.connect(DB_CONN_STR,?function(err,?db)?{
>???console.log("連接成功翼闽!");
>?const?db?=?client.db("demo");
>???db.collection("age").insert([
>?????{?
>???????title:?"插入的值A(chǔ)"
>?????},?{
>???????title:?"插入的值B"
>?????}
>???],?function(err,?result)?{
>?????if?(err)?{
>???????console.log('Error:'?+?err);
>???????return;
>?????}
>?????console.log(result)
>???})
>?});
>?```
>?
>?##?4.更新數(shù)據(jù)
>?注意如果不加$set就是完全替換原來的那份(沒有設(shè)置的屬性值將會丟失),加上$set則只是更新對應(yīng)的屬性值洲炊,其余不做改變
>?
>?```js
>?var?MongoClient?=?require('mongodb').MongoClient;
>?var?DB_CONN_STR?=?'mongodb://localhost:27017/wscats';
>?
>?MongoClient.connect(DB_CONN_STR,?function(err,?db)?{
>???console.log("連接成功感局!");
>???db.collection("age").update({
>?????"_id":?1
>???},?{
>?????$set:?{
>???????title:?"你好,世界",
>???????skill:?"js"
>?????}
>???},?function(err,?result)?{
>?????if?(err)?{
>???????console.log('Error:'?+?err);
>???????return;
>?????}
>?????//console.log(result);
>???});
>?});
>?```
>?
>?##?5.刪除數(shù)據(jù)
>?```js
>?var?MongoClient?=?require('mongodb').MongoClient;
>?var?DB_CONN_STR?=?'mongodb://localhost:27017/wscats';
>?
>?MongoClient.connect(DB_CONN_STR,?function(err,?db)?{
>???console.log("連接成功暂衡!");
>???db.collection("age").remove({
>?????"_id":?1
>???},?function(err,?result)?{
>?????if?(err)?{
>???????console.log('Error:'?+?err);
>???????return;
>?????}
>?????//console.log(result);
>?????//關(guān)閉數(shù)據(jù)庫
>?????db.close();
>???});
>?});
>?```
>?
>?##?6.關(guān)閉數(shù)據(jù)庫
>?```js
>?db.close();
>?```
>?
>?#?封裝自定義模塊
>?新建`mongo.js`寫入以下代碼询微,封裝自定義模塊,方便其他路由復(fù)用狂巢,注意`assert`是node自帶的斷言模塊撑毛,用于測試代碼
>?
>?參考
>?
>?*?[官網(wǎng)API文檔](http://nodejs.cn/api/assert.html)
>?*?[Node.js的斷言模塊assert進(jìn)行單元測試](https://www.cnblogs.com/hong7zai/p/5909914.html)
>?
>?```js
>?const?MongoClient?=?require('mongodb').MongoClient;
>?const?assert?=?require('assert');
>?const?url?=?'mongodb://localhost:27017';
>?const?dbName?=?'shop';
>?function?query(callback)?{
>???MongoClient.connect(url,?function(err,?client)?{
>?????assert.equal(null,?err);
>?????console.log("Connected?successfully?to?server");
>?????const?db?=?client.db(dbName);
>?????callback(db);
>?????client.close();
>???});
>?}
>?module.exports?=?{
>???query
>?}
>?```
>?
>?在路由文件中引入和使用
>?
>?```js
>?var?mongo?=?require('./mongo.js')
>?router.post('/addproduct',?function(req,?res,?next)?{
>???mongo.query(function(db)?{
>?????db.collection("product").insertMany([req.body],?function(err,?result)?{
>???????console.log("Inserted?1?document?into?the?collection");
>???????res.send('respond?with?a?resource');
>?????});
>???})
>?});
>?```
>
###?mongoose?
>?
###?可視化
>?*?[Robo?3T](https://robomongo.org/)
>?*?[Studio3t](https://studio3t.com/download-thank-you/?OS=win64)
##?Socket
###?socket.io
```html
<!DOCTYPE?html>
<html?lang="en">
<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>socket.io</title>
??<script?src="socket.io.js"?charset="utf-8"></script>
</head>
<body>
??<h1>gp6?交流區(qū)</h1>
??<div?id="content"?name="name"?style="overflow-y:?scroll;?width:?400px;?height:?300px;?border:?solid?1px?#000"></div>
??<br?/>
??<div>
????<input?type="text"?id="msg"?style="width:?200px;">
??</div>
??<button?id="submit">提交</button>
??<script>
????var?socket?=?io.connect('http://10.9.164.98:8081');
????const?content?=?document.getElementById('content')
????document.querySelector('#submit')
??????.addEventListener('click',?function?()?{
????????var?msg2?=?msg.value
????????socket.emit('receive',?msg2)
????????msg.value?=?''
????????content.innerHTML?+=?msg2?+?'<br/>'
??????},?false)
??????socket.on('message',?function(msg){
????????content.innerHTML?+=?msg?+?'<br/>'
??????})
??</script>
</body>
</html>
```
server.js
```js
var?express?=?require('express');
var?app?=?express();
var?server?=?require('http').Server(app);
var?io?=?require('socket.io')(server);
app.use(express.static(__dirname?+?'/client'))
io.on('connection',?function?(socket)?{
??setInterval(function?()?{
????socket.emit('list',?'abc')
??},?1000)
??socket.broadcast.emit('list',?'test');
??socket.on('backend',?(msg)?=>?{
????console.log(msg);
??})
??socket.on('receive',?(msg)?=>?{
????socket.broadcast.emit('message',?msg);
??})
});
server.listen(8081,?'10.9.164.98');
```
###?net模塊
serverCode
```js
const?net?=?require('net')
const?server?=?new?net.createServer()
let?clients?=?{}
let?clientName?=?0
server.on('connection',?(client)?=>?{
??client.name?=?++clientName
??clients[client.name]?=?client
??client.on('data',?(msg)?=>?{
????//?console.log('客戶端傳來:'?+?msg);
????broadcast(client,?msg.toString())
??})
??client.on('error',?(e)?=>?{
????console.log('client?error'?+?e);
????client.end()
??})
??client.on('close',?(data)?=>?{
????delete?clients[client.name]
????console.log(client.name?+?'?下線了');
??})
})
function?broadcast(client,?msg)?{
??for?(var?key?in?clients)?{
????clients[key].write(client.name?+?'?說:'?+?msg)
??}
}
server.listen(9000)
```
clientCode
```js
var?net?=?require('net')
const?readline?=?require('readline')
var?port?=?9000
var?host?=?'127.0.0.1'
var?socket?=?new?net.Socket()
socket.setEncoding?=?'UTF-8'
socket.connect(port,?host,?()?=>?{
??socket.write('hello.')
})
socket.on('data',?(msg)?=>?{
??console.log(msg.toString())
??say()
})
socket.on('error',?function?(err)?{
??console.log('error'?+?err);
})
socket.on('close',?function?()?{
??console.log('connection?closeed');
})
const?r1?=?readline.createInterface({
??input:?process.stdin,
??output:?process.stdout
})
function?say()?{
??r1.question('請輸入:',?(inputMsg)?=>?{
????if?(inputMsg?!=?'bye')?{
??????socket.write(inputMsg?+?'\n')
????}?else?{
??????socket.destroy()
??????r1.close()
????}
??})
}
```
###?websocket
```js
const?ws?=?new?WebSocket('ws://localhost:8080/')
ws.onopen?=?()?=>?{
??ws.send('大家好')
}
ws.onmessage?=?(msg)?=>?{
??const?content?=?document.getElementById('content')
??content.innerHTML?+=?msg.data?+?'<br/>'
}
ws.onerror?=?(err)?=>?{
??console.log(err);
}
ws.onclose?=?()?=>?{
??console.log('closed~');
}
ws.send(msg2)
```
server.js
```js
const?WebSocket?=?require('ws')
const?ws?=?new?WebSocket.Server({?port:?8080?})
let?clients?=?{}
let?clientName?=?0
ws.on('connection',?(client)?=>?{
??client.name?=?++clientName
??clients[client.name]?=?client
??client.on('message',?(msg)?=>?{
????broadcast(client,?msg)
??})
??client.on('close',?()?=>?{
????delete?clients[client.name]
????console.log(client.name?+?'?離開了~')
??})
})
function?broadcast(client,?msg)?{
??for?(var?key?in?clients)?{
????clients[key].send(client.name?+?'?說:'?+?msg)
??}
}
```
##?SSR?與?SEO
vue?和?react?介紹?
##?項(xiàng)目實(shí)戰(zhàn)
###?api接口
*?RestfulApi?規(guī)范
*?接口文檔的生成(apidoc)
*?接口請求方式區(qū)別
###?跨域解決
*?cors
*?jsonp
*?proxy
###?身份驗(yàn)證
####?JWT
####?Cookia+Session
###?圖片上傳
1.?安裝multer模塊
```javascript
npm?install?multer
```
2.?引用模塊
它是依賴于express的一個(gè)模塊
```javascript
//引用express并配置
var?express?=?require("express");
var?app?=?express();
app.listen(3000);
```
```javascript
var?multer?=?require('multer');
/*var?upload?=?multer({
??//如果用這種方法上傳,要手動添加文明名后綴
????????//如果用下面配置的代碼唧领,則可以省略這一句
??dest:?'uploads/'
})*/
```
3.?配置
設(shè)置保存文件的地方藻雌,并根據(jù)上傳的文件名對應(yīng)文件添加后綴
可以通過`filename`屬性定制文件保存的格式
|屬性值|用途|
|-|-|
|`destination`|設(shè)置資源的保存路徑。注意斩个,如果沒有這個(gè)配置項(xiàng)胯杭,默認(rèn)會保存在`/tmp/uploads`下。此外受啥,路徑需要自己創(chuàng)建|
|`filename`|設(shè)置資源保存在本地的文件名|
```javascript
var?storage?=?multer.diskStorage({
??//設(shè)置上傳后文件路徑做个,uploads文件夾會自動創(chuàng)建。
??destination:?function(req,?file,?cb)?{
????cb(null,?'./uploads')
??},
??//給上傳文件重命名滚局,獲取添加后綴名
??filename:?function(req,?file,?cb)?{
????var?fileFormat?=?(file.originalname).split(".");
????//給圖片加上時(shí)間戳格式防止重名名
????//比如把?abc.jpg圖片切割為數(shù)組[abc,jpg],然后用數(shù)組長度-1來獲取后綴名
????cb(null,?file.fieldname?+?'-'?+?Date.now()?+?"."?+?fileFormat[fileFormat.length?-?1]);
??}
});
var?upload?=?multer({
??storage:?storage
});
```
4.?接受文件
`upload.single('xxx')`叁温,xxx與表單中的name屬性的值對應(yīng)
這里雖然用到post請求,但實(shí)際上不需要`bodyParser`模塊處理
```javascript
app.post('/upload-single',?upload.single('logo'),?function(req,?res,?next)?{
??console.log(req.file)
??console.log('文件類型:%s',?req.file.mimetype);
??console.log('原始文件名:%s',?req.file.originalname);
??console.log((req.file.originalname).split("."))
??console.log('文件大泻顺搿:%s',?req.file.size);
??console.log('文件保存路徑:%s',?req.file.path);
??res.send({
????ret_code:?'0'
??});
});
```
5.?多圖上傳
多圖上傳只要更改一下地方膝但,前端往file輸入框加多一個(gè)`multiple="multiple"`屬性值,此時(shí)就可以在選圖的時(shí)候多選了谤草,當(dāng)然也可以并列多個(gè)file輸入框(不推薦多個(gè)上傳圖片輸入框)跟束,這樣體驗(yàn)會不好
```html
<input?type="file"?name="logo"?multiple="multiple"?/>
```
后端也需要相應(yīng)的改變
```javascript
app.post('/upload-single',?upload.single('logo'),?function(req,?res,?next)?{
//upload.single('logo')變?yōu)閡pload.array('logo',?2),數(shù)字代表可以接受多少張圖片
app.post('/upload-single',?upload.array('logo',?2),?function(req,?res,?next)?{
```
如果不想有圖片數(shù)量上傳限制丑孩,我們可以用`upload.any()`方法
```javascript
app.post('/upload-single',?upload.any(),?function(req,?res,?next)?{?
??res.append("Access-Control-Allow-Origin","*");
??res.send({
????wscats_code:?'0'
??});
});
```
6.?前端部分
-?formData表單提交
```html
<form?action="http://localhost:3000/upload-single"?method="post"?enctype="multipart/form-data">
??<h2>單圖上傳</h2>
??<input?type="file"?name="logo">
??<input?type="submit"?value="提交">
</form>
```
-?formData表單+ajax提交
```javascript
<form?id="uploadForm">
??<p>指定文件名:?<input?type="text"?name="filename"?value=""?/></p>
??<p>上傳文件:?<input?type="file"?name="logo"?/></?p>
??<input?type="button"?value="上傳"?onclick="doUpload()"?/>
</form>
```
`FormData`對象冀宴,是可以使用一系列的鍵值對來模擬一個(gè)完整的表單,然后使用`XMLHttpRequest`發(fā)送這個(gè)"表單"
**注意點(diǎn)**
-?processData設(shè)置為false温学。因?yàn)閐ata值是FormData對象略贮,不需要對數(shù)據(jù)做處理。
-?`<form>`標(biāo)簽添加`enctype="multipart/form-data"`屬性。
-?cache設(shè)置為false逃延,上傳文件不需要緩存览妖。
-?contentType設(shè)置為false。因?yàn)槭怯蒨<form>`表單構(gòu)造的FormData對象揽祥,且已經(jīng)聲明了屬性`enctype="multipart/form-data"`讽膏,所以這里設(shè)置為false
上傳后,服務(wù)器端代碼需要使用從查詢參數(shù)名為logo獲取文件輸入流對象拄丰,因?yàn)閌<input>`中聲明的是`name="logo"`
```javascript
function?doUpload()?{
??$.ajax({
????url:?'http://localhost:3000/upload-single',
????type:?'POST',
????cache:?false,?//不必須
????data:?new?FormData($('#uploadForm')[0]),
????processData:?false,//必須
????contentType:?false,//必須
????success:?function(data)?{
??????console.log(data)
????}
??})
}
```
>?參考文檔
[Github?MyDemo](https://github.com/Wscats/node-tutorial/tree/master/uploadFiles)
[Github?Multer](https://github.com/expressjs/multer)
[MDN?FormData對象的使用](https://developer.mozilla.org/zh-CN/docs/Web/API/FormData/Using_FormData_Objects)
##?自動化測試?mocha
Mocha('摩卡')府树,誕生于2011年,現(xiàn)在比較流行的JavaSscript測試框架之一,可以運(yùn)行于Node環(huán)境和瀏覽器環(huán)境
測試框架:可以運(yùn)行測試的工具料按。通過他奄侠,可以為JavaScript應(yīng)用?添加測試,從而保證代碼質(zhì)量
>?參考文檔
[mochajs](https://mochajs.org/)
[mocha中文文檔](https://segmentfault.com/a/1190000011362879)
###?安裝配置
使用npm?全局安裝
```?
$?npm?install?--global?mocha
```
項(xiàng)目依賴?局部安裝?
```
$?npm?isntall?mocha
```
###?基本語法
###?assert?斷言
+?斷言庫:chai
+?should?風(fēng)格斷言
+?expect?風(fēng)格斷言
全局安裝chai
```js
npm?install?chai?-g?
```
###?案例使用?
遞歸執(zhí)行
```
$?mocha?test?--recursive?
```