Node.js筆記

Node介紹

為什么要學(xué)習(xí)Node.js

  • 企業(yè)需求
    • 具有服務(wù)端開(kāi)發(fā)經(jīng)驗(yàn)更改
    • front-end
    • back-end
    • 全棧開(kāi)發(fā)工程師
    • 基本的網(wǎng)站開(kāi)發(fā)能力
      • 服務(wù)端
      • 前端
      • 運(yùn)維部署
    • 多人社區(qū)

學(xué)習(xí)地址http://nodejs.cn/api/

Node.js是什么

  • Node.js是JavaScript 運(yùn)行時(shí)
  • 通俗易懂的講衙耕,Node.js是JavaScript的運(yùn)行平臺(tái)
  • Node.js既不是語(yǔ)言耸彪,也不是框架,它是一個(gè)平臺(tái)
  • 瀏覽器中的JavaScript
    • EcmaScript
      • 基本語(yǔ)法
      • if
      • var
      • function
      • Object
      • Array
    • Bom
    • Dom
  • Node.js中的JavaScript
    • 沒(méi)有Bom偶芍,Dom
    • EcmaScript
    • 在Node中這個(gè)JavaScript執(zhí)行環(huán)境為JavaScript提供了一些服務(wù)器級(jí)別的API
      • 例如文件的讀寫(xiě)
      • 網(wǎng)絡(luò)服務(wù)的構(gòu)建
      • 網(wǎng)絡(luò)通信
      • http服務(wù)器
  • 構(gòu)建與Chrome的V8引擎之上
    • 代碼只是具有特定格式的字符串
    • 引擎可以認(rèn)識(shí)它,幫你解析和執(zhí)行
    • Google Chrome的V8引擎是目前公認(rèn)的解析執(zhí)行JavaScript代碼最快的
    • Node.js的作者把Google Chrome中的V8引擎移植出來(lái)寺擂,開(kāi)發(fā)了一個(gè)獨(dú)立的JavaScript運(yùn)行時(shí)環(huán)境
  • Node.js uses an envent-driven,non-blocking I/O mode that makes it lightweight and efficent.
    • envent-driven 事件驅(qū)動(dòng)
    • non-blocking I/O mode 非阻塞I/O模型(異步)
    • ightweight and efficent. 輕量和高效
  • Node.js package ecosystem,npm,is the larget scosystem of open sourcr libraries in the world
    • npm 是世界上最大的開(kāi)源生態(tài)系統(tǒng)
    • 絕大多數(shù)JavaScript相關(guān)的包都存放在npm上搂根,這樣做的目的是為了讓開(kāi)發(fā)人員更方便的去下載使用
    • npm install jquery

Node能做什么

  • web服務(wù)器后臺(tái)
  • 命令行工具
    • npm(node)
    • git(c語(yǔ)言)
    • hexo(node)
    • ...
  • 對(duì)于前端工程師來(lái)講,接觸最多的是它的命令行工具
    • 自己寫(xiě)的很少锨匆,主要是用別人第三方的
    • webpack
    • gulp
    • npm

起步

安裝Node環(huán)境

  • 查看Node環(huán)境的版本號(hào)
  • 下載:https://nodejs.org/en/
  • 安裝:
    • 傻瓜式安裝,一路next
    • 安裝過(guò)再次安裝會(huì)升級(jí)
  • 確認(rèn)Node環(huán)境是否安裝成功
    • 查看node的版本號(hào):node --version
    • 或者node -v
  • 配置環(huán)境變量

解析執(zhí)行JavaScript

  1. 創(chuàng)建編寫(xiě)JavaScript腳本文件
  2. 打開(kāi)終端冬筒,定位腳本文件的所屬目錄
  3. 輸入node 文件名執(zhí)行對(duì)應(yīng)的文件

注意:文件名不要用node.js來(lái)命名恐锣,也就是說(shuō)除了node這個(gè)名字隨便起,最好不要使用中文舞痰。

文件的讀寫(xiě)

文件讀取:

//瀏覽器中的JavaScript是沒(méi)有文件操作能力的
//但是Node中的JavaScript具有文件操作能力
//fs是file-system的簡(jiǎn)寫(xiě)土榴,就是文件系統(tǒng)的意思
//在Node中如果想要進(jìn)行文件的操作就必須引用fs這個(gè)核心模塊
//在fs這個(gè)和興模塊中,就提供了人所有文件操作相關(guān)的API
//例如 fs.readFile就是用來(lái)讀取文件的

//  1.使用fs核心模塊
var fs = require('fs');

// 2.讀取文件
fs.readFile('./data/a.txt',function(err,data){
   if(err){
        console.log('文件讀取失敗');
   }
    else{
         console.log(data.toString());
    }
})

文件寫(xiě)入:

//  1.使用fs核心模塊
var fs = require('fs');

// 2.將數(shù)據(jù)寫(xiě)入文件
fs.writeFile('./data/a.txt','我是文件寫(xiě)入的信息',function(err,data){
   if(err){
        console.log('文件寫(xiě)入失敗');
   }
    else{
         console.log(data.toString());
    }
})

http

服務(wù)器:

// 1.加載http核心模塊
var http = require('http');

// 2.使用http.createServer()創(chuàng)建一個(gè)web服務(wù)器
var server = http.createServer();

// 3.服務(wù)器要做的事兒
// 提供服務(wù):對(duì)數(shù)據(jù)服務(wù)
// 發(fā)請(qǐng)求
//  接收請(qǐng)求
//  處理請(qǐng)求
//  反饋(發(fā)送響應(yīng))
//  當(dāng)客戶(hù)端請(qǐng)求過(guò)來(lái)响牛,就會(huì)自動(dòng)觸發(fā)服務(wù)器的request請(qǐng)求事件玷禽,然后執(zhí)行第二個(gè)參數(shù):回調(diào)處理函數(shù)
server.on('request',function(){
    console.log('收到客戶(hù)的請(qǐng)求了')
})

// 4.綁定端口號(hào),啟動(dòng)服務(wù)
server.listen(3000,function(){
    console.log('runing...')
})

Node中的模塊系統(tǒng)

使用Node編寫(xiě)應(yīng)用程序主要就是在使用:

  • EcmaScript語(yǔ)言

    • 和瀏覽器一樣娃善,在Node中沒(méi)有Bom和Dom
  • 核心模塊

    • 文件操作的fs
    • http服務(wù)操作的http
    • url路徑操作模塊
    • path路徑處理模塊
    • os操作系統(tǒng)信息
  • 第三方模塊

    • art-template
    • 必須通過(guò)npm來(lái)下載才可以使用
  • 自己寫(xiě)的模塊

    • 自己創(chuàng)建的文件

什么是模塊化

  • 文件作用域(模塊是獨(dú)立的论衍,在不同的文件使用必須要重新引用)【在node中沒(méi)有全局作用域瑞佩,它是文件模塊作用域】
  • 通信規(guī)則
    • 加載require
    • 導(dǎo)出exports

CommonJS模塊規(guī)范

在Node中的JavaScript還有一個(gè)重要的概念聚磺,模塊系統(tǒng)。

  • 模塊作用域

  • 使用require方法來(lái)加載模塊

  • 使用exports接口對(duì)象來(lái)導(dǎo)出模板中的成員

    加載require

    語(yǔ)法:

    var 自定義變量名 = require('模塊')
    

    作用:

    • 執(zhí)行被加載模塊中的代碼
    • 得到被加載模塊中的exports導(dǎo)出接口對(duì)象

    導(dǎo)出exports

    • Node中是模塊作用域炬丸,默認(rèn)文件中所有的成員只在當(dāng)前模塊有效

    • 對(duì)于希望可以被其他模塊訪(fǎng)問(wèn)到的成員瘫寝,我們需要把這些公開(kāi)的成員都掛載到exports接口對(duì)象中就可以了

      導(dǎo)出多個(gè)成員(必須在對(duì)象中):

      exports.a = 123;
      exports.b = function(){
          console.log('bbb')
      };
      exports.c = {
          foo:"bar"
      };
      exports.d = 'hello';
      
導(dǎo)出單個(gè)成員(拿到的就是函數(shù),字符串):

