無標(biāo)題文章

#?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

>?```

>?

>?![image](https://user-images.githubusercontent.com/17243165/31977540-fc0a5a6e-b96f-11e7-9a2b-34d66d7241c4.png)

>?

>?#?命令行

>?經(jīng)過上面的配置之后,就可以返回bin目錄下找到`mongo.exe`命令伶椿,并管理員下執(zhí)行辜伟,就可以出現(xiàn)mongodb的命令行模式

>?

>?```shell

>?D:\Program?Files?(x86)\MongoDB\Server\3.2\bin

>?```

>?

>?![image](https://user-images.githubusercontent.com/17243165/31978099-57bce3ca-b972-11e7-88bd-30f5d68036ed.png)

>?

>?然后就可以使用下面的命令來測試了

>

###?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?

```

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市载矿,隨后出現(xiàn)的幾起案子遭铺,更是在濱河造成了極大的恐慌,老刑警劉巖恢准,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件魂挂,死亡現(xiàn)場離奇詭異,居然都是意外死亡馁筐,警方通過查閱死者的電腦和手機(jī)涂召,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敏沉,“玉大人果正,你說我怎么就攤上這事∶顺伲” “怎么了秋泳?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長攒菠。 經(jīng)常有香客問我迫皱,道長,這世上最難降的妖魔是什么辖众? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任卓起,我火速辦了婚禮,結(jié)果婚禮上凹炸,老公的妹妹穿的比我還像新娘戏阅。我一直安慰自己,他們只是感情好啤它,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布奕筐。 她就那樣靜靜地躺著舱痘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪离赫。 梳的紋絲不亂的頭發(fā)上芭逝,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機(jī)與錄音笆怠,去河邊找鬼铝耻。 笑死誊爹,一個(gè)胖子當(dāng)著我的面吹牛蹬刷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播频丘,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼办成,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了搂漠?” 一聲冷哼從身側(cè)響起迂卢,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桐汤,沒想到半個(gè)月后而克,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怔毛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年员萍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拣度。...
    茶點(diǎn)故事閱讀 40,561評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡碎绎,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抗果,到底是詐尸還是另有隱情筋帖,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布冤馏,位于F島的核電站日麸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏逮光。R本人自食惡果不足惜赘淮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望睦霎。 院中可真熱鬧梢卸,春花似錦、人聲如沸副女。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至戴陡,卻和暖如春塞绿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背恤批。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工异吻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人喜庞。 一個(gè)月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓诀浪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親延都。 傳聞我的和親對象是個(gè)殘疾皇子雷猪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容

  • var a = 1; console.log(typeof a);// 'number' var b = '1';...
    zdnexus閱讀 332評論 0 0
  • ### 一.Node.js Express 框架 #### 1.Express 簡介 Express 是一個(gè)簡潔而...
    ac2661e3db02閱讀 199評論 0 0
  • 概要 64學(xué)時(shí) 3.5學(xué)分 章節(jié)安排 電子商務(wù)網(wǎng)站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,223評論 0 3
  • # MVC ?框架的所有代碼結(jié)構(gòu)整合都是采用MVC的基礎(chǔ)架構(gòu),這也是蘋果iOS系統(tǒng)的基本架構(gòu)晰房。Controller...
    keldonwang閱讀 318評論 0 0
  • 喜歡的歌總是有那么幾句歌詞唱到自己心里去求摇。看了一下歌曲列表殊者,最愛劉若英与境,當(dāng)愛在靠近,為愛癡狂猖吴,后來摔刁。。距误。猜扮。 今天聽...
    湛藍(lán)的甜閱讀 243評論 0 0