node 學習筆記.md

Node.js第一天

1. 初識Node.js

1.1 Node.js是什么

  • Node.js? is a JavaScript runtime built on Chrome's V8 JavaScript engine.
    • Node.js不是一門語言
    • Node.js不是庫(library)、不是框架(frame)
    • ==Node.js是一個JavaScript運行環境==
    • <u>簡單點來講就是Node.js可以解析和執行JavaScript代碼</u>
    • ==以前只有瀏覽器可以解析和執行JavaScript代碼==
    • 也就是說現在的JavaScript可以完全脫落瀏覽器來運行,一切都歸功于:Node.js
    • 構建于chrome的v8引擎之上
      • 代碼只是具有特定格式的字符串
      • 引擎可以幫你去解析和執行
      • chrome的V8引擎是目前工人的解析執行js代碼最快的
      • node.js作者把Google Chrome中的V8引擎移植了出來,開發了一個獨立的js運行環境
  • 瀏覽器中的JavaScript
    • ECMAscript
      • js基本語法
    • BOM
    • DOM
  • Node.js中的JavaScript
    • 沒有BOM、DOM
    • 有ECMAScript
    • 在Node這個JavaScript執行環境中為JavaScript提供了一些服務器級別的操作API
      • 例如文件的讀寫
      • 網絡服務的構建
      • 網絡通信
      • http服務器
      • 等處理(相當于開發web服務器,學習對應的api即可)
  • Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
    • event-driven 事件驅動
    • non-blocking I/O model 非阻塞IO模型(異步)
    • lightweight and efficient. 輕量和高效
    • 隨著課程慢慢學習會明白事件驅動、非阻塞IO模型
    • ==NodeJS以事件驅動著名,通過異步的編程達到高吞吐量高性能。==
  • Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
    • npm是世界上最大的開源庫生態系統
    • 絕大多數JavaScript相關的包都存放在了npm上,這樣做的目的是為了讓開發人員更方便的去下載使用
    • npm是基于node.js開發的包管理工具

1.2 ==node的API參考文檔的使用:==

  • 黑點后面代表參數,例:

    • request <http.IncomingMessage>

    如果是方法它會告訴你方法的參數是什么,是否可選的。

    上述request代表綁定函數的參數,因為它是一個事件因此肯定需要綁定一個函數,后面的連接為這個參數的類型,功能上能夠調用什么變量和方法。

  • 加()的代表方法,不加()的代表屬性

1.3 Node.js能做什么

  • web服務器后臺
  • 命令行工具
    • npm
    • git(C語言)
    • hexo(node)
    • 。。。
  • 對于前端開發工程師來講,接觸node最多的是使用它的命令行工具
    • 自己寫的很少,主要是使用別人第三方發布的
    • webpack
    • gulp
    • npm

1.4 達到目標

  • B/S編程模型
    • browser-Server
    • back-end
    • 任何服務端技術這種BS編程模型都是一樣,和語言無關
    • Node只是一個工具
  • 模塊化編程
    • RequireJS
    • SeaJS
    • css中可以使用@import('文件路徑')來實現引入外部文件
    • 以前認知的js智能通過script標簽來加載
    • 在Node中可以像@import一樣來引用加載JavaScript腳本文件
  • Node常用API
  • 異步編程
    • 回調函數
    • Promise
    • async
    • generator
  • Express web開發框架
  • ECMAScript 6
    • 在課程中穿插講解
    • 它只是一個新的語法
  • 。。。
  • 學習node不僅會幫助打開服務端黑盒子,同時有助于學習高級內容
    • Vue.js
    • React
    • angular

2. 起步

2.1. 安裝Node

  • 查看當前Node環境的版本號
  • 官網下載,LTS(長期支持版)穩定版,Current體驗版
  • 安裝,一直下一步
  • 查看,打開命令行 cmd,輸入node --version 或者node -v 查看版本
  • 環境變量

2.2 node練習

  • node只有js文件,代碼一般為 .js文件
  1. 創建編寫js文件
  2. 打開終端(命令行),定位到腳本文件所屬目錄
  3. 輸入node 文件名執行對應的文件,顯示結果

==注意:文件名不能使用node,最好也不要使用中文==

  • 解析執行JavaScript

  • 讀寫文件

    • 使用fs模塊

      var fs = require('fs');
      
    • 讀取文件

      fs.readFile('路徑', '可選編碼', function (error, data) {});//具體查看代碼
      
    • ==readFile方法的第二個參數為可選參數,規定解析文件的編碼,可選擇utf-8編碼,讓他以網頁原文件的形式讀取,不是二進制數據==

    • 寫文件

      fs.writeFile();
      

==Node.js中常常使用回調函數作為參數,因為是異步的==

  • HTTP
    • 詳見代碼。
    • ==服務器和web頁面一般使用字符串進行通信==,因此json的傳遞需要轉化為字符串
    • Ctrl+c可以關閉服務器(bash和cmd一樣)
    • 在瀏覽器查看http請求中,有一個/favicon.ico路徑,該請求是瀏覽器的默認行為,目的是請求網頁的網站頭像
    • ==響應內容只能是字符串或二進制數據(Buffer)。對象、數字、數組、布爾值都不行==,因此response.end()方法能夠響應字符串和二進制數據

3. Node中的JavaScript

什么是模塊化

  • 現實中的模塊化

    • 生產效率高
    • 維護方便,成本低
  • 程序中的模塊化

    • 開發效率高 一次編寫多次使用
    • 方便維護了(維護的成本更低)模塊之間有高耦合低內聚的特點
  • 為什么要在 程序 中使用 模塊化的開發方式

    • 命名沖突
    • 文件依賴

3.1 Node中使用模塊

  • ECMAScript
    • 沒有DOM、BOM
  • 核心模塊
  • 第三方模塊
  • 用戶自定義模塊

3.2 核心模塊

  • Node為JavaScript提供了很多服務器級別的API,這些API絕大多數都被包裝到了一個具名的核心模塊中了。

    • 例如文件操作的fs核心模塊,http服務構建http模塊,path路勁操作模塊、os操作系統信息模塊。

    • 只要提到一個核心模塊,就要想到引入該模塊。使用require[1]

      var fs = require('fs');
      var http = require('http');
      
  • ==模塊的好處:可以完全避免變量命名沖突污染的問題==

  • 但是模塊之間的通信需要使用模塊中的exports對象

  • ==使用的所有文件操作的API都是異步的,即核心模塊fs的方法。執行順序可能要放到其他執行過程的后邊執行==

3.3 用戶自定義模塊

? Node.js不能同時執行多個js文件,需要使用模塊化編程

  • 模塊化相當于同一個文件中的script標簽
  • ==模塊的功能要單一==
  • require

    • require是一個方法,作用是用來加載模塊的
    • 在Node中,模塊有三種
      • 具名的核心模塊,例如fs、http
      • 用戶自己編寫的模塊(就是一個js文件),可以使用require方法加載。==相對路徑必須加./==,否則會報錯,因為會將模塊認定為一個核心模塊

    在node中沒有全局作用域,只有==模塊作用域==,相當于兩個script標簽。require方法加載時可以省略后綴名的。

    • 推薦使用省略后綴名的寫法
    • 既然是模塊作用域,如何讓模塊與模塊之間通信

    require方法有兩個作用:

    1. 加載文件模塊并執行里面的代碼
    2. 拿到被加載文件模塊導出的接口對象

? 每個文件模塊中都提供了一個對象:exports