```javascript
module.exports = 'hello';
```

以下情況會(huì)覆蓋:
```javascript
module.exports = 'hello';
//后者會(huì)覆蓋前者
module.exports = function add(x,y) {
    return x+y;
}
```

也可以通過(guò)以下方法來(lái)導(dǎo)出多個(gè)成員:

```javascript
module.exports = {
    foo = 'hello',
    add:function(){
        return x+y;
    }
};
```

模塊原理

exports和module.exports的一個(gè)引用:

console.log(exports === module.exports);    //true

exports.foo = 'bar';

//等價(jià)于
module.exports.foo = 'bar';

當(dāng)給exports重新賦值后稠炬,exports焕阿!= module.exports.

最終return的是module.exports,無(wú)論exports中的成員是什么都沒(méi)用。

真正去使用的時(shí)候:
    導(dǎo)出單個(gè)成員:exports.xxx = xxx;
    導(dǎo)出多個(gè)成員:module.exports 或者 modeule.exports = {};

總結(jié)

// 引用服務(wù)
var http = require('http');
var fs = require('fs');
// 引用模板
var template = require('art-template');
// 創(chuàng)建服務(wù)
var server = http.createServer();
// 公共路徑
var wwwDir = 'D:/app/www';
server.on('request', function (req, res) {
    var url = req.url;
    // 讀取文件
    fs.readFile('./template-apche.html', function (err, data) {
        if (err) {
            return res.end('404 Not Found');
        }
        fs.readdir(wwwDir, function (err, files) {
            if (err) {
                return res.end('Can not find www Dir.')
            }
            // 使用模板引擎解析替換data中的模板字符串
            // 去xmpTempleteList.html中編寫(xiě)模板語(yǔ)法
            var htmlStr = template.render(data.toString(), { 
                title: 'D:/app/www/ 的索引',
                files:files 
            });
            // 發(fā)送響應(yīng)數(shù)據(jù)
            res.end(htmlStr);
        })
    })
});
server.listen(3000, function () {
    console.log('running....');
})
1.jQuery中的each 和 原生JavaScript方法forEach的區(qū)別:
    提供源頭:
        原生js是es5提供的(不兼容IE8),
        jQuery的each是jQuery第三方庫(kù)提供的(如果要使用需要用2以下的版本也就是1.版本),它的each方法主要用來(lái)遍歷jQuery實(shí)例對(duì)象(偽數(shù)組),同時(shí)也可以做低版本forEach的替代品,jQuery的實(shí)例對(duì)象不能使用forEach方法首启,如果想要使用必須轉(zhuǎn)為數(shù)組([].slice.call(jQuery實(shí)例對(duì)象))才能使用
2.模塊中導(dǎo)出多個(gè)成員和導(dǎo)出單個(gè)成員
3.301和302的區(qū)別:
    301永久重定向,瀏覽器會(huì)記住
    302臨時(shí)重定向
4.exports和module.exports的區(qū)別:
    每個(gè)模塊中都有一個(gè)module對(duì)象
    module對(duì)象中有一個(gè)exports對(duì)象
    我們可以把需要導(dǎo)出的成員都掛載到module.exports接口對(duì)象中
    也就是`module.exports.xxx = xxx`的方式
    但是每次寫(xiě)太多了就很麻煩暮屡,所以Node為了簡(jiǎn)化代碼,就在每一個(gè)模塊中都提供了一個(gè)成員叫`exports`
    `exports === module.exports`結(jié)果為true,所以完全可以`exports.xxx = xxx`
    當(dāng)一個(gè)模塊需要導(dǎo)出單個(gè)成員的時(shí)候必須使用`module.exports = xxx`的方式毅桃,=,使用`exports = xxx`不管用,因?yàn)槊總€(gè)模塊最終return的是module.exports,而exports只是module.exports的一個(gè)引用,所以`exports`即使重新賦值,也不會(huì)影響`module.exports`褒纲。
    有一種賦值方式比較特殊:`exports = module.exports`這個(gè)用來(lái)新建立引用關(guān)系的准夷。
    

require的加載規(guī)則

  • 核心模塊

    • 模塊名
  • 第三方模塊

    • 模塊名
  • 用戶(hù)自己寫(xiě)的

    • 路徑

require的加載規(guī)則:

  • 優(yōu)先從緩存加載

  • 判斷模塊標(biāo)識(shí)符

    • 核心模塊
    • 自己寫(xiě)的模塊(路徑形式的模塊)
    • 第三方模塊(node_modules)
      • 第三方模塊的標(biāo)識(shí)就是第三方模塊的名稱(chēng)(不可能有第三方模塊和核心模塊的名字一致)
      • npm
        • 開(kāi)發(fā)人員可以把寫(xiě)好的框架庫(kù)發(fā)布到npm上
        • 使用者通過(guò)npm命令來(lái)下載
      • 使用方式:var 名稱(chēng) = require('npm install【下載包】 的包名')
        • node_modules/express/package.json main
        • 如果package.json或者main不成立,則查找被選擇項(xiàng):index.js
        • 如果以上條件都不滿(mǎn)足莺掠,則繼續(xù)進(jìn)入上一級(jí)目錄中的node_modules按照上面的規(guī)則依次查找衫嵌,直到當(dāng)前文件所屬此盤(pán)根目錄都找不到最后報(bào)錯(cuò)
// 如果非路徑形式的標(biāo)識(shí)
// 路徑形式的標(biāo)識(shí):
    // ./  當(dāng)前目錄 不可省略
    // ../  上一級(jí)目錄  不可省略
    //  /xxx也就是D:/xxx
    // 帶有絕對(duì)路徑幾乎不用(D:/a/foo.js)
// 首位表示的是當(dāng)前文件模塊所屬磁盤(pán)根目錄
// require('./a'); 


// 核心模塊
// 核心模塊本質(zhì)也是文件,核心模塊文件已經(jīng)被編譯到了二進(jìn)制文件中了彻秆,我們只需要按照名字來(lái)加載就可以了
require('fs'); 

// 第三方模塊
// 凡是第三方模塊都必須通過(guò)npm下載(npm i node_modules)楔绞,使用的時(shí)候就可以通過(guò)require('包名')來(lái)加載才可以使用
// 第三方包的名字不可能和核心模塊的名字是一樣的
// 既不是核心模塊,也不是路徑形式的模塊
//      先找到當(dāng)前文所述目錄的node_modules
//      然后找node_modules/art-template目錄
//      node_modules/art-template/package.json
//      node_modules/art-template/package.json中的main屬性
//      main屬性記錄了art-template的入口模塊
//      然后加載使用這個(gè)第三方包
//      實(shí)際上最終加載的還是文件

//      如果package.json不存在或者mian指定的入口模塊不存在
//      則node會(huì)自動(dòng)找該目錄下的index.js
//      也就是說(shuō)index.js是一個(gè)備選項(xiàng)唇兑,如果main沒(méi)有指定酒朵,則加載index.js文件
//      
        // 如果條件都不滿(mǎn)足則會(huì)進(jìn)入上一級(jí)目錄進(jìn)行查找
// 注意:一個(gè)項(xiàng)目只有一個(gè)node_modules,放在項(xiàng)目根目錄中幔亥,子目錄可以直接調(diào)用根目錄的文件
var template = require('art-template');

模塊標(biāo)識(shí)符中的/和文件操作路徑中的/

文件操作路徑:

// 咱們所使用的所有文件操作的API都是異步的
// 就像ajax請(qǐng)求一樣
// 讀取文件
// 文件操作中 ./ 相當(dāng)于當(dāng)前模塊所處磁盤(pán)根目錄
// ./index.txt    相對(duì)于當(dāng)前目錄
// /index.txt    相對(duì)于當(dāng)前目錄
// /index.txt   絕對(duì)路徑,當(dāng)前文件模塊所處根目錄
// d:express/index.txt   絕對(duì)路徑
fs.readFile('./index.txt',function(err,data){
    if(err){
       return  console.log('讀取失敗');
    }
    console.log(data.toString());
})

模塊操作路徑:

// 在模塊加載中耻讽,相對(duì)路徑中的./不能省略
// 這里省略了.也是磁盤(pán)根目錄
require('./index')('hello')

