之前有段時間閒着無聊,去B站上簡單瞭解了一下NodeJS,下面是根據一個視頻記錄的筆記,供大家參考,錯誤之處請大家幫忙斧正。侵刪。
目錄
1.1 官網介紹:https://nodejs.org/en/
4.3 路由機制 - url模塊,path模塊,querystring ——09.js
4.4 路由機制 小程序:——小小阿帕奇(靜態資源文件) 10.js
6.2.2 require的模塊中能夠,如果有異步函數,將不會等待
0 參考教程
教程:http://www.runoob.com/nodejs/nodejs-tutorial.html
公開課教程:https://www.bilibili.com/video/av26470001?p=4 18h
教程:https://developer.mozilla.org/zh-CN/docs/Learn/Server-side/Express_Nodejs
1 NodeJs簡介
1.1 官網介紹:https://nodejs.org/en/
Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.
Node.js是一個構建在chrome's V8 JS引擎上的一個JavaScript的運行環境
Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
Node.js使用了事件驅動,非阻塞I/O模型,是輕量級的和高效的
Node.js' package ecosystem, npm, is the largest ecosystem of open source libraries in the world.
Node.js的包生態(npm),是世界上最大的開源庫生態系統
1.2 安裝NodeJs
Node.js可以在任何操作系統上安裝,底層是chrome V8引擎。
安裝方法:下載run安裝就可以
如何證明是否安裝好了呢?
- 進入terminal
- $node -v //查看安裝的版本號
爲什麼能夠輸入node命令呢?
- 在/usr/local/bin下面有node可執行程序
此時擁有了兩個運行環境 :一個node,一個瀏覽器。若用瀏覽器解析,需要在h5文件中引用js文件,才能解析。
此時,證明了nodejs是一個JavaScript runtime(js運行環境)。
Node.js特點:單線程,非阻塞異步I/O,事件驅動
Node.js使JS的觸角延伸到了服務器開發中,在node的世界中,我們關心的不再是JS操作網頁上的DOM,製作交互動畫,表單驗證……而是服務器端的事情:HTTP請求的處理,GET請求,和POST請求,數據庫增刪改查,cookie和session等等。
1.3 NodeJS哲學
這是一個帥哥爲了解決性能問題而發明了Node JS
異步I/O:絕大多數網站I/O是非常多的,I是數據的讀取Input,O是數據的輸出Output。但是在讀取I/O時,CPU就被閒置了。
2008年Google發明了Chrome V8滿足它關於高性能web服務器的想象,使用V8引擎解析JS程序,非常的快,並且V8隱性性能好,都是異步I/O,閉包特性方便,Ryan Dah1就把V8移植到服務器端。
2 開發服務器程序
2.1 helloworld 02.js
2.2 NodeJS是運行在服務器端
注意,此時查看源代碼,在頁面上查看不到(3+4),而是7,說明js運行了在服務器上,而不是前端的js。
Nodejs程序在服務器端運行。
2.3 NodeJS架構
NodeJS是一個讓JS運行在服務器端的開發平臺,它讓JS的觸角伸到了服務器端,但是Node似乎和其他服務器有點不同,如:PHP,ASP,JSP:
- Node.js不是一個獨立的語言,與PHP,Perl,python,jsp,ruby的“既是語言,也是平臺”不同,Node.JS使用JS進行編程。NodeJS是一個工具,語言仍是JS。
- 與PHP,JSP不同,Nodejs跳過來Apache,Tomcat,Nginx,IIS等HTTP服務器。它自己不用健在任何服務器軟件上,NodeJs的許多設計理念與經典架構LAMP有着很大不同,可以提供強大的伸縮能力
- Nodejs自身哲學:花最小的硬件成本,追求更高的併發和處理性能
我們使用NodeJS開發服務器:GET請求,POST請求,POST請求參數的處理,數據庫的增刪改查,Cookie,session等等。
NodeJS沒有根目錄的概念!!!
3 Node.JS特定
3.1 單線程
在Java,PHP或者.net等服務器端語言中,會爲每一個客戶端連接創建一個新的進程,而每個進程需要耗費大約2MB內存,也就是說,理論上一個8GB內存的服務器可以同時連接的最大用戶數爲4000個左右,要讓web應用程序同時支持更多的用戶,就需要增加服務器的數量,而web英語程序的硬件成本當然就上升了。
Node.js不爲每個客戶連接創建一個新的線程,而是使用一個線程。當有用戶連接,就觸發一個內部事件,通過非阻塞I/O、事件驅動機制,讓Node.js程序宏觀上也是並行的。使用NodeJS,一個8GB內存的服務器可以同時處理超過4W用戶的鏈接。
另外,單線程帶來的好處還有:OS不需要再有線程創建、銷燬的時間開銷。
證明1:nodejs是單線程的 03.js
我們發現,所有用戶共享了同一個變量a。
證明2:nodejs是單線程的 04.js
寫了一個小程序,如果因爲程序有錯誤,此時唯一的node進程將停止,此時會導致所有訪問者無法訪問
3.2 異步I/O
異步I/O:一個服務員,照顧很多客人。
當一個訪問者訪問服務器後,此時這個人就會去讀取文本文件,此時CPU並沒有被阻塞,其他人仍舊可以訪問。當這個人的文本文件讀取完畢之後,此時就會使用回調函數呈現頁面。
只要I/O越多,NodeJS宏觀上越並行。
NodeJS適合開發I/O多的任務,但不適合計算繁多的任務!
下圖:
左圖:多線程的宏觀並行,CPU經常會等待I/O結束,CPU的性能會白白浪費;右圖:單線程的宏觀並行,單線程的程序,當並行極大的時候,CPU理論上的計算能力是100%。
因爲Nodejs想在破的機器上也能夠運行,所以劍走偏鋒採用了單線程的模式,既然單線程必須異步I/O。
例如:擋在訪問數據庫取得的數據的時候,需要一段時間。在傳統的處理機制中,在執行了訪問數據庫代碼之後,整個線程都將暫停下來,等待數據庫的返回結果,才能執行後面的代碼,即:I/O阻塞了代碼的執行,極大地降低了程序的執行效率。
由於Nodejs中採用非阻塞I/O,因此在執行力訪問數據庫的代碼後,將立即轉而執行其後面的代碼,把數據庫返回的結果放在回調函數中,從而提高了程序的執行效率。
當某個I/O執行完畢後,將以事件的形式通知執行I/O操作的線程,線程執行這個事件的回調函數,爲了處理異步I/O,線程必須有事件循環,不斷地檢查有沒有未處理的時間,依次予以處理。
3.3 事件驅動
事件驅動是Nodejs的底層機制,我們只需要瞭解Nodejs不會上錯菜的原因就是事件驅動,有一個事件環。
事件環機制是Nodejs的底層機制,保證了Nodejs可以高效準確的運行,而不會紊亂。
4 路由機制
4.1 路由表
這是重點和難點
之前的案例,不管訪問3000端口的什麼網址,都能得到同樣的結果。如果要根據用戶訪問的網址,給用戶不同的顯示,就需要使用req的url屬性來進行判斷。
此時,就知道了req裏面是用戶訪問的請求信息,請求的網址是req,res是服務器的響應信息。
事實上,我們並不存在music文件夾、news文件夾,甚至我們可以僞裝一個地址,但實際上並不存在music.html文件。
更加證明了Node.js是沒有Apache的,是沒有URL和真實物理文件映射關係的,這叫做頂層路由設計。能夠支持頂層路由設計的比較流行的語言只有Node.js和Python。
甚至可以用正則表達式驗證url是否匹配某一個模式,惡補正則:
補充:
js中正則:exec(): 最好用的;search();replace();test()
結合fs模塊,做了一個小小的學生管理系統,頂層路由可以設計的非常漂亮,比如:
http://127.0.0.1:3000/student/100001
就是檢索100001號的學生的情況,比如:
http://127.0.0.1:3000/student.php?xuehao=100003
不知道高明到哪裏去了,後面將知道,這種風格的路由叫做RESTful類型的路由。
至此,頂級路由案例學完了07.js。
Noticed: res.end("")表示結束輸出流,讓網頁停止轉動。
4.2 靜態資源的文件使用 08.js
我們現在不要進入Apaxhe的世界中無法自拔,Apache是一個怎樣的世界:是一個物理文件和URL --對應的世界,比如:
但是,Nodejs沒有根目錄的概念!沒有URL和物理文件一一對應的關係!
此時,如果我們的Html頁面上,試圖插入一個圖片。
此時,才能往頁面中插入圖片。
我們要做一個靜態資源管理器,但是需要儲備知識,接下來介紹URL模塊和path模塊。
4.3 路由機制 - url模塊,path模塊,querystring ——09.js
如果一個URL比較完整,包括querystring部分(就是GET請求查詢字符串傳參部分),hash部分,那麼此時
http://127.0.0.1:3000/b.html?id=123#123
req.url是:/b.html?id=123
即:querystring屬於req.url,但是hash不屬於。
但是,我們此時要得到文件名的部分,我不想要querystring,此時可以用正則提煉,但是太麻煩。
Node中提供了內置模塊,url,path,querystring模塊,都可以服務於url識別。
代碼09.js:
網址是:
http://127.0.0.1:3000/haha/1.html?id=123&name=小明#abc
輸出:
沒有正確識別protocal協議,host主機名等等,但是能夠正確識別pathname,query,
如果加上url.parse(req.url, true),此時querystring部分將會自動變爲一個對象,方便我們存入到數據庫中。
還有兩個模塊,path和querystring模塊,都是服務於url提取的。
示意圖:
他們還有其他方法,也暫時不用預習,後面項目會用的而別兇。
4.4 路由機制 小程序:——小小阿帕奇(靜態資源文件) 10.js
創建一個文件夾myweb,我們的程序能夠自動爲文件裏面呢的文件,圖片,css,js加上路由
基本結構好思想:用戶輸入什麼URL,我就真的用fs去讀那個文件:
有幾個不好用的地方:
第一點:首先是content-type的是。
就是說如果我們訪問的是.html文件,那麼content-type="text/html",如果是image,那麼應該是"mime/jpeg".
解決方案:
第二點:不能自動識別首頁:.index文件,
比如我們輸入127.0.0.1:3000/b,希望讀取myweb中b文件夾裏面的index.html
解決方案:這種路徑都沒有拓展名,如果用戶輸入了一個url都沒有拓展名,則自動那個不全一個index.html即可。
第三點:還有一個更大的問題,就是304的問題,304狀態碼not mofified, 當文件沒有改變的時候,服務器要發出304狀態碼,拒絕傳輸文件;我們的小程序沒有這樣的識別,我們也不解決了,因爲確實太複雜,還要利用session和cookie保存鏡像。
5 小結
Q1: Nodejs是什麼?
是一個JS的runtime,運行環境。實際上就是利用chrom v8引擎,將它一直到了服務器上,用它去開發服務器程序,可以提供HTTP服務。
Q2:Nodejs的主要特點?
A:Single-thread; non-blocking IO; Event driven事件驅動。
這三個特點是相輔相成的,是必須的選擇。
Nodejs爲了在低硬件服務器條件下高併發,所以就要減少內存消耗,所以劍走偏鋒,選擇了單線程,因此必須使用非阻塞I/O,因爲只有一個線程,就必須當A用戶去I/O時,處理B的業務的事情;B業務去I/O時,處理C的事情。。。。A、B、C都有回調函數。
爲了讓A、B、C都不亂套,每個人都進行合理調度,誰先來,處理誰,不能讓B一直等待,處理C、D、E。。。。所以Node使用事件環,採用事件驅動來調度事件。
Q3: 下列那條語句是正確的,爲什麼?
A: var data = fs.readFile("./test.txt"); 錯誤
B: fs.readFile("./test.txt", function(err,data){…}); 正確
因爲fs.readFile是異步方法,事實上Node的fs模塊,mongdb模塊基本上都是異步方法。一定要記住,異步函數不能通過return返回,不能通過等號來接受數據。必須通過回調函數傳實參的模式來傳輸數據。
Q4: 說說http模塊,它有哪些功能,有哪些方法?req和res各有什麼屬性和方法?
A:http模塊用來提供http服務的,最常用的就是http.createServer((req,res)=>{}); 創建一個服務器。
req封裝了HTTP上行請求的所有信息,常用屬性req.url, req.connection.remoteAddress、 req.type等等。
Res是服務器應該給出的下行響應,常用方法是res.write(), res.write(), res.setHeder()…
Q5: 說說nodejs的頂層路由設計?
Apache和Nodejs的模式完全不一樣。
Apache天生有靜態資源服務,但是nodejs不行,nodejs必須使用路由清單給出明確的路由纔行。
Q6: 說說常用的cmd命令?
cmd命令在windows下很弱,將來我們學習linux你會發現相當強大。
$clear - 清除屏幕內容
$cd - 切換文件夾
$ls- 列出當前文件夾下的所有文件
$node - 執行node.js文件
$ctrl+C - 打斷進程
6 模塊
模塊module,指的是一些列有關係的js程序的集合
後面我們學習的MVC: model,view,control。
6.1 內置模塊
Nodejs中內置了很多模塊,可以直接用require進行引用,按照國際慣例來講,你接受的名字最好和模塊一樣。
內置模塊的引用使用require函數,引用是無路徑的、無條件的。在任何目錄下,都是通過require("http")來引用內置http模塊的,而不需要require("../http")
內置模塊是nodejs天生就有的。nodejs手冊:http://nodejs.cn/api/
後面還會學習一些模塊,注意記憶裏面常用的方法和屬性,比如:
url.parse()
querystring.parse()
6.2 自定義模塊
每一個js文件就是一個模塊,Node.js使用CMD(通用模塊定義規範)。後面我們將知道webpack、seajs也使用哪個的是cmd規範,而Angular,requirejs等使用的是amd(異步模塊定義)規範。
接下來了解Node.js模塊的一些特性,而這些特性今後在學習webpack,seajs的時候也是通用的。
6.2.1 require誰,就運行誰
結論:require誰,就會運行誰。
注意:require("./test.js")中的。./堅決不能省略。因爲如果省略了,代表讀取node_module文件夾中的文件
6.2.2 require的模塊中能夠,如果有異步函數,將不會等待
結論:require文件中,如果有異步語句,此時nodejs仍然不會死等它結束,會返回執行文件中的程序,如果文件讀取完畢,執行回調函數。
6.2.3 連續require
6.2.4 循環引用
如果A引用B,B又引用了A,會發生什麼呢?
答案:NodeJS很智能,如果B重新引用了A,此時如同沒有引用一樣,會幫你自動抑制。
6.2.5 文件夾的層次
6.3 JS文件天生是隔離作用域的
結論:在Node.js中,每個js文件是一個單獨的作用域。和DOM瀏覽器開發不一樣,瀏覽器中, var a 此時a自動成爲了window對象的屬性,此時js文件和js文件可以共享作用域。 但是nodejs中,每個js文件是個銅牆鐵壁,天生是隔離作用域的。
6.4 使用exports.xx = xx 暴露屬性出去
我們如果想要把元素、變量、函數向外暴露 ,此時可以使用exports.** = **向外暴露,
此時引用這個模塊的文件將用等號來接受,等號左邊的變量將自動成爲exports對象。
上面的例子中,var test 這個變量就是exports對象。
這種暴露非常好用,通常用於類似的功能向外暴露。比如mathtool.js:
mathtool.js
01.js
6.5 使用module.exports暴露
剛剛我們一個js文件中向外暴露了多個東西,比如sum, average,pingfang等等。但是如果一個js文件中,僅僅向外暴露一個東西,此時用module.exports不方便。比如我們要向外暴露一個類。
結構:
調用函數的時候,要使用People.People()寫法,非常不方便。
原因是什麼?因爲js文件中要暴露很多東西。
此時,Nodejs建議使用module.exports= People.
6.6 神奇的node_modules文件夾
如果我們寫一個引用,沒有寫./:
此時表示引用node_modules文件夾中的文件。
並且更爲神奇的是,node_modules文件夾裏面的模塊,在引用的時候不需要考慮路徑,只需要確保node_modules文件夾需要引用的模塊的js文件里路徑的任何祖先路徑中。
不僅如此,比如我們haha.js文件,它不是要引入test.js嗎,只要node_modules文件夾出現在任何haha.js的祖先目錄中都可以
但是,其他的非祖先文件夾不行!!!
但是有一個路徑更神奇,叫做系統環境路徑,在:
C:\Users\你的電腦名字\AppData\Roaming\npm\node_modules
天下無人不識君,這個是全局路徑。
當省略文件名的時候,會自動識別index.js文件
做對這個題目,就說明已經會了
Q: 下列的四種require寫法,實際引入的是誰?
Require("./a.js") -- 引入當前文件夾中的a.js
Require("a.js") -- 引入node_modules中的a.js文件
Require("a") --引入的是node_modules文件夾中的a文件夾的index.js文件
Require("./a") --引入的是當前文件夾中的a文件夾中的index.js文件。
7 npm的世界
我們剛剛封裝了一個數學函數mathtool很好用,此時node開發中也發現了這個事情,如果讓全球開發者貢獻自己的實用模塊那該多好。讓大家不要重複造輪子。
說一下什麼是模塊,一個math-tool就是一個模塊。但是如果5、6個js文件都是服務於數學方面的,那它們的整體又可以作爲一個大模塊。例如:
Math-tool整體就是一個大函數。
所以Nodejs主導了一個社區,叫做npm(node package management, node包管理器)。
7.1 npm使用
淘寶鏡像: https://npm.taobao.org/
此時就可以在上面尋找我們要的東西,比如現在有需求,比如:把公曆變爲農曆。第一時間就要想到,我不要重複造輪子,要去npm社區找找看!
搜索“農曆”就可以,點擊詳情,就可以看到api
直接使用npm命令就可以下載,npm是隨着node安裝的,
$npm install solarlunar
注意:一定要聯網。此時solarlunar這個模塊就會被自動的下載到你項目的node_modules文件夾。
非常神奇!此時通過查看API,可以實現我們的業務了。
7.2 依賴
很明顯,現在的項目03.js依賴了兩個別人的包,分別是:chinese-finance-number,solarLunar。顯然的,node_modules是不能刪除的,否則一定會報錯。
但是我們現在用U盤拷貝項目給別人,此時完全沒有必要去拷貝node_modules文件夾,因爲這裏面的東西在npm線上啊,npm很穩定,隨時可以下載!
npm有一個創造性的舉動,可以讓開發者聲明自己的項目的全部依賴,可以告訴別人該項目依賴什麼文件。
創建package.json中,定義項目依賴的包,如下:
將package.json放入到項目的根目錄中,再
$ npm install命令就可以自動安裝項目需要的包
所以package.json文件非常重要,就是項目的身份證!事實上,裏面還有很多配置項。
可以用:
$npm init
此時npm將會有一個表單,引導你去創建一個較爲完整的package.json文件。此時,只需要回答一些問題即可,如果默認選項則按回車即可。會幫我們生成:如下。
版本符號:
我們看看npm的手冊: https://docs.npmjs.com/files/package.json
常用舉例:
但是還是不方便,如果我們安裝一個以來的時候,能夠自動進入到package.json中,就好了!
$npm install solarlunar --save
可以幫助我們自動將依賴項solarlunar加入到package.json中。
如果想要限制版本
$npm install solarlunar#^1.0.0 --save
卸載
$npm uninstall **
總結
$npm init //幫我們創建一個package.json文件的,項目開發的第一件事就是這個。
$npm install //根據package.json文件,安裝全部的項目依賴
$npm install solarlunar //install solarlunar package
$npm install solarlunar -- save// install package and write dependencies into package.json.
7.3 全局安裝
$npm install package_name -g
此時,這個包會被安全到全局。
Mac下地址: /usr/local/lib/node_modules
可以通過$npm list -g查看所有全局安裝的模塊。
一些CLI(命令行程序)、工程化的東西將被安裝到全局。隨着學習的深入,我們將遇見。
7.4 淘寶鏡像
淘寶爲了方便中國程序員對抗GFW,就提供了淘寶鏡像cnpm。你可以用此代替官方版本(只讀),同步頻率目前5分鐘一次,以保證與官網同步。
如何設置淘寶鏡像呢?如下:
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
今後所有的npm活動操作只要用cnpm代替即可。
最後說一句,有很多包非常大,自己也依賴別人,不用怕,他們的依賴也寫在了他們自己的package.json文件中。
cnpm,npm可以深入讀取每個包的package.json, 能夠自動將其依賴都下載下來。
npm是個怎樣的世界?就是站在巨人的肩膀上,而他也站在別人的肩膀上。。。。
8 GET請求和POST請求
8.1 GET請求
Get 請求傳參數通過URL,POST請求傳參通過HTTP上行報文。GET請求不安全,方便分享網址;POST請求安全,不方便分享往回,內容信息無限長。
GET請求的參數在URL中:
Nodejs如果獲得get請求,實際上就是如何解析URL,昨天已經講過了。就是:
安裝一個小型服務器,因爲只有服務器環境才能發出Ajax請求。
$ cnpm install serve-static --save
$ cnpm install finalhandler --save
8.2 POST請求
8.3 formidable模塊
npm上可以下載一個formidable的模塊,用來處理post請求。甚至可以處理圖片、zip等多媒體文件的上傳。
APi文檔: https://npm.taobao.org/package/formidable
Step1 - install dependency
$cnpm install --save formidable
Step2 - 通過看API來學習和使用
9 Express框架入門
傳統Node.js的缺點:
- 路由不方便製作,尤其是正則表達式路由
- 靜態資源服務不方便
- 頁面呈遞不方便,沒有模板引擎
官網:https://expressjs.com/zh-cn/
Express 高度包容、快速而極簡的 Node.js Web 框架。 是一種保持最低程度規模的靈活 Node.js Web 應用程序框架,爲 Web 和移動應用程序提供一組強大的功能。
Express 提供精簡的基本 Web 應用程序功能,而不會隱藏您瞭解和青睞的 Node.js 功能。
基於Express的流行框架
9.1 基本使用
$cnpm install express --save
然後就可以寫基本的demo了,基本上都是固定動作,沒什麼好解釋的
看一下中間件的特寫:
表示當用get請求訪問/news時做的事情,此時輸出使用res.send()而不是res.end().好處是自動變成UTF-8編碼。
並且express是自動使用pathname與/news進行比較,就是說會自動過濾querystring,hash等等。
我們可以用冒號:來引導一個變量,此時特別方便做一個正則的路由:
9.2 靜態路由
一箇中間件解決問題:
App.use("/jingtai", express.static("public"));
use表示使用中間件,剛纔的get表示在get請求下使用中間件。use是無論什麼請求都會使用。
此時我們放置在public文件夾中的xx.png會被路由到/jingtai下:
http://127.0.0.1:3000/jingtai/xx.png
靜態資源表放在最後,省得攔截別的!
9.3 使用某一個頁面
例如:當我們訪問/的時候,我們就想要呈遞一個public/haha.html頁面,這是一個經常的事情
此時不需要那個邏輯了:
而是:
//express的常用的send函數
res.sendFile(頁面絕對路徑)//發送頁面給用戶
res.send(content)//發送內容給用戶
9.4 模板引擎
express可以像php一樣使用模板引擎,此時最好用的模板引擎就是ejs
全稱:Embedded JavaScript templates 嵌入式JS模板
API: https://npm.taobao.org/package/ejs
$cnpm install ejs --save
使用案例1:
發生了什麼?
此時當用戶訪問/的時候,會自動使用views文件夾中的shouye.ejs當做模板。字典就是後面傳入的json。
模板是後臺服務器填充的,訪問者休想看到源代碼。
使用案例2:
views/shouye.ejs
輸出效果:
很方便,任何JS中能夠使用的數據類型都能傳:
08.js
views/shouye.ejs