? exports默認是一個空對象,是require方法的返回值

  • exports
    • exports用來實現不同模塊之間的通信
    • ==每個模塊中都提供了一個exports對象,默認是一個空對象==
    • 該對象類似于原型鏈中的prototype對象,能夠向對象中添加數據,另一個模塊可以通過require方法返回的exports對象使用這些添加的數據

4. web服務器開發

4.1端口的概念

計算機中只有一個物理網卡,而且同一個局域網中,網卡的地址必須是唯一的。

  • 網卡是通過唯一的ip地址來進行定位的
  • ip地址用來定位計算機
  • 端口號用來定位具體的應用程序(所有需要聯網通信的軟件都必須具有端口號),通信的軟件占用
  • 端口號的范圍從0~65536之間
  • 在計算機中有一些默認的端口號,最好不要去占用
    • 例如http服務的80
  • 可以同時開啟多個服務,但一定要確保不同服務占用的端口號不一致。同一個端口號只能被一個程序占用
  • ==目前發現,node的網絡編程主要是通過request和response兩個對象來實現的。一般就是監聽request事件來獲取客戶端和服務端的數據。==
  • ==request代表著客戶端的信息,response代表著服務器的信息==
  • 開啟服務器流程
var http = require('http')

//1.  創建server
var server = http.createServer()

//2.  監聽Server的request請求事件,設置請求處理函數
        // 請求
            // 處理
        // 響應
        // 一個請求對應一個響應,如果一個請求過程中,已經結束響應了,則不能重發發送響應。
        // 沒有請求就沒有響應

server.on('request', function (req, res) {
    console.log(req.url)
})

// 3.綁定端口號,啟動服務

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

4.2 Content-Type

  • 在服務端默認發送的數據,其實都是utf-8編碼的內容
    但在瀏覽器不知道你是utf-8編碼的內容
    瀏覽器在不知道服務器響應內容的編碼情況下會按照==當前操作系統的默認編碼==去解析
  • ==在http協議中,Content-Type就是用來告知對方我給你發送的數據內容是什么類型==,響應和請求都可以設置
    • Content-Type的值:
      • text/plain為普通文本,瀏覽器不會解析
      • text/html為html文本,瀏覽器會當做html文件解析該文本
      • 圖片不需要指定編碼,編碼一般指定的是:字符編碼

==網址http://tool.oschina.net能夠查詢文件對應的Content-Type==

==URL==:統一資源定位符,一個url對應一個資源

5. 注意事項

  • node不管什么模塊,涉及到路徑的都是從app所在的目錄為起點的

Node.js第二天

1. 代碼規范問題

  • 采用無分號代碼風格的時候,需要注意一下情況:

    • 當一行的代碼是以:()、[]、`` `[1] 為開頭,需要在這行代碼的前面加上分號防止語法錯誤

    • 在ES6中使用`` `(反引號)包圍的字符串時,字符串中拼接變量不用使用+變量+的形式,直接在字符串中使用${}來引用變量

      str = `adadadadd${str1}ppppppp`;
      

      ?

    say()
    ;(function(){//此處需要加分號
        
    })()//這種形式絕對不會報錯
    
    • 能在一些第三方開發的代碼中一上來就以一個;開頭。
    • 不論使用什么風格的代碼,在符合上述情況的時候,最好都在開頭加上
    • 有些人也使用!或者~
    //連續調用兩個方法,規范如下
    http
      .createServer()
      .listen()
    

    ?

  • ==進程是資源的分配和調度的一個獨立單元,而線程是CPU調度的基本單元==

2. 目錄斜杠問題

  • 字符串目錄一般采用斜杠/,也可以使用\\,\反斜杠的作用是用來轉義
  • Windows的目錄采用了\來顯示目錄,這點與字符串和網址的url相反

3. Node 安裝第三方庫

  • 在需要安裝的目錄下 輸入npm install +包名,安裝到執行目錄中新建的node_modules目錄下
  • 一般安裝在與服務器代碼同級目錄下
  • node_modules目錄不能夠修改

4. 服務器端渲染

  • 在服務端使用模板引擎(見代碼)
  • 瀏覽器端的渲染在script標簽中進行,服務端的渲染在服務器端進行,服務器渲染是可以減少請求,提升頁面性能的,在實際操作時視情況選用
  • 客戶端渲染不利于SEO(爬蟲查找)搜索引擎優化,例如ajax等異步渲染(想想全部刷新的頁面一般都是服務器端渲染)
  • 服務端渲染是可以被爬蟲抓取到的,客戶端異步渲染是很難被爬蟲抓取到的
  • 真正的網站是異步和服務端渲染相結合的
  • 例如京東的商品列表,服務器喧嚷是為了SEO搜索引擎優化;商品評論為了用戶體驗,而且也不需要SEO優化,所以采用客戶端渲染
  • 服務器渲染一般會刷新整個頁面,頁面源碼會看到呈現的文字(方便SEO);客戶端渲染一般不會刷新頁面。
  • ==服務端和瀏覽器端使用模板引擎有本質區別,瀏覽器端是將要解析的模板放在script標簽找中的,服務器是將整個web頁面當做要解析的字符串,因此服務器端使用模板引擎會在沒有解析時暴露源碼內容,瀏覽器端使用模板引擎則不會出現這種情況==

5. 瀏覽器解析靜態資源

  • js的執行會阻塞DOM樹的解析和渲染
  • css的加載不會阻塞DOM樹的解析,會阻塞DOM樹的渲染

  • 瀏覽器收到HTML響應內容之后,會開始從上到下一次解析

    當在解析的過程中,如果發現:

    ? link

    ? script

    ? img

    ? iframe

    ? video

    ? audio

    等帶有src或者是href(a標簽除外)屬性的標簽(具有外鏈的資源標簽)的時候,瀏覽器會自動對這些資源發起新的請求

6. 開發文件規范問題(處理網站中的靜態資源)

  • 為了讓目錄結構保持統一清晰,約定把所有的HTML文件都放在views目錄下
  • 為了方便的統一處理靜態資源(css,img,js,第三方資源),我們約定把所有的靜態資源都存放在public目錄下
    • css目錄中存放css文件
    • img目錄下存放圖片文件
    • js目錄中存放js文件
    • lib目錄下存放第三方資源
  • 使用代碼控制,可以控制用戶訪問哪些資源。即可以訪問index.html和public目錄中的資源
  • ==客戶端的請求路徑不再使用文件路徑了(相對路徑),web頁面的請求url一般使用/(根目錄)的url地址來引用資源。服務端代碼的路徑還是相對路徑==
  • 瀏覽器在真正發送請求的時候會自動把 http://127.0.0.1:3000 拼上,所以在瀏覽器顯示的請求路徑一律寫成根路徑形式,例如/public,這種形式叫做url路徑

7. url模塊

  • url模塊能夠用于 URL 處理與解析
    • url.parse()方法能夠解析請求網址,返回一個包含各個請求數據的對象。==記住第二個參數設置為true==
    • 該方法返回的路勁也為端口號后面的路徑

==網頁中的路徑都是url路徑,不是文件路徑==