npm

  • node package manage(node包管理器)
  • 通過(guò)npm命令安裝jQuery包(npm install --save jquery),在安裝時(shí)加上--save會(huì)主動(dòng)生成說(shuō)明書(shū)文件信息(將安裝文件的信息添加到package.json里面)

npm網(wǎng)站

npmjs.com 網(wǎng)站 是用來(lái)搜索npm包的

npm命令行工具

npm是一個(gè)命令行工具帕棉,只要安裝了node就已經(jīng)安裝了npm针肥。

npm也有版本概念,可以通過(guò)npm --version來(lái)查看npm的版本

升級(jí)npm(自己升級(jí)自己):

npm install --global npm

常用命令

  • npm init(生成package.json說(shuō)明書(shū)文件)
    • npm init -y(可以跳過(guò)向?qū)惆椋焖偕?
  • npm install
    • 一次性把dependencies選項(xiàng)中的依賴(lài)項(xiàng)全部安裝
    • 簡(jiǎn)寫(xiě)(npm i)
  • npm install 包名
    • 只下載
    • 簡(jiǎn)寫(xiě)(npm i 包名)
  • npm install --save 包名
    • 下載并且保存依賴(lài)項(xiàng)(package.json文件中的dependencies選項(xiàng))
    • 簡(jiǎn)寫(xiě)(npm i 包名)
  • npm uninstall 包名
    • 只刪除慰枕,如果有依賴(lài)項(xiàng)會(huì)依然保存
    • 簡(jiǎn)寫(xiě)(npm un 包名)
  • npm uninstall --save 包名
    • 刪除的同時(shí)也會(huì)把依賴(lài)信息全部刪除
    • 簡(jiǎn)寫(xiě)(npm un 包名)
  • npm help
    • 查看使用幫助
  • npm 命令 --help
    • 查看具體命令的使用幫助(npm uninstall --help)

解決npm被墻問(wèn)題

npm存儲(chǔ)包文件的服務(wù)器在國(guó)外,有時(shí)候會(huì)被墻即纲,速度很慢具帮,所以需要解決這個(gè)問(wèn)題。

https://developer.aliyun.com/mirror/NPM?from=tnpm淘寶的開(kāi)發(fā)團(tuán)隊(duì)把npm在國(guó)內(nèi)做了一個(gè)鏡像(也就是一個(gè)備份)低斋。

安裝淘寶的cnpm:

npm install -g cnpm --registry=https://registry.npm.taobao.org;
#在任意目錄執(zhí)行都可以
#--global表示安裝到全局蜂厅,而非當(dāng)前目錄
#--global不能省略,否則不管用
npm install --global cnpm

安裝包的時(shí)候把以前的npm替換成cnpm膊畴。

#走國(guó)外的npm服務(wù)器下載jQuery包掘猿,速度比較慢
npm install jQuery;

#使用cnpm就會(huì)通過(guò)淘寶的服務(wù)器來(lái)下載jQuery
cnpm install jQuery;

如果不想安裝cnpm又想使用淘寶的服務(wù)器來(lái)下載:

npm install jquery --registry=https://npm.taobao.org;

但是每次手動(dòng)加參數(shù)就很麻煩,所以我們可以把這個(gè)選項(xiàng)加入到配置文件中:

npm config set registry https://npm.taobao.org;

#查看npm配置信息
npm config list;

只要經(jīng)過(guò)上面的配置命令唇跨,則以后所有的npm install都會(huì)通過(guò)淘寶的服務(wù)器來(lái)下載

package.json

每一個(gè)項(xiàng)目都要有一個(gè)package.json文件(包描述文件稠通,就像產(chǎn)品的說(shuō)明書(shū)一樣)

這個(gè)文件可以通過(guò)npm init自動(dòng)初始化出來(lái)


