Nodejs
node.js官網:
https://nodejs.org/en/
node.js中文:
http://nodejs.cn/
node.js社區:
https://cnodejs.org/
Node.js簡介
Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。
Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型,使其輕量又高效。
簡單的說 Node.js 就是運行在服務端的 JavaScript。
V8引擎本身就是用於Chrome瀏覽器的JS解釋部分,但是Ryan Dahl這哥們,把這個V8搬到了服務器上,用於做服務器的軟件。
Node.js是一個讓JavaScript運行在服務器端的開發平台,它讓JavaScript的觸角伸到了服務器端,可以與PHP、JSP、Python、Ruby平起平坐。
Node.js不是一種獨立的語言,與PHP、JSP、Python、Perl、Ruby的“既是語言,也是平台”不同,Node.js使用JavaScript進行編程,運行在JavaScript引擎上(V8)。
與PHP、JSP、.net等相比(PHP、JSP、.net都需要運行在服務器程序上,Apache、Naginx、Tomcat、IIS。),Node.js跳過了Apache、Naginx、Tomcat、IIS等HTTP服務器,它自己不用建設在任何服務器軟件之上。
瀏覽器、nodejs和其他服務器之間的關系
node.js特點:
單線程
在Java、PHP或者.net等服務器端語言中,會為每一個客戶端連接創建一個新的線程。而每個線程需要耗費大約2MB內存。也就是說,理論上,一個8GB內存的服務器可以同時連接的最大用戶數為4000個左右。要讓Web應用程序支持更多的用戶,就需要增加服務器的數量,而Web應用程序的硬件成本當然就上升了。
Node.js不為每個客戶連接創建一個新的線程,而僅僅使用一個線程。當有用戶連接了,就觸發一個內部事件,通過非阻塞I/O、事件驅動機制,讓Node.js程序宏觀上也是並行的。使用Node.js,一個8GB內存的服務器,可以同時處理超過4萬用戶的連接。
非阻塞 I/O(異步)
當在訪問數據庫取得數據的時候,需要一段時間。在傳統的單線程處理機制中,在執行了訪問數據庫代碼之后,整個線程都將暫停下來,等待數據庫返回結果,才能執行后面的代碼。也就是說,I/O阻塞了代碼的執行,極大地降低了程序的執行效率。
由於Node.js中采用了非阻塞型I/O機制,因此在執行了訪問數據庫的代碼之后,將立即轉而執行其后面的代碼,把數據庫返回結果的處理代碼放在回調函數中,從而提高了程序的執行效率。
當某個I/O執行完畢時,將以事件的形式通知執行I/O操作的線程,線程執行這個事件的回調函數。為了處理異步I/O,線程必須有事件循環,不斷的檢查有沒有未處理的事件,依次予以處理。
阻塞模式下,一個線程只能處理一項任務,要想提高吞吐量必須通過多線程。而非阻塞模式下,一個線程永遠在執行計算操作,這個線程的CPU核心利用率永遠是100%。
這是一種特別有哲理的解決方案:與其人多,但是好多人閑着;還不如一個人玩命,往死里干活兒。
事件驅動
在Node中,客戶端請求建立連接,提交數據等行為,會觸發相應的事件。在Node中,在一個時刻,只能執行一個事件回調函數,但是在執行一個事件回調函數的中途,可以轉而處理其他事件(比如,又有新用戶連接了),然后返回繼續執行原事件的回調函數,這種處理機制,稱為“事件循環”機制。
Node.js底層是C++(V8也是C++寫的)。底層代碼中,近半數都用於事件隊列、回調函數隊列的構建。用事件驅動來完成服務器的任務調度,用一個線程,擔負起了處理非常多的任務的使命。
Node.js是一個讓JavaScript運行在服務器端的開發平台,所以必須首先在本機安裝Node.js環境
1. 下載地址
官網術語解釋
LTS 版本:Long-term Support 版本,長期支持版,即穩定版。
Current 版本:Latest Features 版本,最新版本,新特性會在該版本中最先加入。
2. 啟動程序 node-v10.16.0-x64.msi 一直點下一步......
3. 在cmd中,輸入 node -v 就能夠查看版本號,同時,也內置了npm,通過 npm -v 查看版本號
4. 重啟電腦,系統環境變量才會起作用。
就是在系統的任何目錄下,都能運行c:\program files\nodejs里面的程序。
NPM 介紹
NPM是隨同NodeJS一起安裝的包管理工具,包的結構使您能夠輕松跟蹤依賴項和版本。
能解決NodeJS代碼部署上的很多問題,常見的使用場景有以下幾種:
允許用戶從NPM服務器下載別人編寫的第三方包到本地使用。
允許用戶從NPM服務器下載並安裝別人編寫的命令行程序到本地使用。
允許用戶將自己編寫的包或命令行程序上傳到NPM服務器供別人使用。
生成 package.json 配置文件
$ npm init
$ npm init -y 跳過所有提問
安裝包
本地安裝(當前項目能訪問到)
$ npm install 包名
全局安裝(其他項目都能訪問到)
$ npm install 包名 -global
縮寫形式
$ npm i 包名 -g
項目依賴(添加到dependencies)
$ npm install 包名 -save
項目上線需要用到的包,縮寫 $ npm install 包名 -S (大寫)
開發依賴(添加到devDependencies)
$ npm install 包名 -save-dev
開發測試需要用到的包,縮寫 $ npm install 包名 -D (大寫)
清除緩存數據
npm cache verify
如報錯 -4048 時使用
淘寶 NPM 鏡像
npm install -g cnpm --registry=https://
registry.npm.taobao.org
編寫nodejs程序
之前所編寫的javascript代碼都是在瀏覽器中運行的,因此可以直接在瀏覽器的控制器console中編寫js代碼並運行。
現在我們編寫的javascrip代碼都是在Node環境中執行,執行方式有兩種:
方式一:在window命令行環境中輸入指令node並回車,進行node的交互式環境,編寫javascript代碼執行即可。其中node交互式環境也稱之為REPL(Read Eval Print Loop-讀取評估打印循環 ),按兩次ctrl+c,可退出REPL環境。
先輸入node指令,進入node的交互式環境,在輸入js代碼。
C:\Users\qf>node
> 2+2
方式二:把javascript代碼寫在后綴為.js的文件中
如有一個hello.js文件,在window命令行中輸入node hello.js即可執行。
如在hello.js中編寫以下代碼:
var a = 2;
var b = 2;
console.log(a+b);
命令行中執行:node hello.js
注:在瀏覽器環境中全局對象是window,在node環境中全局對象變為global
搭建web服務器
步驟:
1. 加載http模塊
2. 創建http服務,
3. 服務端對象監聽request 請求事件,用於監聽客戶端的請求
4. 啟動http服務,監聽端口
參考代碼:
1. 加載http模塊 var http = require('http');
2. 創建http服務 var server = http.createServer();
3. 服務端對象監聽request 請求事件,用於監聽客戶端的請求
server.on('request',function (req, res) {
//req-請求對象 , res-響應對象
//處理客戶端請求邏輯
console.log('收到請求: '+ req.url); //用戶請求地址
res.end(); //必須結束響應,否則瀏覽器會被掛起
});
4. 啟動http服務,開始監聽3000端口
server.listen(3000, function () {
console.log('服務已經啟動,請訪問: http://localhost:3000 或 http://127.0.0.1:3000');
});
注意:
1. 在監聽request事件中,最后一定要res.end()結束響應。
2. 瀏覽器顯示中文可能是亂碼,需設置響應頭告訴瀏覽器顯示時所使用的編碼,要在res.end()之前設置
res.setHeader("Content-type","text/plain;charset=utf-8"); // 響應為純文本
res.setHeader("Content-type","text/html;charset=utf-8"); //響應為html文本
或者
Node.js是服務器的程序,寫的js語句,都將運行在服務器上。返回給客戶的,都是純htm已經處理好的
l。
nodejs 讀寫文件
使用到的文件系統模塊 var fs = require('fs');
讀文件:fs.readFile(file[, options], callback)
* 參數1:要讀取的文件路徑,必填。
* 參數2:讀取文件時的選項,比如:文件編碼utf8。選填。
* 參數3:文件讀取完畢后的回調函數,必填。
讀文件注意:
* 該操作采用異步執行
* 回調函數有兩個參數,分別是err和data
* 如果讀取文件時沒有指定編碼,返回的是二進制數據,如指定編碼utf8,會返回指定的編碼數據。
* 只要異步操作,回調函數第一個都是錯誤對象err優先
寫文件:fs.writeFile(file, data[, options], callback);
* 參數1:要寫入的文件路徑,必填。
* 參數2:要寫入的數據,必填。
* 參數3:寫入文件時的選項,比如:文件編碼。
* 參數4:文件寫入完畢后的回調函數,必填。
寫文件注意:
* 該操作采用異步執行
* 如果文件存在則替換原內容
* 默認寫入的文件編碼為utf8
* 回調函數有1個參數:err,表示在寫入文件的操作過程中是否出錯了。
* 如果出錯了err != null,成功時 err === null
* 寫入文件(文件不存在則自動創建)
注:writeFile寫入文件是先把文件內容清空在寫入,如果要追加寫入的話可以使用appendFile函數
了解其他常見模塊:
path模塊 提供用於處理文件路徑和目錄路徑的實用工具。
var path = require('path');
url模塊 用於處理與解析 URL。
var url = require('url');
querystring 模塊提供用於解析和格式化 URL 查詢字符串的實用工具。
本地服務器
npm i http-server -g
cmd 進入當前目錄 運行 http-server
CommonJS模塊化規范
概述
node應用由模塊組成,采用的commonjs模塊規范。
每一個文件就是一個模塊,擁有自己獨立的作用域,變量,以及方法等,對其他的模塊都不可見。
加載某個模塊,其實是加載該模塊的module.exports屬性,require方法用於加載模塊。
CommonJS模塊的特點:
所有代碼都運行在模塊作用域,不會污染全局作用域。
模塊可以多次加載,但是只會在第一次加載時運行一次,然后運行結果就被緩存了,以后再加載,就直接讀取緩存結果。
要想讓模塊再次運行,必須清除緩存。
加載模塊是同步的,也就是說,只有加載完成,才能執行后面的操作。
模塊加載的順序,按照其在代碼中出現的順序。
module對象
CommonJS規范規定,每個模塊內部,module變量代表當前模塊。
這個變量是一個對象,它的exports屬性(即module.exports)是對外的接口。
module.exports屬性
module.exports屬性表示當前模塊對外輸出的接口,其他文件加載該模塊,實際上就是讀取module.exports變量。
exports變量
node為每一個模塊提供了一個exports變量(可以說是一個對象),指向 module.exports。
這相當於每個模塊中都有一句這樣的命令 var exports = module.exports;這樣,在對外輸出時,可以在這個變量上添加方法。
例如:exports.add = function (r){return Math.PI * r *r};
注意:不能把exports直接指向一個值,這樣就相當於切斷了 exports 和module.exports 的關系。
例如:exports=function(x){console.log(x)};
一個模塊的對外接口,就是一個單一的值,不能使用exports輸出,必須使用 module.exports輸出。
例如:module.exports=function(x){console.log(x);};
require方法
require方法用於加載模塊文件,相當於讀入並執行一個js文件,然后返回該模塊的exports對象,沒有發現指定模塊,則就會報錯。
例如:example.js
exports.name = 'tom';
exports.age = 50;
在 同目錄下的 demo.js 文件中
var example = require('./example.js');
console.log(example.name); // tom
console.log(example.age); // 50
或者 example.js
function fn(){console.log(1)};
var name = 'tom'
module.exports = {fn:fn,name:name}
es6的對象簡寫,key 和 value 一致,可以只寫一個:module.exports = {fn,name};
在 同目錄下的 demo.js 文件中
var example = require('./example.js');
example.fn(); // 1
ES6 模塊與 CommonJS 模塊的差異
ES6 模塊與 CommonJS 模塊完全不同,它們有兩個重大差異。
第一個差異:
CommonJS 模塊輸出的是一個值的拷貝,ES6 模塊輸出的是值的引用。
CommonJS 模塊是運行時加載,ES6 模塊是編譯時輸出接口。
第二個差異:
因為 CommonJS 加載的是一個對象(即module.exports屬性),該對象只有在腳本運行完才會生成。
而 ES6 模塊不是對象,它的對外接口只是一種靜態定義,在代碼靜態解析階段就會生成。
下面重點解釋第一個差異。
CommonJS 模塊輸出的是值的拷貝,也就是說,一旦輸出一個值,模塊內部的變化就影響不到這個值。請看下面這個模塊文件lib.js的例子。
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
counter: counter,
incCounter: incCounter,
};
上面代碼輸出內部變量counter和改寫這個變量的內部方法incCounter。然后,在main.js里面加載這個模塊。
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
上面代碼說明,lib.js模塊加載以后,它的內部變化就影響不到輸出的mod.counter了。這是因為mod.counter是一個原始類型的值,會被緩存。除非寫成一個函數,才能得到內部變動后的值。
// lib.js
var counter = 3;
function incCounter() {
counter++;
}
module.exports = {
get counter() {
return counter
},
incCounter: incCounter,
};
上面代碼中,輸出的counter屬性實際上是一個取值器函數。現在再執行main.js,就可以正確讀取內部變量counter的變動了。
$ node main.js
3
4