8. ==服務器讓客戶端重定向==

  • 如何通過服務器讓客戶端重定向,重定向就是為了減少代碼的書寫

    1. 狀態碼設置為302臨時重定向(還有個301為永久重定向)

      ? 通過res.statusCode

    2. 在響應頭中通過Location告訴客戶端往哪重定向

      ? 通過res.setHeader來

    3. 設置完重定向之后一定要關閉請求,res.end()

    res.statusCode = 302
    res.setHeader('Location', '/')
    res.end()
    

    ?

    如果客戶端發現收到服務器的響應的狀態碼是302,就會自動去響應頭中找Location,然后對該地址發起新的請求。所有就可以看到客戶端自動跳轉

  • 301永久重定向 瀏覽器會記住

    • 比如訪問a.com 會跳轉至b.com 下次瀏覽器不在向a.com發請求會直接跳轉至b.com
  • 302 臨時重定向 瀏覽器不會記憶

    • a.com臨時,下次瀏覽器還會請求a.com 然后告訴瀏覽器訪問b.com。每次請求都嘗試一下

9. Node中使用Console測試

  • 在任何目錄下打開cmd,輸入node回車即可進入console控制臺,可以在console中測試方法
  • 在node的console中所有核心模塊可以直接使用,不需要require引入
  • 按Ctrl+c兩次即可退出console
  • 這個專用名稱是REPL
    • read
    • eval
    • print
    • loop

10.Node 常用指令

  • 目前就一個node 進入node環境執行

11. 注意:

  • ==原生的node 用 res.end() 來結束請求;express 框架用 res.send() 結束請求。==

Node.js 第三天

知識點

  • 模塊系統
    • 核心模塊
    • 第三方模塊
    • 自己寫的模塊
    • 加載規則以及加載機制
    • 循環加載的問題
  • npm
  • package.json
  • Express
    • 第三方web開發框架
    • ==高度封裝了http模塊==
    • 提高編碼效率,更加專注于業務,而非底層細節
    • 學習過程知其所以然
  • 增刪改查
    • 使用文件來保存數據(鍛煉異步編碼),遇到業務能夠自己封裝
  • MongoDB(數據庫)
    • (所有方法都封裝好了)

軟件版本

  • 涉及到軟件工程知識
  • x.x.x
    • 第一個數字代表大版本,新增功能比較多,甚至可能去除了某些功能
    • 第二個數字:加入了新功能
    • 第三個數字:修復bug,提升了性能
    • 一般是客戶端軟件、技術框架開發者比較理解的多
    • 做網站很少涉及到版本的概念,網站目的就是快

each和forEach解析

  • jquery中each使用,一般用于遍歷jQuery選擇器選擇到的偽數組實例對象
    • $.each(數組,function(index,element){}),該方法一個參數為要操作的數組,另一個為具體執行函數
    • $(selector).each(function(index,element){}),該方法為選取元素執行函數
    • jQuery的function中的參數與原生的forEach方法剛好相反
    • each方法的作用
      1. 方便的遍歷jquery元素的
      2. 可以在不兼容forEach的低版本瀏覽器中使用jQuery的each方法
  • forEach(function(item,index){})為原生js中的方法,可以遍歷任何可以被遍歷的成員
  • ==forEach只能夠遍歷數組,each可以遍歷jquery獲取到的偽數組==
  • jQuery的each方法支持低版本的IE瀏覽器,ECMAScript5中的forEach方法不支持IE8版本以及以下的瀏覽器
  • ==$('div')的jQuery獲取元素的方法,返回的是一個偽數組,偽數組是對象不是數組,不能夠使用forEach方法,forEach()是Array.prototype原型鏈中的方法==

偽數組轉化為數組

  • 偽數組使用[].slice.call(eles)可以使用數組中的方法
  • 偽數組是一個key為數字的對象

node和PHP

  • php是文件型的
  • node定制性更強
  • node比較底層,很多功能需要自己實現。
  • 在Node中開啟的服務器,默認是黑盒子,所有資源都不允許用戶來訪問,用戶可以訪問哪些資源具體有開發人員編寫設計的代碼為準。可以處理url,php則會顯示index.html的形式

1.Node中的模塊系統

使用node編寫應用程序主要就是在使用:

  • ECMAScript語言
  • 核心模塊
    • 文件操作的fs
    • http服務的http
    • url路徑操作系統
    • path路徑處理模塊
    • os操作系統信息
  • 第三方模塊
    • art-template
    • 必須通過npm來下載可以使用
  • 自己寫 的模塊
    • 自己創建的模塊

1.1 什么是模塊化

  • 具有文件作用域
  • 通信規則
    • 加載 require
    • 導出
  • ==模塊的好處:可以完全避免變量命名沖突污染的問題==

1.2 CommonJs模塊規范

在Node中的js還有一個重要的概念:模塊系統

  • 模塊作用域
  • 使用require方法用來加載模塊
  • 使用exports接口對象用來導出模塊中的成員

如果一個模塊需要直接導出某個成員,而非掛載的方式,需要使用下面的方式

module.exports = 'hello'//這樣另一個模塊加載這個模塊就會返回'hello',而不是exports對象
1.2.1 加載 require

語法:

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

兩個作用:

  • 執行被加載模塊中的代碼
  • 得到被加載模塊中的exports導出接口對象
1.2.2 導出exports
  • Node中是模塊作用域,默認文件中所有的成員只在當前文件模塊中有效
  • 對于希望可以被其他模塊訪問的成員,我們就需要把這些公開的成員都掛載到exports接口對象中

導出多個成員(必須在對象中):

exports.a = 123
exports.b = {
    foo : 123
}

導出單個成員(拿到的就是:函數、字符串):

module.exports = 'hello'
module.exports = function(){//后者會覆蓋前者
    
}

因此,也可以如下方式導出多個成員:

module.exports = {
    //加成員
}
1.2.3 原理解析
  • 可以認為在node中,沒一個模塊都有一個自己的module對象
  • module對象中,有一個成員叫:exports成員也是一個對象
  • 也就是說需要對外導出成員,只需要把導出的成員掛載到module.exports對象中
var module = {
    exports : {
        
    }
}
//誰require這個模塊,就會得到 module.exports
//為了簡化操作,在模塊中還有一句代碼
var exports = module.exports  //exports和module.exports掛載是一樣的。這行代碼應該是在第一行
console.log(exports === module.exports)//結果為true
//默認在代碼的最后又一句:
return module.exports
//一定要記住return的是module.exports,不能通過直接修改exports影響返回的對象,因此導出單個成員需要修改module.exports對象
  • exports是module.exports的一個引用