D:\code\node中的模塊系統(tǒng)>npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg>` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (node中的模塊系統(tǒng))
Sorry, name can only contain URL-friendly characters.
package name: (node中的模塊系統(tǒng)) cls
version: (1.0.0)
description: 這是一個(gè)測(cè)試項(xiàng)目
entry point: (main.js)
test command:
git repository:
keywords:
author: xiaochen
license: (ISC)
About to write to D:\code\node中的模塊系統(tǒng)\package.json:

{
  "name": "cls",
  "version": "1.0.0",
  "description": "這是一個(gè)測(cè)試項(xiàng)目",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "xiaochen",
  "license": "ISC"
}


Is this OK? (yes) yes

對(duì)于目前來(lái)講,最有用的是dependencies選項(xiàng)买猖,可以用來(lái)幫助我們保存第三方包的依賴(lài)信息改橘。

如果node_modules刪除了也不用擔(dān)心,只需要在控制面板中npm install就會(huì)自動(dòng)把package.json中的dependencies中所有的依賴(lài)項(xiàng)全部都下載回來(lái)玉控。

  • 建議每個(gè)項(xiàng)目的根目錄下都有一個(gè)package.json文件
  • 建議執(zhí)行npm install 包名的時(shí)候都加上--save選項(xiàng)飞主,目的是用來(lái)保存依賴(lài)信息

package.json和package-lock.json

npm 5以前是不會(huì)有package-lock.json這個(gè)文件

npm5以后才加入這個(gè)文件

當(dāng)你安裝包的時(shí)候,npm都會(huì)生成或者更新package-lock.json這個(gè)文件

  • npm5以后的版本安裝都不要加--save參數(shù),它會(huì)自動(dòng)保存依賴(lài)信息
  • 當(dāng)你安裝包的時(shí)候碌识,會(huì)自動(dòng)創(chuàng)建或者更新package-lock.json文件
  • package-lock.json這個(gè)文件會(huì)包含node_modules中所有包的信息(版本讽挟,下載地址。丸冕。耽梅。)
    • 這樣的話(huà)重新npm install的時(shí)候速度就可以提升
  • 從文件來(lái)看,有一個(gè)lock稱(chēng)之為鎖
    • 這個(gè)lock使用來(lái)鎖版本的
    • 如果項(xiàng)目依賴(lài)了1.1.1版本
    • 如果你重新install其實(shí)會(huì)下載最細(xì)版本胖烛,而不是1.1.1
    • package-lock.json的另外一個(gè)作用就是鎖定版本號(hào)眼姐,防止自動(dòng)升級(jí)

path路徑操作模塊

參考文檔:https://nodejs.org/docs/latest-v13.x/api/path.html

  • path.basename:獲取路徑的文件名,默認(rèn)包含擴(kuò)展名
  • path.dirname:獲取路徑中的目錄部分
  • path.extname:獲取一個(gè)路徑中的擴(kuò)展名部分
  • path.parse:把路徑轉(zhuǎn)換為對(duì)象
    • root:根路徑
    • dir:目錄
    • base:包含后綴名的文件名
    • ext:后綴名
    • name:不包含后綴名的文件名
  • path.join:拼接路徑
  • path.isAbsolute:判斷一個(gè)路徑是否為絕對(duì)路徑

Node中的其它成員(__dirname,__filename)

在每個(gè)模塊中佩番,除了require,exports等模塊相關(guān)的API之外众旗,還有兩個(gè)特殊的成員:

  • __dirname,是一個(gè)成員趟畏,可以用來(lái)動(dòng)態(tài)獲取當(dāng)前文件模塊所屬目錄的絕對(duì)路徑

  • __filename贡歧,可以用來(lái)動(dòng)態(tài)獲取當(dāng)前文件的絕對(duì)路徑(包含文件名)

  • __dirnamefilename是不受執(zhí)行node命令所屬路徑影響的

在文件操作中,使用相對(duì)路徑是不可靠的赋秀,因?yàn)閚ode中文件操作的路徑被設(shè)計(jì)為相對(duì)于執(zhí)行node命令所處的路徑利朵。

所以為了解決這個(gè)問(wèn)題,只需要把相對(duì)路徑變?yōu)榻^對(duì)路徑(絕對(duì)路徑不受任何影響)就可以了猎莲。

就可以使用__dirname或者__filename來(lái)幫助我們解決這個(gè)問(wèn)題

在拼接路徑的過(guò)程中绍弟,為了避免手動(dòng)拼接帶來(lái)的一些低級(jí)錯(cuò)誤,推薦使用path.join()來(lái)輔助拼接

var fs = require('fs');
var path = require('path');

// console.log(__dirname + 'a.txt');
// path.join方法會(huì)將文件操作中的相對(duì)路徑都統(tǒng)一的轉(zhuǎn)為動(dòng)態(tài)的絕對(duì)路徑
fs.readFile(path.join(__dirname + '/a.txt'),'utf8',function(err,data){
    if(err){
        throw err
    }
    console.log(data);
});

補(bǔ)充:模塊中的路徑標(biāo)識(shí)和這里的路徑?jīng)]關(guān)系著洼,不受影響(就是相對(duì)于文件模塊)

注意:

模塊中的路徑標(biāo)識(shí)和文件操作中的相對(duì)路徑標(biāo)識(shí)不一致

模塊中的路徑標(biāo)識(shí)就是相對(duì)于當(dāng)前文件模塊樟遣,不受node命令所處路徑影響

Express(快速的)

作者:Tj

原生的http在某些方面表現(xiàn)不足以應(yīng)對(duì)我們的開(kāi)發(fā)需求,所以就需要使用框架來(lái)加快我們的開(kāi)發(fā)效率身笤,框架的目的就是提高效率豹悬,讓我們的代碼高度統(tǒng)一。

在node中有很多web開(kāi)發(fā)框架液荸。主要學(xué)習(xí)express

  • http://expressjs.com/,其中主要封裝的是http瞻佛。

  • // 1 安裝
    // 2 引包
    var express = require('express');
    // 3 創(chuàng)建服務(wù)器應(yīng)用程序
    //      也就是原來(lái)的http.createServer();
    var app = express();
    
    // 公開(kāi)指定目錄
    // 只要通過(guò)這樣做了,就可以通過(guò)/public/xx的方式來(lái)訪(fǎng)問(wèn)public目錄中的所有資源
    // 在Express中開(kāi)放資源就是一個(gè)API的事
    app.use('/public/',express.static('/public/'));
    
    //模板引擎在Express中開(kāi)放模板也是一個(gè)API的事
    
    // 當(dāng)服務(wù)器收到get請(qǐng)求 / 的時(shí)候莹弊,執(zhí)行回調(diào)處理函數(shù)
    app.get('/',function(req,res){
        res.send('hello express');
    })
    
    // 相當(dāng)于server.listen
    app.listen(3000,function(){
        console.log('app is runing at port 3000');
    })
    

學(xué)習(xí)Express

起步

安裝:
cnpm install express
hello world:
// 引入express
var express = require('express');

// 1. 創(chuàng)建app
var app = express();

//  2. 
app.get('/',function(req,res){
    // 1
    // res.write('Hello');
    // res.write('World');
    // res.end()

    // 2
    // res.end('hello world');

    // 3
    res.send('hello world');
})

app.listen(3000,function(){
    console.log('express app is runing...');
})
基本路由

路由:

  • 請(qǐng)求方法

  • 請(qǐng)求路徑

  • 請(qǐng)求處理函數(shù)

get:

//當(dāng)你以get方法請(qǐng)求/的時(shí)候涤久,執(zhí)行對(duì)應(yīng)的處理函數(shù)
app.get('/',function(req,res){
    res.send('hello world');
})

post:

//當(dāng)你以post方法請(qǐng)求/的時(shí)候涡尘,執(zhí)行對(duì)應(yīng)的處理函數(shù)
app.post('/',function(req,res){
    res.send('hello world');
})
Express靜態(tài)服務(wù)API
// app.use不僅僅是用來(lái)處理靜態(tài)資源的忍弛,還可以做很多工作(body-parser的配置)
app.use(express.static('public'));

app.use(express.static('files'));

app.use('/stataic',express.static('public'));
// 引入express
var express = require('express');

// 創(chuàng)建app
var app = express();

// 開(kāi)放靜態(tài)資源
// 1.當(dāng)以/public/開(kāi)頭的時(shí)候,去./public/目錄中找對(duì)應(yīng)資源
// 訪(fǎng)問(wèn):http://127.0.0.1:3000/public/login.html
app.use('/public/',express.static('./public/')); 

// 2.當(dāng)省略第一個(gè)參數(shù)的時(shí)候考抄,可以通過(guò)省略/public的方式來(lái)訪(fǎng)問(wèn)
// 訪(fǎng)問(wèn):http://127.0.0.1:3000/login.html
// app.use(express.static('./public/'));   

// 3.訪(fǎng)問(wèn):http://127.0.0.1:3000/a/login.html
// a相當(dāng)于public的別名
// app.use('/a/',express.static('./public/')); 

//  
app.get('/',function(req,res){
    res.end('hello world');
});

app.listen(3000,function(){
    console.log('express app is runing...');
});
在Express中配置使用art-templete模板引擎
  • art-template官方文檔
  • 在node中细疚,有很多第三方模板引擎都可以使用,不是只有art-template
    • 還有ejs川梅,jade(pug)疯兼,handlebars然遏,nunjucks

安裝:

npm install --save art-template
npm install --save express-art-template

//兩個(gè)一起安裝
npm i --save art-template express-art-template

配置:

app.engine('html', require('express-art-template'));

使用:

app.get('/',function(req,res){
    // express默認(rèn)會(huì)去views目錄找index.html
    res.render('index.html',{
           title:'hello world'     
    });
})

如果希望修改默認(rèn)的views視圖渲染存儲(chǔ)目錄,可以:

// 第一個(gè)參數(shù)views千萬(wàn)不要寫(xiě)錯(cuò)
app.set('views',目錄路徑);
在Express中獲取表單請(qǐng)求數(shù)據(jù)
獲取get請(qǐng)求數(shù)據(jù):

Express內(nèi)置了一個(gè)api吧彪,可以直接通過(guò)req.query來(lái)獲取數(shù)據(jù)

// 通過(guò)requery方法獲取用戶(hù)輸入的數(shù)據(jù)
// req.query只能拿到get請(qǐng)求的數(shù)據(jù)
 var comment = req.query;
獲取post請(qǐng)求數(shù)據(jù):

在Express中沒(méi)有內(nèi)置獲取表單post請(qǐng)求體的api待侵,這里我們需要使用一個(gè)第三方包body-parser來(lái)獲取數(shù)據(jù)。

安裝:

npm install --save body-parser;

配置:

// 配置解析表單 POST 請(qǐng)求體插件(注意:一定要在 app.use(router) 之前 )

var express = require('express')
// 引包
var bodyParser = require('body-parser')

var app = express()

// 配置body-parser
// 只要加入這個(gè)配置姨裸,則在req請(qǐng)求對(duì)象上會(huì)多出來(lái)一個(gè)屬性:body
// 也就是說(shuō)可以直接通過(guò)req.body來(lái)獲取表單post請(qǐng)求數(shù)據(jù)
// parse application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({ extended: false }))

// parse application/json
app.use(bodyParser.json())

使用:

app.use(function (req, res) {
  res.setHeader('Content-Type', 'text/plain')
  res.write('you posted:\n')
  // 可以通過(guò)req.body來(lái)獲取表單請(qǐng)求數(shù)據(jù)
  res.end(JSON.stringify(req.body, null, 2))
})

在Express中配置使用express-session插件操作

參考文檔:https://github.com/expressjs/session

安裝:

npm install express-session

配置:

//該插件會(huì)為req請(qǐng)求對(duì)象添加一個(gè)成員:req.session默認(rèn)是一個(gè)對(duì)象
//這是最簡(jiǎn)單的配置方式
//Session是基于Cookie實(shí)現(xiàn)的
app.use(session({
  //配置加密字符串秧倾,他會(huì)在原有的基礎(chǔ)上和字符串拼接起來(lái)去加密
  //目的是為了增加安全性,防止客戶(hù)端惡意偽造
  secret: 'keyboard cat',
  resave: false,
  saveUninitialized: true,//無(wú)論是否適用Session傀缩,都默認(rèn)直接分配一把鑰匙
  cookie: { secure: true }
}))

使用:

// 讀
//添加Session數(shù)據(jù)
//session就是一個(gè)對(duì)象
req.session.foo = 'bar';

//寫(xiě)
//獲取session數(shù)據(jù)
req.session.foo

//刪
req.session.foo = null;
delete req.session.foo

提示:

默認(rèn)Session數(shù)據(jù)時(shí)內(nèi)存儲(chǔ)數(shù)據(jù)那先,服務(wù)器一旦重啟,真正的生產(chǎn)環(huán)境會(huì)把Session進(jìn)行持久化存儲(chǔ)赡艰。

利用Express實(shí)現(xiàn)ADUS項(xiàng)目

模塊化思想

模塊如何劃分:

  • 模塊職責(zé)要單一

javascript模塊化:

  • Node 中的 CommonJS
  • 瀏覽器中的:
    • AMD require.js
    • CMD sea.js
  • es6中增加了官方支持

起步

  • 初始化
  • 模板處理

路由設(shè)計(jì)

請(qǐng)求方法 請(qǐng)求路徑 get參數(shù) post參數(shù) 備注
GET /students 渲染首頁(yè)
GET /students/new 渲染添加學(xué)生頁(yè)面
POST /students/new name,age,gender,hobbies 處理添加學(xué)生請(qǐng)求
GET /students/edit id 渲染編輯頁(yè)面
POST /students/edit id,name,age,gender,hobbies 處理編輯請(qǐng)求
GET /students/delete id 處理刪除請(qǐng)求

提取路由模塊

router.js:

/**
 * router.js路由模塊
 * 職責(zé):
 *      處理路由
 *      根據(jù)不同的請(qǐng)求方法+請(qǐng)求路徑設(shè)置具體的請(qǐng)求函數(shù)
 * 模塊職責(zé)要單一售淡,我們劃分模塊的目的就是增強(qiáng)代碼的可維護(hù)性,提升開(kāi)發(fā)效率
 */
var fs = require('fs');

// Express專(zhuān)門(mén)提供了一種更好的方式
// 專(zhuān)門(mén)用來(lái)提供路由的
var express = require('express');
// 1 創(chuàng)建一個(gè)路由容器
var router = express.Router();
// 2 把路由都掛載到路由容器中

router.get('/students', function(req, res) {
    // res.send('hello world');
    // readFile的第二個(gè)參數(shù)是可選的慷垮,傳入utf8就是告訴他把讀取到的文件直接按照utf8編碼揖闸,直接轉(zhuǎn)成我們認(rèn)識(shí)的字符
    // 除了這樣來(lái)轉(zhuǎn)換,也可以通過(guò)data.toString()來(lái)轉(zhuǎn)換
    fs.readFile('./db.json', 'utf8', function(err, data) {
        if (err) {
            return res.status(500).send('Server error.')
        }
        // 讀取到的文件數(shù)據(jù)是string類(lèi)型的數(shù)據(jù)
        // console.log(data);
        // 從文件中讀取到的數(shù)據(jù)一定是字符串料身,所以一定要手動(dòng)轉(zhuǎn)換成對(duì)象
        var students = JSON.parse(data).students;
        res.render('index.html', {
            // 讀取文件數(shù)據(jù)
            students:students
        })
    })
});

router.get('/students/new',function(req,res){
    res.render('new.html')
});

router.get('/students/edit',function(req,res){
    
});

router.post('/students/edit',function(req,res){
    
});

router.get('/students/delete',function(req,res){
    
});

// 3 把router導(dǎo)出
module.exports = router;

app.js:

var router = require('./router');

// router(app);
// 把路由容器掛載到app服務(wù)中
// 掛載路由
app.use(router);

設(shè)計(jì)操作數(shù)據(jù)的API文件模塊

es6中的find和findIndex:

find接受一個(gè)方法作為參數(shù)楔壤,方法內(nèi)部返回一個(gè)條件

find會(huì)便利所有的元素,執(zhí)行你給定的帶有條件返回值的函數(shù)

符合該條件的元素會(huì)作為find方法的返回值

如果遍歷結(jié)束還沒(méi)有符合該條件的元素惯驼,則返回undefined

/**
 * student.js
 * 數(shù)據(jù)操作文件模塊
 * 職責(zé):操作文件中的數(shù)據(jù)蹲嚣,只處理數(shù)據(jù),不關(guān)心業(yè)務(wù)
 */
var fs = require('fs');
 /**
  * 獲取所有學(xué)生列表
  * return []
  */
exports.find = function(){
    
}


 /**
  * 獲取添加保存學(xué)生
  */
exports.save = function(){
        
}

/**
 * 更新學(xué)生
 */
exports.update = function(){
        
}

 /**
 * 刪除學(xué)生
 */
exports.delete = function(){
        
}

步驟

  • 處理模板

  • 配置靜態(tài)開(kāi)放資源

  • 配置模板引擎

  • 簡(jiǎn)單的路由祟牲,/studens渲染靜態(tài)頁(yè)出來(lái)

  • 路由設(shè)計(jì)

  • 提取路由模塊

  • 由于接下來(lái)的一系列業(yè)務(wù)操作都需要處理文件數(shù)據(jù)隙畜,所以我們需要封裝Student.js'

  • 先寫(xiě)好student.js文件結(jié)構(gòu)

    • 查詢(xún)所有學(xué)生列別哦的API
    • findById
    • save
    • updateById
    • deleteById
  • 實(shí)現(xiàn)具體功能

    • 通過(guò)路由收到請(qǐng)求
    • 接受請(qǐng)求中的參數(shù)(get,post)
      • req.query
      • req.body
    • 調(diào)用數(shù)據(jù)操作API處理數(shù)據(jù)
    • 根據(jù)操作結(jié)果給客戶(hù)端發(fā)送請(qǐng)求
  • 業(yè)務(wù)功能順序

    • 列表
    • 添加
    • 編輯
    • 刪除

子模板和模板的繼承(模板引擎高級(jí)語(yǔ)法)【include说贝,extend议惰,block】

注意:

模板頁(yè):

<!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>模板頁(yè)</title>
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css"/>
    {{ block 'head' }}{{ /block }}
</head>
<body>
    <!-- 通過(guò)include導(dǎo)入公共部分 -->
    {{include './header.html'}}
    
    <!-- 留一個(gè)位置 讓別的內(nèi)容去填充 -->
    {{ block  'content' }}
        <h1>默認(rèn)內(nèi)容</h1>
    {{ /block }}
    
    <!-- 通過(guò)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頁(yè)面:
<div id="">
    <h1>公共的頭部</h1>
</div>
footer頁(yè)面:
<div id="">
    <h1>公共的底部</h1>
</div>

模板頁(yè)的使用:

<!-- 繼承(extend:延伸,擴(kuò)展)模板也layout.html -->
<!-- 把layout.html頁(yè)面的內(nèi)容都拿進(jìn)來(lái)作為index.html頁(yè)面的內(nèi)容 -->
{{extend './layout.html'}}

<!-- 向模板頁(yè)面填充新的數(shù)據(jù) -->
<!-- 填充后就會(huì)替換掉layout頁(yè)面content中的數(shù)據(jù) -->
<!-- style樣式方面的內(nèi)容 -->
{{ block 'head' }}
    <style type="text/css">
        body{
            background-color: skyblue;
        }
    </style>
{{ /block }}
{{ block 'content' }}
    <div id="">
        <h1>Index頁(yè)面的內(nèi)容</h1>
    </div>
{{ /block }}
<!-- js部分的內(nèi)容 -->
{{ block 'script' }}
    <script type="text/javascript">
        
    </script>
{{ /block }}

最終的顯示效果:

MongoDB

關(guān)系型和非關(guān)系型數(shù)據(jù)庫(kù)

關(guān)系型數(shù)據(jù)庫(kù)(表就是關(guān)系乡恕,或者說(shuō)表與表之間存在關(guān)系)言询。

  • 所有的關(guān)系型數(shù)據(jù)庫(kù)都需要通過(guò)sql語(yǔ)言來(lái)操作
  • 所有的關(guān)系型數(shù)據(jù)庫(kù)在操作之前都需要設(shè)計(jì)表結(jié)構(gòu)
  • 而且數(shù)據(jù)表還支持約束
    • 唯一的
    • 主鍵
    • 默認(rèn)值
    • 非空

非關(guān)系型數(shù)據(jù)庫(kù)

  • 非關(guān)系型數(shù)據(jù)庫(kù)非常的靈活
  • 有的關(guān)系型數(shù)據(jù)庫(kù)就是key-value對(duì)兒
  • 但MongDB是長(zhǎng)得最像關(guān)系型數(shù)據(jù)庫(kù)的非關(guān)系型數(shù)據(jù)庫(kù)
    • 數(shù)據(jù)庫(kù) -》 數(shù)據(jù)庫(kù)
    • 數(shù)據(jù)表 -》 集合(數(shù)組)
    • 表記錄 -》文檔對(duì)象

一個(gè)數(shù)據(jù)庫(kù)中可以有多個(gè)數(shù)據(jù)庫(kù),一個(gè)數(shù)據(jù)庫(kù)中可以有多個(gè)集合(數(shù)組)傲宜,一個(gè)集合中可以有多個(gè)文檔(表記錄)

{
    qq:{
       user:[
           {},{},{}...
       ]
    }
}
  • 也就是說(shuō)你可以任意的往里面存數(shù)據(jù)运杭,沒(méi)有結(jié)構(gòu)性這么一說(shuō)

安裝

啟動(dòng)和關(guān)閉數(shù)據(jù)庫(kù)

啟動(dòng):

# mongodb 默認(rèn)使用執(zhí)行mongod 命令所處盼復(fù)根目錄下的/data/db作為自己的數(shù)據(jù)存儲(chǔ)目錄
# 所以在第一次執(zhí)行該命令之前先自己手動(dòng)新建一個(gè) /data/db
mongod

如果想要修改默認(rèn)的數(shù)據(jù)存儲(chǔ)目錄,可以:

mongod --dbpath = 數(shù)據(jù)存儲(chǔ)目錄路徑

停止:

在開(kāi)啟服務(wù)的控制臺(tái)函卒,直接Ctrl+C;
或者直接關(guān)閉開(kāi)啟服務(wù)的控制臺(tái)辆憔。

連接數(shù)據(jù)庫(kù)

連接:

# 該命令默認(rèn)連接本機(jī)的 MongoDB 服務(wù)
mongo

退出:

# 在連接狀態(tài)輸入 exit 退出連接
exit

基本命令

  • show dbs
    • 查看數(shù)據(jù)庫(kù)列表(數(shù)據(jù)庫(kù)中的所有數(shù)據(jù)庫(kù))
  • db
    • 查看當(dāng)前連接的數(shù)據(jù)庫(kù)
  • use 數(shù)據(jù)庫(kù)名稱(chēng)
    • 切換到指定的數(shù)據(jù)庫(kù)怯伊,(如果沒(méi)有會(huì)新建)
  • show collections
    • 查看當(dāng)前目錄下的所有數(shù)據(jù)表
  • db.表名.find()
    • 查看表中的詳細(xì)信息

在Node中如何操作MongoDB數(shù)據(jù)庫(kù)

使用官方的MongoDB包來(lái)操作

http://mongodb.github.io/node-mongodb-native/

使用第三方包mongoose來(lái)操作MongoDB數(shù)據(jù)庫(kù)

第三方包:`mongoose`基于MongoDB官方的`mongodb`包再一次做了封裝真慢,名字叫`mongoose`,是WordPress項(xiàng)目團(tuán)隊(duì)開(kāi)發(fā)的。

https://mongoosejs.com/

學(xué)習(xí)指南(步驟)

官方學(xué)習(xí)文檔:https://mongoosejs.com/docs/index.html

設(shè)計(jì)Scheme 發(fā)布Model (創(chuàng)建表)

// 1.引包
// 注意:按照后才能require使用
var mongoose = require('mongoose');

// 拿到schema圖表
var Schema = mongoose.Schema;

// 2.連接數(shù)據(jù)庫(kù)
// 指定連接數(shù)據(jù)庫(kù)后不需要存在颠毙,當(dāng)你插入第一條數(shù)據(jù)庫(kù)后會(huì)自動(dòng)創(chuàng)建數(shù)據(jù)庫(kù)
mongoose.connect('mongodb://localhost/test');

// 3.設(shè)計(jì)集合結(jié)構(gòu)(表結(jié)構(gòu))
// 用戶(hù)表
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方法就是用來(lái)將一個(gè)架構(gòu)發(fā)布為 model
//      第一個(gè)參數(shù):傳入一個(gè)大寫(xiě)名詞單數(shù)字符串用來(lái)表示你的數(shù)據(jù)庫(kù)的名稱(chēng)
//                  mongoose 會(huì)自動(dòng)將大寫(xiě)名詞的字符串生成 小寫(xiě)復(fù)數(shù) 的集合名稱(chēng)
//                  例如 這里會(huì)變成users集合名稱(chēng)
//      第二個(gè)參數(shù):架構(gòu)
//  返回值:模型構(gòu)造函數(shù)
var User = mongoose.model('User', userSchema);

添加數(shù)據(jù)(增)

// 5.通過(guò)模型構(gòu)造函數(shù)對(duì)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ù)條件刪除一個(gè):

Model.findOneAndRemove(conditions,[options],[callback]);

根據(jù)id刪除一個(gè):

User.findByIdAndRemove(id,[options],[callback]);

更新(改)

更新所有:

User.remove(conditions,doc,[options],[callback]);

根據(jù)指定條件更新一個(gè):

User.FindOneAndUpdate([conditions],[update],[options],[callback]);

根據(jù)id更新一個(gè):

// 更新   根據(jù)id來(lái)修改表數(shù)據(jù)
User.findByIdAndUpdate('5e6c5264fada77438c45dfcd', {
    username: 'junjun'
}, function(err, ret) {
    if (err) {
        console.log('更新失敗');
    } else {
        console.log('更新成功');
    }
});

查詢(xún)(查)

查詢(xún)所有:

// 查詢(xún)所有
User.find(function(err,ret){
    if(err){
        console.log('查詢(xún)失敗');
    }else{
        console.log(ret);
    }
});

條件查詢(xún)所有:

// 根據(jù)條件查詢(xún)
User.find({ username:'xiaoxiao' },function(err,ret){
    if(err){
        console.log('查詢(xún)失敗');
    }else{
        console.log(ret);
    }
});

條件查詢(xún)單個(gè):

// 按照條件查詢(xún)單個(gè)嫩挤,查詢(xún)出來(lái)的數(shù)據(jù)是一個(gè)對(duì)象({})
// 沒(méi)有條件查詢(xún)使用findOne方法,查詢(xún)的是表中的第一條數(shù)據(jù)
User.findOne({
    username: 'xiaoxiao'
}, function(err, ret) {
    if (err) {
        console.log('查詢(xún)失敗');
    } else {
        console.log(ret);
    }
});

使用Node操作MySQL數(shù)據(jù)庫(kù)

文檔:https://www.npmjs.com/package/mysql

安裝:

npm install --save  mysql
// 引入mysql包
var mysql      = require('mysql');

// 創(chuàng)建連接
var connection = mysql.createConnection({
  host     : 'localhost',   //本機(jī)
  user     : 'me',      //賬號(hào)root
  password : 'secret',  //密碼12345
  database : 'my_db'    //數(shù)據(jù)庫(kù)名
});
 
// 連接數(shù)據(jù)庫(kù)    (打開(kāi)冰箱門(mén))
connection.connect();
 
//執(zhí)行數(shù)據(jù)操作    (把大象放到冰箱)
connection.query('SELECT * FROM `users` ', function (error, results, fields) {
  if (error) throw error;//拋出異常阻止代碼往下執(zhí)行
  // 沒(méi)有異常打印輸出結(jié)果
  console.log('The solution is: ',results);
});

//關(guān)閉連接  (關(guān)閉冰箱門(mén))
connection.end();

異步編程

回調(diào)函數(shù)

不成立的情況下:

function add(x,y){
    console.log(1);
    setTimeout(function(){
        console.log(2);
        var ret = x + y;
        return ret;
    },1000);
    console.log(3);
    //到這里執(zhí)行就結(jié)束了绘沉,不會(huì)i等到前面的定時(shí)器营搅,所以直接返回了默認(rèn)值 undefined
}

console.log(add(2,2));
// 結(jié)果是 1 3 undefined 4

使用回調(diào)函數(shù)解決:

回調(diào)函數(shù):通過(guò)一個(gè)函數(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);
});

注意:

凡是需要得到一個(gè)函數(shù)內(nèi)部異步操作的結(jié)果(setTimeout,readFile,writeFile,ajax,readdir)

這種情況必須通過(guò)   回調(diào)函數(shù) (異步API都會(huì)伴隨著一個(gè)回調(diào)函數(shù))

ajax:

基于原生XMLHttpRequest封裝get方法:

var oReq = new XMLHttpRequest();
// 當(dāng)請(qǐng)求加載成功要調(diào)用指定的函數(shù)
oReq.onload = function(){
    console.log(oReq.responseText);
}
oReq.open("GET", "請(qǐng)求路徑",true);
oReq.send();
function get(url,callback){
    var oReq = new XMLHttpRequest();
    // 當(dāng)請(qǐ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)地獄):

文件的讀取無(wú)法判斷執(zhí)行順序(文件的執(zhí)行順序是依據(jù)文件的大小來(lái)決定的)(異步api無(wú)法保證文件的執(zhí)行順序)

var fs = require('fs');

fs.readFile('./data/a.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯(cuò)誤信息打印到控制臺(tái)
        throw err;
    }
    console.log(data);
});

fs.readFile('./data/b.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯(cuò)誤信息打印到控制臺(tái)
        throw err;
    }
    console.log(data);
});

通過(guò)回調(diào)嵌套的方式來(lái)保證順序:

var fs = require('fs');

fs.readFile('./data/a.text','utf8',function(err,data){
    if(err){
        // 1 讀取失敗直接打印輸出讀取失敗
        return console.log('讀取失敗');
        // 2 拋出異常
        //      阻止程序的執(zhí)行
        //      把錯(cuò)誤信息打印到控制臺(tái)
        throw err;
    }
    console.log(data);
    fs.readFile('./data/b.text','utf8',function(err,data){
        if(err){
            // 1 讀取失敗直接打印輸出讀取失敗
            return console.log('讀取失敗');
            // 2 拋出異常
            //      阻止程序的執(zhí)行
            //      把錯(cuò)誤信息打印到控制臺(tái)
            throw err;
        }
        console.log(data);
        fs.readFile('./data/a.text','utf8',function(err,data){
            if(err){
                // 1 讀取失敗直接打印輸出讀取失敗
                return console.log('讀取失敗');
                // 2 拋出異常
                //      阻止程序的執(zhí)行
                //      把錯(cuò)誤信息打印到控制臺(tái)
                throw err;
            }
            console.log(data);
        });
    });
});

為了解決以上編碼方式帶來(lái)的問(wèn)題(回調(diào)地獄嵌套)转质,所以在EcmaScript6新增了一個(gè)API:Promise

  • Promise:承諾帖世,保證
  • Promise本身不是異步的休蟹,但往往都是內(nèi)部封裝一個(gè)異步任務(wù)

基本語(yǔ)法:

// 在EcmaScript 6中新增了一個(gè)API Promise
// Promise 是一個(gè)構(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);
            }
        });
    });
}

pReadFile('./a.txt')
    .then(function(data) {
        console.log(data);
        return pReadFile('./b.txt');
    })
    .then(function(data) {
        console.log(data);
        return pReadFile('./a.txt');
    })
    .then(function(data) {
        console.log(data);
    })

mongoose所有的API都支持Promise:

// 查詢(xún)所有
User.find()
    .then(function(data){
        console.log(data)
    })

注冊(cè):

User.findOne({username:'admin'},function(user){
    if(user){
        console.log('用戶(hù)已存在')
    } else {
        new User({
             username:'aaa',
             password:'123',
             email:'fffff'
        }).save(function(){
            console.log('注冊(cè)成功');
        })
    }
})
User.findOne({
    username:'admin'
})
    .then(function(user){
        if(user){
            // 用戶(hù)已經(jīng)存在不能注冊(cè)
            console.log('用戶(hù)已存在');
        }
        else{
            // 用戶(hù)不存在可以注冊(cè)
            return new User({
                username:'aaa',
                password:'123',
                email:'fffff'
            }).save();
        }
    })
    .then(funciton(ret){
        console.log('注冊(cè)成功');
    })

Generator

async函數(shù)

其他

修改完代碼自動(dòng)重啟

我們?cè)谶@里可以使用一個(gè)第三方命名行工具:nodemon來(lái)幫助我們解決頻繁修改代碼重啟服務(wù)器的問(wèn)題日矫。

nodemon是一個(gè)基于Node.js開(kāi)發(fā)的一個(gè)第三方命令行工具赂弓,我們使用的時(shí)候需要獨(dú)立安裝:

#在任意目錄執(zhí)行該命令都可以
#也就是說(shuō),所有需要 --global安裝的包都可以在任意目錄執(zhí)行
npm install --global nodemon
npm install -g nodemon

#如果安裝不成功的話(huà)哪轿,可以使用cnpm安裝
cnpm install -g nodemon

安裝完畢之后使用:

node app.js

#使用nodemon
nodemon app.js

只要是通過(guò)nodemon啟動(dòng)的服務(wù)盈魁,則他會(huì)監(jiān)視你的文件變化,當(dāng)文件發(fā)生變化的時(shí)候窃诉,會(huì)自動(dòng)幫你重啟服務(wù)器杨耙。

封裝異步API

回調(diào)函數(shù):獲取異步操作的結(jié)果

function fn(callback){
    // var callback = funtion(data){ console.log(data); }
    setTimeout(function(){
        var data = 'hello';
        callback(data);
    },1000);
}
// 如果需要獲取一個(gè)函數(shù)中異步操作的結(jié)果,則必須通過(guò)回調(diào)函數(shù)的方式來(lái)獲取
fn(function(data){
    console.log(data);
})

數(shù)組的遍歷方法飘痛,都是對(duì)函數(shù)作為一種參數(shù)

EcmaScript 6

參考文檔:https://es6.ruanyifeng.com/

項(xiàng)目案例

目錄結(jié)構(gòu)

.
app.js  項(xiàng)目的入口文件
controllers
models  存儲(chǔ)使用mongoose設(shè)計(jì)的數(shù)據(jù)模型
node_modules    第三方包
package.json    包描述文件
package-lock.json   第三方包版本鎖定文件(npm5之后才有)
public  公共靜態(tài)資源
routes
views   存儲(chǔ)視圖目錄

模板頁(yè)

  • 子模板
  • 模板繼承

路由設(shè)計(jì)

路由 方法 get參數(shù) post參數(shù) 是否需要登錄 備注
/ get 渲染首頁(yè)
/register(登錄) get 渲染注冊(cè)頁(yè)面
/register post email,nickname,password 處理注冊(cè)請(qǐng)求
/login get 渲染登陸界面
/login post email,password 處理登錄請(qǐng)求
/loginout get 處理退出請(qǐng)求

模型設(shè)計(jì)

功能實(shí)現(xiàn)

步驟

  • 創(chuàng)建目錄結(jié)構(gòu)
  • 整合靜態(tài)也-模板頁(yè)
    • include
    • block
    • extend
  • 設(shè)計(jì)用戶(hù)登陸珊膜,退出,注冊(cè)的路由
  • 用戶(hù)注冊(cè)
    • 先處理客戶(hù)端頁(yè)面的內(nèi)容(表單控件的name宣脉,收集表單數(shù)據(jù)车柠,發(fā)起請(qǐng)求)
    • 服務(wù)端
      • 獲取從客戶(hù)端收到的數(shù)據(jù)
      • 操作數(shù)據(jù)庫(kù)
        • 如果有錯(cuò),發(fā)送500告訴客戶(hù)端服務(wù)器錯(cuò)了‘
        • 其他的根據(jù)業(yè)務(wù)發(fā)送不同的響應(yīng)數(shù)據(jù)
  • 登錄
  • 退出

Express中間件

中間件的概念

參考文檔:http://expressjs.com/en/guide/using-middleware.html

中間件:把很復(fù)雜的事情分割成單個(gè)塑猖,然后依次有條理的執(zhí)行竹祷。就是一個(gè)中間處理環(huán)節(jié),有輸入羊苟,有輸出塑陵。

說(shuō)的通俗易懂點(diǎn)兒,中間件就是一個(gè)(從請(qǐng)求到響應(yīng)調(diào)用的方法)方法践险。

把數(shù)據(jù)從請(qǐng)求到響應(yīng)分步驟來(lái)處理猿妈,每一個(gè)步驟都是一個(gè)中間處理環(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(){
    // 解析請(qǐng)求地址中的get參數(shù)
    // var obj = url.parse(req.url,true);
    // req.query = obj.query;
    query(req,res); //中間件
    
    // 解析請(qǐng)求地址中的post參數(shù)
    req.body = {
        foo:'bar'
    }
});

if(req.url === 'xxx'){
    // 處理請(qǐng)求
    ...
}

server.listen(3000,function(){
    console.log('3000 runing...');
});

同一個(gè)請(qǐng)求對(duì)象所經(jīng)過(guò)的中間件都是同一個(gè)請(qǐng)求對(duì)象和響應(yīng)對(duì)象巍虫。

var express = require('express');
var app = express();
app.get('/abc',function(req,res,next){
    // 同一個(gè)請(qǐng)求的req和res是一樣的彭则,
    // 可以前面存儲(chǔ)下面調(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.');
});

中間件的分類(lèi):

應(yīng)用程序級(jí)別的中間件

萬(wàn)能匹配(不關(guān)心任何請(qǐng)求路徑和請(qǐng)求方法的中間件):

app.use(function(req,res,next){
    console.log('Time',Date.now());
    next();
});

關(guān)心請(qǐng)求路徑和請(qǐng)求方法的中間件:

app.use('/a',function(req,res,next){
    console.log('Time',Date.now());
    next();
});

路由級(jí)別的中間件

嚴(yán)格匹配請(qǐng)求路徑和請(qǐng)求方法的中間件

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();

// 中間件:處理請(qǐng)求,本質(zhì)就是個(gè)函數(shù)
// 在express中占遥,對(duì)中間件有幾種分類(lèi)

// 1 不關(guān)心任何請(qǐng)求路徑和請(qǐng)求方法的中間件
// 也就是說(shuō)任何請(qǐng)求都會(huì)進(jìn)入這個(gè)中間件
// 中間件本身是一個(gè)方法俯抖,該方法接收三個(gè)參數(shù)
// Request 請(qǐng)求對(duì)象
// Response 響應(yīng)對(duì)象
// next 下一個(gè)中間件
// // 全局匹配中間件
// app.use(function(req, res, next) {
//  console.log('1');
//  // 當(dāng)一個(gè)請(qǐng)求進(jìn)入中間件后
//  // 如果需要請(qǐng)求另外一個(gè)方法則需要使用next()方法
//  next();
//  // next是一個(gè)方法,用來(lái)調(diào)用下一個(gè)中間件
//  // 注意:next()方法調(diào)用下一個(gè)方法的時(shí)候瓦胎,也會(huì)匹配(不是調(diào)用緊挨著的哪一個(gè))
// });
// app.use(function(req, res, next) {
//  console.log('2');
// });

// // 2 關(guān)心請(qǐng)求路徑的中間件
// // 以/xxx開(kāi)頭的中間件
// app.use('/a',function(req, res, next) {
//  console.log(req.url);
// });

// 3 嚴(yán)格匹配請(qǐng)求方法和請(qǐng)求路徑的中間件
app.get('/',function(){
    console.log('/');
});
app.post('/a',function(){
    console.log('/a');
});

app.listen(3000, function() {
    console.log('app is running at port 3000.');
});

錯(cuò)誤處理中間件

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');
});

配置全局錯(cuò)誤處理中間件:

app.get('/a', function(req, res, next) {
    fs.readFile('.a/bc', funtion() {
        if (err) {
            // 當(dāng)調(diào)用next()傳參后芬萍,則直接進(jìn)入到全局錯(cuò)誤處理中間件方法中
            // 當(dāng)發(fā)生全局錯(cuò)誤的時(shí)候,我們可以調(diào)用next傳遞錯(cuò)誤對(duì)象
            // 然后被全局錯(cuò)誤處理中間件匹配到并進(jìn)行處理
            next(err);
        }
    })
});
//全局錯(cuò)誤處理中間件
app.use(function(err,req,res,next){
    res.status(500).json({
        err_code:500,
        message:err.message
    });
});

內(nèi)置中間件

第三方中間件

參考文檔:http://expressjs.com/en/resources/middleware.html

  • body-parser
  • compression
  • cookie-parser
  • mogran
  • response-time
  • server-static
  • session
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末搔啊,一起剝皮案震驚了整個(gè)濱河市柬祠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌负芋,老刑警劉巖漫蛔,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異旧蛾,居然都是意外死亡莽龟,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)锨天,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)毯盈,“玉大人,你說(shuō)我怎么就攤上這事病袄÷Ц常” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵益缠,是天一觀(guān)的道長(zhǎng)厂镇。 經(jīng)常有香客問(wèn)我,道長(zhǎng)左刽,這世上最難降的妖魔是什么捺信? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮欠痴,結(jié)果婚禮上迄靠,老公的妹妹穿的比我還像新娘。我一直安慰自己喇辽,他們只是感情好掌挚,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著菩咨,像睡著了一般吠式。 火紅的嫁衣襯著肌膚如雪陡厘。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天特占,我揣著相機(jī)與錄音糙置,去河邊找鬼。 笑死是目,一個(gè)胖子當(dāng)著我的面吹牛谤饭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播懊纳,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼揉抵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了嗤疯?” 一聲冷哼從身側(cè)響起冤今,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎茂缚,沒(méi)想到半個(gè)月后辟汰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡阱佛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年帖汞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凑术。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翩蘸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出淮逊,到底是詐尸還是另有隱情催首,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布泄鹏,位于F島的核電站郎任,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏备籽。R本人自食惡果不足惜舶治,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望车猬。 院中可真熱鬧霉猛,春花似錦、人聲如沸珠闰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伏嗜。三九已至坛悉,卻和暖如春伐厌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背裸影。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工挣轨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人空民。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓刃唐,卻偏偏與公主長(zhǎng)得像羞迷,于是被迫代替她去往敵國(guó)和親界轩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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

  • 一衔瓮、關(guān)于命令 常用命令: ? 二浊猾、進(jìn)程和線(xiàn)程 請(qǐng)參考python學(xué)習(xí)筆記中的線(xiàn)程和進(jìn)程 進(jìn)程:我們寫(xiě)的代碼和程序都...
    秋葵2022閱讀 353評(píng)論 0 0
  • 第一章 node簡(jiǎn)介 語(yǔ)言和環(huán)境之間的關(guān)系 語(yǔ)言,是編寫(xiě)代碼的語(yǔ)法規(guī)范热鞍。環(huán)境(平臺(tái))葫慎,提供了執(zhí)行代碼的能力語(yǔ)言必須...
    阿爾方斯閱讀 477評(píng)論 0 0
  • 個(gè)人入門(mén)學(xué)習(xí)用筆記、不過(guò)多作為參考依據(jù)薇宠。如有錯(cuò)誤歡迎斧正 目錄 簡(jiǎn)書(shū)好像不支持錨點(diǎn)偷办、復(fù)制搜索(反正也是寫(xiě)給我自己看...
    kirito_song閱讀 2,479評(píng)論 1 37
  • node.js 介紹 node.js是什么 node.js 是一個(gè)開(kāi)發(fā)平臺(tái),就像java開(kāi)發(fā)平臺(tái)...何為開(kāi)發(fā)平臺(tái)...
    小淺_閱讀 1,165評(píng)論 0 6
  • 1、首先要明白node.js是什么澄港? 1椒涯、簡(jiǎn)單來(lái)說(shuō)node就是運(yùn)行在服務(wù)端的JavaScript. 2、Node....
    Me無(wú)羨閱讀 224評(píng)論 0 0