1.2.4 require方法加載規則
  • 優先從緩存加載

  • 不會重復加載相同的模塊

  • 可以拿到其中的接口對象,不會重復執行模塊中的代碼

  • 這樣做是為了避免重復加載模塊,提高加載效率

  • require的參數為==模塊標識==

  • ==加載時要判斷模塊標識==

    1. 核心模塊
    2. 第三方模塊
      • 凡是第三方模塊都可以通過npm來下載
      • 使用的時候可以同require('包名')的方式進行加載才可以使用(非路徑形式)
      • 不可能有一個第三方名字與核心模塊名字相同
      • 查找規則(以art-template模塊為例)
        1. 先找到當前文件所處目錄中的node_modules目錄(這個目錄就是用來存放第三方模塊的)
        2. 找node_modules/art-template
        3. 找node_modules/art-template/package.json 文件
        4. 找node_modules/art-template/package.json文件中的“main”屬性
        5. mian屬性中就記錄了art-template的入口模塊
        6. 然后加載使用這個第三方包,實際上最后加載的還是文件
        7. 如果package.json文件不存在或者main指定的入口模塊也沒有,則node會自動找該目錄下的index.js作為默認備選項
        8. 若果以上所有任何一個條件都不成立,則會進入上一級目錄中的node_modules目錄查找,規則如上
        9. 如果上一級還沒有,則繼續往上上一級查找,知道磁盤根目錄還找不到,最后報錯:can not find module xxx,==反正要找到node_modules目錄==
    3. 自己寫的模塊

    ==注意:==

    • 路徑形式的模塊采用./,當前目錄不可省略,`../``上一節目錄不可省略
    • 核心模塊的加載按名字來加載,核心模塊的本質也是文件。核心模塊的文件已經被編譯到二進制文件中了,我們只需要按照名字來加載就可以了
    • 一個項目有且只有一個node_modules,不會出現多個node_modules。該目錄放在項目同目錄(項目根目錄)中,這樣的話項目中的代碼都可以加載到第三方包。

1.3 npm

  • node package manager,是一種命令行工具
  • npm install art-template jquery bootstrap(可以一次性裝幾個包)
  • install 也可以用 i代替
1.3.1 npm網站

npmjs.com npm官方網站,可以查詢包和發包

1.3.2 npm命令行工具

npm第二層含義就是一個命令行工具,只要你安裝node就已經安裝好了npm。

npm也有版本的概念(獨立的軟件)

  • npm查看版本輸入:
npm --version 
npm -v
  • npm升級
npm install --global npm//自己升級自己
  • npm初始化
npm init
npm init -y//可以跳過向導,快速生成
  • npm install
    • 一次性把dependencies選項中的依賴項全部安裝
    • ==npm i install可以使用i簡寫代替==
  • npm install 包名
    • 只下載
    • npm i 包名
  • npm安裝指定的版本
    • npm i bootstrap@版本號 (中間沒有空格)
  • npm install --save 包名
    • 下載并保存依賴項(package.json文件中的dependencies選項)
    • npm i -S 包名
  • npm uninstall 包名
    • 只刪除,如果有依賴項會依然保存
    • npm un 包名
  • npm uninstall --save 包名
    • 刪除的同時也會把依賴信息也刪除
    • npm un -S 包名
  • npm help
    • 查看使用幫助
  • npm 命令 --help
    • 查看指定命令的幫助
  • --global 為全局安裝,安裝到node的安裝目錄下,一般用于影響操作的軟件更新或替換
  • npm install 包名 --save-dev 這樣安裝的包和版本號就會存在 package.json 的 devDependencies 這個里面,--save 會會存在 dependencies 里面。簡寫為 -D
1.3.3 解決npm被墻問題
  • npm速度很慢,http://npm.taobao.org/ 淘寶的開發團隊吧npm在國內做了一個備份
  • 安裝淘寶的 cnpm:
# 在任意目錄下執行都可以
# --global表示安裝到全局,而非當前目錄,因此在任意目錄都可以執行
# --global不可以省略
npm install --global cnpm

? 接下來你安裝包的時候吧之前的npm替換成 cnpm

? 舉個例子:

# 這里走國外的npm服務器,速度較慢
npm install jquery

#使用淘寶的服務器下載:jquery
cnpm install jquery

如果不想安裝cnpm又想使用淘寶的服務器來下載:

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

但是每一次手動這樣加參數很麻煩,所以我們可以吧這個選項加入到配置文件中:

npm config set registry https://registry.npm.taobao.org
# 查看 npm 配置信息
npm config list

只要經過了上面命令的配置,則你以后所有的npm install都會默認通過淘寶的服務器來下載。

1.4 package.json

  • package.json,包描述文件,包的說明書,說明包引用了什么依賴
  • npm install art-template --save(放到包后面)或者npm install --save art-template (放到包前面,前后都可以),package.json文件中多了一個屬性"dependencies",該屬性的值的對象中保存著項目的依賴(依賴什么插件、插件的版本)
  • ==建議每一個項目都有一個package.json文件,就像說明書==
  • package.json文件可以通過npm init的方式自動初始化出來,npm init會以向導的方式(問一句答一句),提問項如下(不填的直接回車):
    • name:
    • version
    • description:(項目的描述)
    • entry point:(index.js) (項目的入口一般使用main.js作為入口,與json文件同目錄)
    • test command:(測試命令)
    • git repository:(github地址)
    • keywords:
    • author:作者
    • license: (ISC)(許可證)
    • --save 第三方包會出現"dependencies"屬性
  • 目前最有用的是"dependencies"選項,包含第三方包的依賴信息
  • 建議執行npm install +第三方包名都加上--save這個選項,用來保存(項目依賴信息)
  • 如果node_modules刪除了,只要"dependencies"選項中保存有依賴包,下次直接npm install來加載包即可

==補充:==

npm 5 以前是不會有package-lock.json這個文件的

npm 5 之后才加入這個文件

當你安裝包的時候,npm 都會生成或者更新package-lock.json文件


2. Express

  • experss : 表達,快速的

原生的==http==在某些方面表現不足以應對我們的開發需求,所以我們就需要使用框架來加快我們的開發效率,框架的目的就是提高效率,讓我們的代碼更高度統一。

  • ==注意 express 也可以使用原生的方法==
  • Express 是一個簡潔而靈活的 node.js Web應用框架, 提供了一系列強大特性幫助你創建各種 Web 應用,和豐富的 HTTP 工具。使用 Express 可以快速地搭建一個完整功能的網站。
  • 在Node中,有很多的web開發框架,我們這里以學習express為主。
  • http://expressjs.com/
//先建一個app.js
// 安裝依賴
// 初始化應用
// 引包開發

var express = require('express')

// 2.創建你的服務器應用程序
// 也就是原來的 http.createServer

var app = express()   //相當于原來的server
//當服務器收到get請求/的時候,執行回調處理函數。必須為get請求
app.get('/', function (req, res) {
    res.send('hello world')
})
//不需要一個一個再判斷了
app.get('/about', function (req, res) {
    res.send('你好') //此處的編碼問題框架已經幫我們處理好了,不需要res.setHeader()
})
//相當于server.listen
app.listen(3000, function () {
    console.log('app is running at port 3000.')
})
  • express找不到的路徑,會返回Cannot GET /a (比如說‘a’) 頁面,同時返回狀態碼404
  • express的get方法判斷的路徑為純路徑,不帶傳遞的參數和錨等內容
  • ==app.get()方法會返回app對象,可以鏈式編程,繼續執行get方法==

express的request對象

//req對象中直接包含了 原生 url 模塊中返回的方法
req.query   //返回參數對象

express公開指定的目錄(處理靜態資源)

// 公開指定目錄
//只要這樣做了,就可以直接通過 /public/xx 的方式訪問 public 目錄中的所有資源了
app.use('/public/', express.static('./public/'))  //相當于拼接的'. + 路徑'
  • 不公開目錄默認找不到路徑處理。
  • 上述代碼公開的是public文件夾下的文件,因此次用‘/public/’的形式,也可以使用/public,它只是在檢測url是否以你定義的開頭
  • ==瀏覽器的訪問路徑只有在端口號后沒有'/'的時候補一個‘/’,瀏覽器在文件路徑后面補一個‘/’,其他的路徑都是在瀏覽器地址欄看到的路徑,沒有區別。==

總結:

  • get方法是用來判斷訪問路徑,作出處理的
  • use方法是用來開發靜態資源的。
  • ==瀏覽器路徑確認是文件,會在后面自動拼接一個/==
  • 在Express中開放資源就是一個API的事兒;模板引擎在Express中也是一個API的事兒

偽數組

  • 一般偽數組的形式:(如下的一種對象)
var fakeArr = {
    0 : 'abc',
    1 : 'efg',
    2 : 'aaa',
    length : 3
}

Node.js 第四天

知識點

  • express
  • 基于文件做一套 CRUD
    • C - Creation 增加
      R - Retrieve 查詢
      U - Update 修改
      D - DELETE 刪除

文件路徑和模塊標識

  • 文件路徑/為磁盤根目錄
  • 模塊標識的./中的.不能省去

1. 修改完代碼自動重啟

? 我們這里可以使用一個第三方命令行工具:nodemon來幫助我們解決頻繁修改代碼重啟服務器問題

? nodemon是一個基于Node.js開發的一個第三方命令行工具,我們使用的時候需要獨立安裝:

npm install --global nodemon

安裝完畢之后,使用:

node aap.js

# 使用 nodemon啟動app
nodemon app.js

只要通過nodemon app.js啟動服務,則他會自動監視你的文件變化。當文件發生變化的時候,自動幫你重啟服務器。

2. express 基本路由(router)

路由(routing)是指分組從源到目的地時,決定端到端路徑的網絡范圍的進程。

  • 路由其實就是一張表。我們已經了解了 HTTP 請求的基本應用,而路由決定了由誰(指定腳本)去響應客戶端請求。
  • 這個表里面有具體的映射關系
    • 當請求'/',執行什么語句,分配什么
  • ==app.get()方法會返回app對象,可以鏈式編程,繼續執行get方法==
  • express 中的get方法就相當于一個路由,以什么路徑執行的時候,執行對應的處理函數,就是一個路由處理
  • ==千萬不要糾結端口后面的請求url是否加/開頭,不加也可以==

路由器(映射關系):

  • 請求方法
  • 請求路徑
  • 請求處理函數

get:

//當以 GET方法請求 / 的時候,執行對應的處理函數
app.get('/', function (req, res) {
    res.send('hello')
})

post:

//當以 POST 方法請求 / 的時候,執行對應的處理函數
app.post('/', function (req, res) {
    res.send('hello')
})
  • 同一路徑使用不同的請求方式,就可以讓同一個路徑被多次使用

3. express靜態服務

  1. 當以/public/開頭的時候,添加../public/目錄中尋找對應的資源
app.use('/public/', express.static('./public/'))
  1. 當省略第一個參數的時候,則可以通過省略 /public 的方式來訪問。直接寫該目錄下的資源路徑即可,例如public目錄下的index.html,url為 127.0.0.1:3000/inde.html
app.use(express.static('./public/'))
  1. 當指定第一個參數的時候,必須是/a/為開頭取代 /public/ 訪問public中的資源,是為了方便指定定制訪問路徑。例:1207.0.0.1:3000/a/index.html 才能訪問如下的開放目錄。
app.use('/a/', express.static('./public/'))

4 第四種還沒學

app.use('/static', express.static(path.join(_dirname, './public/')))

web瀏覽器中的路徑為 URL,不是傳統的路徑,是一個定位符

4. 在express中配置使用art-template模板引擎

安裝

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

配置:

app.engine('art', require('express-art-template'))
// 第一個參數標識,當前以 .art 結尾的文件的時候,使用art-template模板引擎
// 也可以把第一個參數改寫為html
app.engine('html', require('express-art-template'))

使用:

// Express 為 Response 響應對象提供了一個方法 : render
// render 方法默認是不可以使用的,但是如果配置了模板引擎就可以使用了
// res.render('html模板名', {模板數據})
// render方法中第一個參數不能寫路徑,默認會去項目中的 views 目錄查找該模板文件
// 也就是說 Express 有一個約定:開發人員把所有的視圖文件都放到 views 目錄中 
// Express 默認查找目錄就是 views 目錄
// 呈現視圖并將呈現的HTML字符串發送到客戶端
app.get('/', function (req, res) {
    res.render('404.art')
})

// 如果想要修改默認的views 為別的目錄
// app.set('views', 'render函數的默認路徑')
app.set('views', './views')


// 查找views 目錄下的文件
app.get('/admin', function (req, res) {
    res.render('admin/index.html', {
        title : '管理系統'
    })
})

==當需要直接返回頁面的時候,也可以使用render方法==

//當需要返回路徑的時候
app.get('/', function (req, res) {
    res.render('index.html')
})

5. express 中重定向的使用

//原生的重定向
res.statusCode = 302
res.setHeader('Location', '/')

//express 中封裝的重定向
res.redirect('/')

GET和POST請求獲取數據

  • res.query是通過查詢字符串來獲取參數,只能獲取get請求參數
  1. get請求數據在請求行中,post需要設置請求行
  2. GET沒有請求體,post的請求數據在請求體中

在express 中獲取表單 POST 請求體數據

  • 在Express 中沒有內置獲取表單 POST 請求體的API,這里我們需要使用一個第三方中間件body-parser

安裝:

$ npm install body-parser

配置:

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

var app = express()

//配置 body-parser
//只要加入這個配置,則在 req 請求對象上會多出一個屬性:body
//也就是說你可以直接通過 req.body 來獲取表單 POST 請求體數據了
// 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')
  //可以通過 req.body 來獲取表單 POST 請求數據
  res.end(JSON.stringify(req.body, null, 2))
})

fs.readFile()方法補充

//fs.readFile()方法讀取到的文件為二進制數據,如果想要解析該二進制數據,可以使用toString()方法,也可以使用readFile()方法第二個參數傳值為'utf-8'

==SyntaxError表示語法錯誤==

6. express 中設置狀態碼

  • express的響應對象response中有一個方法status()可以通過傳遞參數來設置響應碼,該方法還可以鏈式編程,
res.status(500).send('hello')

==數據庫中的性別gender一般使用0或1代替==

7. express注意

  • express沒有提供讀取文件的api,主要封裝了http操作的api

  • fs模塊是異步的,因此在寫讀取文件的時候,最好在當前的http操作函數中讀取文件。相當于setTimeout()方法

    例如:[圖片上傳失敗...(image-ed9523-1536318692721)]

8. Express-crud 路由設計

  • 路由設計就是請求不同的url,響應不同的結果
  • 路由的設計:考慮請求方式、請求路徑、請求參數。
請求方式 請求路徑 get請求 post請求 備注
GET /students 渲染首頁
GET /students/new 渲染新增頁面
POST /students/new name、age、gender、hobbies 處理新增請求
GET /students/edit id 渲染編輯頁面
POST /students/edit id、name、age、gender、hobbies 處理編輯請求
GET /students/delete id 處理刪除請求

路由的文件采用模塊化開發,需要單獨寫一個模塊。

  • 路由模塊放在app入口模塊的同級目錄

express 提供了一種==包裝路由==的方法:

//1. 創建一個路由容器
var router = express.Router()
//2. 把路由都掛載到 router 路由容器中
router.get('/', function(){})
......
//3. 導出router容器
module.exports = router

在app文件中使用router容器:

//1. 導入路由容器文件
var router = require('./router')
//2. 把路由容器掛載到app服務中
app.use(router)

==模塊的職責要單一,不要混寫。劃分模塊的目的就是為了增強項目代碼的可維護性,提升開發效率==

注意:

==配置模板引擎和body-parser 一定要在 app.use(router) 掛載路由之前,才能正常執行==

9. 獲取函數異步操作的結果(封裝異步API)

  • 獲取一個函數中異步操作的結果,則必須通過==回調函數==來獲取
  • 回調函數的關鍵作用

例如:

function fn (callback) {
    setTimeout(function () {
        var data = 'ddd'
        callback(data)
    }, 1000)
}
fn(function (data) {
    console.log(data)
})

只要是獲取函數中的異步函數的數據(函數中還有執行的函數),都可以采用上述方法。通過引用類型可以獲取到異步操作的結果。(試一下對象行不行) 實際運行的過程中是上面的fn函數中代用了回調函數。

注意:

==callback的運行一定要寫在函數的最后一行,要不然會阻礙后邊代碼的執行==

10. ES6 初識

ECMAscript 6 中的一個數組方法:find

該方法需要接收一個函數作為參數

當某個遍歷項符合回調函數中的遍歷條件時,find會終止遍歷,同時返回遍歷的成員。只要對比項和數組中的某一項相同時,就會返回數組中的某一項。

var stu = students.find(function (item) {
    return item.name === student.name
})

ECMAscript 6 中的一個數組方法:findIndex(),同上

11. 數字和字符串之間的轉換

  • 數字轉換成字符串

    使用:toString方法,只能字符串使用

  • 字符串轉化為數字

    使用:parseInt()方法

  • 十進制轉化成其他進制,要使用toString()方法

  • 任何進制(包括字符串)轉化成十進制都要使用parseInt()方法

==瀏覽器端渲染和服務器端渲染==

表單請求的post不需要設置請求頭。

瀏覽器腳本請求需要設置請求頭。

post只是一種請求!不是響應

請求方式

1. 表單
    2. a 標簽,href 屬性
    3. 腳本(ajax,http)

1和2屬于采用服務器端渲染的方式,3屬于瀏覽器端渲染的方式。

  • 說白了就是 服務器端加載時服務器處理完成返回一個 html 頁面直接渲染就可以呈現。瀏覽器端加載時先返回一個 HTML 頁面,然后在加載的過程中渲染需要使用的功能。

服務器端使用 template 和瀏覽器端使用 template 的區別

  • 瀏覽器端是先寫好 HTML ,引入一個 template 區域來加載。加載的過程中從 js 中提取數據。
  • 服務器端是 直接寫好模板頁面的字符串,傳遞給處理程序,然后再返回數據,瀏覽器加載。
  • 兩種加載都是渲染字符串的形式。處理完成返回字符串,渲染到頁面上。

Node.js 第五天

JavaScript的異步操作是因為其單線程、事件循環的底層機制。

1. 異步編程

注意:凡是需要得到一個函數內部異步操作的結果

  • setTimeout
  • readFile
  • writeFile
  • ajax

這種情況必須通過:回調函數來獲取數據

  • JavaScript中的事件一般都是等代碼執行完成之后才會執行的,因此函數中有綁定事件的,獲取事件中的數據一般要使用callback 回調函數的形式

2. 瀏覽器中的模塊化

瀏覽器中可以使用第三方庫實現模塊化開發,js天生不支持模塊化

  • require.js 第三方庫 AMD
  • sea.js 第三方庫 CMD

PHP直接支持require、include,因為PHP當初設計時加入了這個功能

  • 模塊作用域
  • 可以使用API 來進行文件與文件之間的依賴加載

node.js中的模塊化 CommonJS

  • CommonJS、AMD、CMD 都是民間搞出來的
  • EXMAScript 是官方規范定義。官方解決在不同的環境使用不同的 JavaScript 模塊化解決方案,所有ECMAscript 在2015年發布了 ECMAscript 2016 官方標準,支持了模塊化。雖然標準已經發布,但是有的 js 運行環境還不支持

學vue 的時候會去學習,例如:

  • less 編譯器 > css
  • es6 編譯器 > es5

目前前端的情況就是使用很多新技術,然后利用編譯器工具打包可以在低版本瀏覽器中運行。

使用新技術的目的就是為了提高效率,增加可維護性

3. app.use()

  • app.use() 涉及到中間件的問題
  • 不僅可以用來處理靜態資源,還可以配置中間件等等,很多工作
  • 中間件有一套規則

4. 新版本的npm特性(npm5 以上)

  1. 新版本的的npm不需要加 --save 命令
  2. uninstall 不加--save 會刪除依賴
  3. 當安裝包的時候,會自動創建或者是更新package-lock.json這個文件
  4. package-lock.json文件中保存著所有依賴樹(所有包)的信息,下載地址,刪除node_modules文件之后能夠根據該文件快速下載依賴。
    • 這樣重新npm install的速度就會提升
  5. 從文件來看有lock,是用來鎖定版本的
    • 如果項目依賴了某第三方庫的 1.1.1 版本,如果重新安裝有可能升級這個庫,而package-lock.json會鎖住依賴的版本,不會出現升級造成項目不可用的情況

5.條件函數的使用(封裝數組 find 方法)

  • 不論條件函數還是回調函數,函數作為參數的情況,都接收的是整個函數,參數寫成 callback 或者 conditionFunc ,不能寫成callback(a,b)這樣的形式

例:

//條件函數的使用
//封裝數組的 find 和 findIndex 方法
Array.prototype.myFind = function (conditionFunc) {
    for (var i = 0; i < this.length; i++) {
        if (conditionFunc(this[i], i)) {
            return this[i]
        }
    }
}

var ret = [1,2,3].myFind(function (item, index) {
    return item === 2
})

數組中的遍歷方法,都是對函數作為參數的一種應用。數組中遍歷的方法都有:

  • every
  • some
  • includes
  • map
  • reduce

6. sublime 中設置 js 代碼高亮

  • ctrl + shift + p , 快捷鍵 set syntax javascript

7. Express 定制 404 頁面

  • express 定制404頁面需要通過中間件來配置

  • 只需要在自己的路由之后增加一個

    app.use(function (req, res) {

    ? //所有未處理的請求路徑都會跑到這里

    })

MongoDB

1. 關系型數據庫和非關系型數據庫

表就是關系

或者說表與表之間存在關系

  • 所有的關系型數據庫都需要通過spq語言來操作
  • 所有的關系型數據庫在操作之前都需要設計表結構
  • 而且數據表還支持約束(保證數據的完整性)
    • 唯一的
    • 主鍵
    • 默認值
    • 非空
  • 非關系型數據庫非常靈活
  • 有的非關系型數據庫就是 key-value 對兒,沒有表
  • 在 MongoDB 是長的最像關系型數據庫的非關系型數據庫
    • 它也是 創建數據庫 -> 數據庫(MongoDB中的叫法)
    • 數據表 -> 集合(數組)
    • 表記錄 -> 文檔對象
  • MongoDB 不需要設計表結構
  • 也就是說你可以任意的往里面存數據,沒有結構性這么一說
  • 查看MongoDB版本 在命令行工具中輸入 mongod --version
  • 需要配置==環境變量==,復制安裝包的 bin 文件目錄,加 ; 粘貼到Windows的環境變量中

2. 啟動和關閉數據庫

啟動:

# mongodb 默認使用執行 mongod 命令所處盤符根目錄下的 /data/db 作為自己的數據存儲目錄 
# 所以在第一次執行命令之前先自己手動新建一個 /data/db (在需要執行的盤符根目錄下)
mongod

如果想要修改默認的數據存儲目錄,可以:

mongod --dbpath=數據存儲目錄路徑

停止:

#在開啟服務的控制臺,直接 Ctrl + c 即可停止
# 或者直接關閉開啟服務的控制臺也可以

3. 連接數據庫

連接:

# 默認連接本機的 MongoDB 服務
mongo

退出:

# 在連接狀態輸入 exit 退出連接
exit

4. 基本命令

  • show dbs
    • 查看數據庫列表,查看顯示所有數據庫(查看有數據的數據庫)
  • db
    • 查看當前操作的數據庫
  • use 數據庫名稱
    • 切換到指定的數據庫(如果沒有回創建)
    • 例如新建數據庫: use zbb
  • 插入數據(不設計表結構,默認每個數據自帶ID)
    • db.students.insertOne({"name":"jack"})相當于新建了一個students數據往里面加元素
  • show collections
    • 可以查看當前表里的集合
  • db.集合名.find()
    • 查看當前集合中的數據

5. 在Node中如何操作 MongoDB 數據庫

5.1 使用官方的mongodb包來操作

  • 可以查看官方文檔 github 搜索 mongodb node

5.2 使用第三方 mongoose 來操作 MongoDB 數據庫

第三方包:mongoose基于MongoDB 官方的 mongodb包再做的一次封裝.

安裝: npm i mongoose

開啟mongodb 數據庫,然后執行例程代碼

var mongoose = require('mongoose');

//連接MongoDB數據庫,引號中的第一個為本機,第二個為本機的test數據庫。
//指定連接的數據庫不需要存在,當你插入第一條數據之后就會自動被創建出來
mongoose.connect('mongodb://localhost/test', { useMongoClient: true });

mongoose.Promise = global.Promise;

// 創建一個模型
// 就是在設計數據庫
// MongoDB 是動態的,非常靈活,只需要在代碼中設計你的數據庫就可以了
// mongoose 這個包就可以讓你的設計編寫過程變得非常的簡單
var Cat = mongoose.model('Cat', { name: String });
// Cat 為表名(最終生成名稱為 cats),第二個參數為數據結構

// 實例化一個Cat 
var kitty = new Cat({ name: '喵喵'});

// 持久化保存 kitty 實例
kitty.save(function (err) {
  if (err) {
    console.log(err);
  } else {
    console.log('meow');
  }
});

5.3 MongoDB 數據庫的基本概念

按開發順序:

  • 可以有多個數據庫
  • 一個數據庫中可以有多個集合(表)
  • 一個集合中可以有多個文檔(表記錄)
  • 文檔結構很靈活,沒有任何限制
  • MongoDB 非常靈活,不需要像 MySQL 一樣先創建數據庫、表、設計表結構
    • 在這里只需要:當你需要插入數據的時候,只需要指定往那個數據庫的哪個集合操作就可以
    • 一切都有 MongoDB 來幫助自動完成建庫建表這件事
  • MongoDB 默認開啟端口 27017

理解:

{
    qq : {//數據庫
        users : [//集合
            {name:"zzz"}//文檔對象 
        ]
    },
    taobao : {
            
    }
}

mongoose 官方指南

1. 設計Schema 發布 model

  • 可以在文檔中的 model.js 中查看增刪改查的方法
// 此模塊是為了新建一個數據庫
var mongoose = require('mongoose');
var Schema = mongoose.Schema;

// 連接數據庫
mongoose.connect('mongodb://localhost/zbb')

// 相當于設計集合結構(表結構)
// 字段名稱就是表結構中的屬性名稱
// 每個屬性的值做了強制要求,都為js代碼
// 約束的目的是為了保證數據的完整性,不要有臟數據
// var blogSchema = new Schema({
//   title:  String,
//   author: String,
//   body:   String,
//   comments: [{ body: String, date: Date }],
//   date: { type: Date, default: Date.now },
//   hidden: Boolean,
//   meta: {
//     votes: Number,
//     favs:  Number
//   }
// });


// 設計約束案例:
var userSchema = new Schema({
  username: {
    type: String,
    required: true //必須要有不能為空
  },
  password: {
    type: String,
    required: true
  },
  // email: {
  //   type: String
  // }
  email: String
})

// 3. 將文檔結構發布為模型
// mongoose.model 方法就是用來將一個架構發布為 model
// 第一個參數:傳入一個大寫名詞單數字符串來表示你的數據庫名稱
//              mongoose 會自動將大寫名詞的字符串生成小寫復數的集合名稱
//              User在數據庫中會變成users 集合名稱
//  第二個參數:架構 Schema    
//  返回值 :模型構造函數       
// var Blog = mongoose.model('Blog', blogSchema)
var User = mongoose.model('User', userSchema)

// 4. 當我們游了模型構造函數之后就可以使用構造函數對users 集合中的數據操作了(增刪改查)

2. 增加數據

// 4. 當我們有了模型構造函數之后就可以使用構造函數對users 集合中的數據操作了(增刪改查)
// new 模型構造函數 參數為對象
var admin = new User({
  username: 'admin',
  password: 'aaa1'
})

// 5. 數據持久化
// 使用 .save() 方法
admin.save(function (err, ret) {
  if (err) {
    console.log('保存失敗');
  } else {
    console.log('保存成功');
    console.log(ret);  //就是剛剛保存的數據,id自動生成
  }
})

3. 查詢

查詢所有:

User.find(function (err, ret) {
  if(err) {
    console.log('查詢失敗')
  } else {
    console.log('查詢成功');
    console.log(ret);
  }
})

按條件查詢所有:

User.find({
  username: 'zs'
}, function (err, ret) {
  if(err) {
    console.log('查詢失敗')
  } else {
    console.log(ret);
  }
})

按條件查詢單個:

User.findOne({
  username: 'zs',
  password: '123'
}, function (err, ret) {
  if(err) {
    console.log('查詢失敗')
  } else {
    console.log(ret);
  }
})

4. 刪除數據

按條件刪除:

User.remove({
  username: 'zs'
}, function (err) {
  if (err) {
    console.log('刪除失敗')
  } else {
    console.log('刪除成功')
    console.log(ret);
  }
})

根據條件刪除一個:

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

根據id刪除一個:

Model.findByIdAndRemove(id, [options], [callback])

5. 更新數據

根據id更新一個:

User.findByIdAndUpdate('數據的id', {
  更新的數據
  password: '1234'
}, function (err, ret) {
  if(err) {
    console.log('更新失敗');
  } else {
    console.log('更新成功');
  }
})

剩余的更新方法查看文檔。

Node 操作 MySQL數據庫

安裝:

npm install --save mysql

未完待續。。。

異步編程

回調函數

Promise

callback hell:

[圖片上傳失敗...(image-3fee8d-1536318692721)]

無法保證順序的代碼:

var fs = require('fs')

fs.readFile('a.text', 'utf8', function(err, data) {
    if (err) {
        throw err
    }
    console.log(data)
    fs.readFile('b.text', 'utf8', function(err, data) {
        if (err) {
            throw err
        }
        console.log(data)
        fs.readFile('c.text', 'utf8', function(err, data) {
            if (err) {
                throw err
            }
            console.log(data)
        })
    })
})
注意:

之前的讀取文件 return 結束函數的方式,在開發中常常使用 throw err 的形式拋出異常

多個異步編程的情況,無法保證回調函數的執行順序(多線程調度機制),只能通過異步嵌套異步的形式保證順序。代碼太繁瑣,不好修改。

為了解決以上編碼方式帶來的問題(回調地獄嵌套),所以在ECMAscript 6 中新增了一個 API:Promise

  • Promise : 保證承諾

Promise 容器:

  • 容器中存放了一個==異步任務==

  • 任務默認有三種狀態

    • 默認狀態:pending 正在進行(之前網站響應有過這個狀態)
    • pending 狀態發生變化,變為兩種狀態其中的一種
      • Resolved 解決 成功
      • Rejected 失敗
      • 狀態改變之后不能再改變成另一種狀態

    在ECMAscript 6 中新增了一個 API Promise

    Promise 是一個構造函數

    使用:

    創建一個 Promise 容器

    1. 給別人一個承諾

      Promise 容器一旦創建,就開始執行里面的代碼

      new Promise(function () {
          //一個異步任務
      })
      

      注意:承諾本身不是異步的,只是它存放的異步任務是異步的。Promise 本身不是異步的,但是內部往往都是封裝一個異步任務

Promise 的具體使用:

[圖片上傳失敗...(image-d65b7a-1536318692721)]

var p1 = new Promise(function (resolve, reject) {
    fs.readFile('a.txt', function (err, data) {  //異步任務
        if (err) {
            //容器中的任務失敗
            //把容器中的 Pending 狀態變為 Rejected
            reject(err)
        } else {
            //承諾容器中的任務成功了
            //把容器中的 Pending 狀態改為成功 Resolved
            //也就是說這里調用的resolve 方法實際上就是then 方法傳遞的那個 function
            resolve(data)
        }
    })
})
//p1 就是那個承諾
//當p1 成功了 然后(then) 做指定的操作
//then 方法接收的第一個 function 就是容器中的 resolve 函數中的數據
//調用了reject 就相當于調用了 then 方法的第二個參數函數
p1
   .then(function (data) {
    console.log(data)
    //return 123
    // 當前成功函數 return 的結果就可以在后面的 then 中function 接收到
    //當你 return 123 后面就會接收到 123
    //return hello 后面就會接收到 'hello'
    // 沒有 return 后面收到的就是哦undefined
    // 上面這些事原理
    // 真正使用的是:我們可以 return 一個 Promise 對象
    //當 return 一個 Promise 對象的時候,后續的 then 中的方法的第一個參數會作為 p2 的 resolve函數的結果
    return p2
}, function (err) {
    console.log('讀取文件失敗', err)
})

以上情況稱為異步調用鏈式編程。

  • Promise 應用場景

    在開發的過程中,通過多個接口獲取的數據或者多個ajax請求獲取的數據,可以使用Promise 來寫

    jquery 中的ajax 方法已經支持 Promise ,可以使用 ==then(Promise對象使用then 方法)== 方法。

    在需要==不同的請求==獲取結果的時候,需要使用Promise解決方案,可以鏈式編程獲取異步處理結果。

  • ==callback 注意事項(開發中的小事)==:

    [圖片上傳失敗...(image-97a1fd-1536318692721)]

    圖中的fn() 函數調用沒有傳遞 callback 參數(會出錯),因此需要使用圖中到的解決方案

注意:

==1. 要使用 .then() 方法,必須要支持 Promise==

==2. mongoose 的查詢操作都是異步操作,都可以使用 .then 方法==

==3. .then() 方法的執行,必須要等到 Promise 的狀態改變==

中間件概念

  • middlewares

  • 例如開發一個模塊解決一個功能

    一般采取如下的形式

    module.exports = function (req, res) {
        req.body = {}
    }
    
    //在調用的模塊中使用下列函數
    query(req) //把req 對象傳遞到執行函數中
    

    調用返回的函數,傳遞req 對象就可以讓該對象擁有body 屬性。例如在express中使用 bodyParse 的配置和 template 的配置,都需要先配置之后才會給 req 對象添加一個方法

    Express 中間件(app.use的用法)

    中間件: 處理請求的,本質就是函數。

    ==中間件主要執行規則就是前面的中間件會對后面的中間件進行封堵。next() 方法也要求匹配,如果路徑不匹配也不行。next 是執行后面的,不是執行后一個==

    在 Express 中,對中間件有幾種分類。

    同一個請求所經歷的中間件都是同一個請求對象和響應對象。傳遞相同的req 對象和 res 對象。

    1. 應用程序級別中間件
    1. 不關心請求路徑的請求方法的中間件

      也就是說任何請求(不關心請求路徑和請求方法)都會進入這個中間件

      使用 app.use() 方法來調用,參數一般為函數

      app.use(function (req, res, next) {
          
      })
      

      ?

      • 中間件本身就是一個方法,該方法接收三個參數

        • Request 請求對象

        • Response 響應對象

        • next 下一個中間件,next 本身為函數,默認中間件不會執行下一個中間件,調用 next() 方法之后,會執行下一個中間件。

          app.use(function (req, res, next) {
              console.log(1)
              next()
          })
          app.use(function (req, res, next) {
              console.log(2)
          })
          
 當一個請求進入一個中間件之后,如果不調用 next 則會停留在當前中間件。所以next 是一個方法,用來調用下一個中間件的。
  1. 關心請求路徑的中間件(以 /xxx 開頭的路徑中間件)。

    app.use('/a', function (req, res, next) {
        console.log(req.url) //此處打印的 url 是出去 /a 后面的url
    })
    

    當請求路徑是以 a開頭的情況,執行function 里面的代碼

當請求進來,會從第一個中間件開始進行匹配。(代碼順序)

? 如果匹配,則進來。

? 如果請求進入中間件之后,沒有調用next() 方法,則代碼會停留在這個中間件中。

? 如果調用了next 則向后查找匹配的中間件

? 如果不匹配,則繼續判斷匹配下一個中間件。

  • 如果沒有能匹配的中間件,express 會默認輸出:Cannot GET 路徑(POST 請求則換成POST)
2. 路由級別的中間件
  1. 除了以上中間件之外,還有一種最常用的

    嚴格匹配請求方法和請求路徑的中間件

    app.get 和 app.post

    app.get('/a', function (req, res, next) {
        console.log('aaa')
        next()
    })
    app.get('/a', function (req, res, next) {
        console.log('bbb')
    })
    

    上述例子中下面的不會覆蓋上面的中間件

總結

  • express 框架下編程也可以使用原生的node編寫
  • 原生的node 使用 res.end() 方法來結束請求,express 框架使用 res.send() 方法來結束請求
  • 原生的node使用自己判斷的方式來開發靜態資源;express 框架使用 app.use() (中間件形式)方法來開放靜態資源

  1. ==```` 是ES6中新增的一種字符串包裹方式,叫做:模板字符串。它支持換行```和非常方便的拼接變量== ? ?

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 概要 64學時 3.5學分 章節安排 電子商務網站概況 HTML5+CSS3 JavaScript Node 電子...
    阿啊阿吖丁閱讀 9,290評論 0 3
  • node.js 介紹 node.js是什么 node.js 是一個開發平臺,就像java開發平臺...何為開發平臺...
    小淺_閱讀 1,175評論 0 6
  • 作者: Manuel Kiessling 翻譯: goddyzhao & GrayZhang & MondayC...
    紫月凌楓閱讀 2,386評論 5 26
  • 她彳亍在長長的古巷中,被風吹亂的發絲經淚水粘在臉上。古巷悠長,顯得有些空蕩。沒有雨,空氣里卻泛著潮濕。巷的盡頭,白...
    鯨栗閱讀 535評論 1 2
  • vscode中打開項目 系統會自動打包到dist目錄下的JS文件然后新建一個命令行窗口 運行后瀏覽器會自動打開調試...
    Retr0Amarok閱讀 1,198評論 0 0