一. NodeJs簡介
1.1 nodeJs的誕生
在Netscape設計出JavaScript後的短短幾個月,JavaScript事實上已經是前端開發的唯一标準。後來,微軟通過IE擊敗了Netscape後一統桌面,結果幾年時間,浏覽器毫無進步。
沒有競争就沒有發展。微軟認為IE6浏覽器已經非常完善,幾乎沒有可改進之處,然後解散了IE6開發團隊!而Google卻認為支援現代Web應用的新一代浏覽器才剛剛起步,尤其是浏覽器負責運作JavaScript的引擎性能還可提升10倍。先是Mozilla借助已壯烈犧牲的Netscape遺産在2002年推出了Firefox浏覽器,緊接着Apple于2003年在開源的KHTML浏覽器的基礎上推出了WebKit核心的Safari浏覽器,不過僅限于Mac平台。随後,Google也開始建立自家的浏覽器。他們也看中了WebKit核心,于是基于WebKit核心推出了Chrome浏覽器。
Chrome浏覽器是跨Windows和Mac平台的,并且,Google認為要運作現代Web應用,浏覽器必須有一個性能非常強勁的JavaScript引擎,于是Google自己開發了一個高性能JavaScript引擎,名字叫V8引擎并且開源。
有個叫Ryan Dahl的歪果仁,他的工作是用C/C++寫高性能Web服務。對于高性能,異步IO、事件驅動是基本原則,但是用C/C++寫就太痛苦了。于是這位仁兄開始設想用進階語言開發Web服務。他評估了很多種進階語言,發現很多語言雖然同時提供了同步IO和異步IO,但是開發人員一旦用了同步IO,他們就再也懶得寫異步IO了,是以,最終,Ryan瞄向了JavaScript。因為JavaScript是單線程執行,根本不能進行同步IO操作,是以,JavaScript的這一“缺陷”導緻了它隻能使用異步IO。標明了開發語言,還要有運作時引擎。這位仁兄曾考慮過自己寫一個,不過明智地放棄了,因為V8就是開源的JavaScript引擎。讓Google投資去優化V8,咱隻負責改造一下拿來用,還不用付錢,這個買賣很劃算。于是在2009年,Ryan正式推出了基于JavaScript語言和V8引擎的開源Web伺服器項目,命名為Node.js。雖然名字很土,但是,Node第一次把JavaScript帶入到後端伺服器開發,加上世界上已經有無數的JavaScript開發人員,是以Node一下子就火了起來。
1.2 nodeJs的優勢
優勢是借助JavaScript天生的事件驅動機制加V8高性能引擎,使編寫高性能Web服務輕而易舉。其次,JavaScript語言本身是完善的函數式語言,在前端開發時,開發人員往往寫得比較随意,讓人感覺JavaScript就是個“玩具語言”。但是,在Node環境下,通過子產品化的JavaScript代碼,加上函數式程式設計,并且無需考慮浏覽器相容性問題,直接使用最新的ECMAScript 6标準,可以完全滿足工程上的需求。
二. 安裝NodeJs
2.1 nodeJs版本
- 本課程中所用版本為node-v12.16.2的64位版本。
- 在Windows上安裝時務必選擇全部元件,包括勾選Add to Path。
2.2 版本驗證
- 打開指令提示符,然後輸入
,如果安裝正确,結果如圖node -v
2.3 node指令
- 在指令提示符中輸入node,此時進入nodejs的互動環境。在該環境下支援輸入任意的JavaScript代碼,例如輸入200+200回車後得到輸出結果為400。
- 連按兩次Ctrl+C退出nodeJs互動環境
三. NPM簡介
3.1 什麼是NPM
- NPM(NodeJS Package Manager)就是nodejs的包(插件)管理工具。
- NPM無需手動安裝,在nodeJs環境安裝成功後就已經自動安裝成功了NPM。
- 在指令提示符輸入
正确結果如圖npm -v
3.2 為什麼需要NPM
- 我們在開發時,會用到很多别人寫好的JavaScript代碼,比如jQuery、bootstrap等。
- 我們使用這些插件時,需要自己到官網去下載下傳代碼,解壓代碼然後再使用,非常繁瑣。
- NPM就是來解決這個問題的。開發人員把自己開發的代碼子產品打包後釋出到NPM官網。
- 使用時通過npm指令來進行安裝即可直接使用,無需關心代碼存在哪以及從哪去下載下傳。
- 再者,當我們使用A子產品,而A子產品又依賴于B子產品的時候,NPM可以根據依賴關系,自動把需要的依賴包下載下傳并管理。
3.3 更改NPM鏡像
- 由于npm的源在國外,是以國内使用者使用起來各種不友善,是以一般會把NPM資源位址改為國内的鏡像資源。目前最為主流的鏡像是淘寶網提供的NPM鏡像。
- 淘寶鏡像倉庫位址:https://registry.npm.taobao.org/
- 淘寶鏡像搜尋位址:https://npm.taobao.org/
- 使用方式有兩種:
-
安裝cnpm後使用cnpm指令。
該方式其實是安裝一個cnpm插件,安裝這個插件也是需要從國外位址來安裝,有可能失敗。安裝成功後使用才沒有後續問題。
-
更改npm配置資訊
從根本上解決下載下傳包速度過慢的問題。推薦使用。
-
3.3.1 安裝cnpm
npm install -g cnpm --registry=https://registry.npm.taobao.org
3.3.2 更改npm配置資訊
- 更改方式
npm config set registry https://registry.npm.taobao.org
- 驗證是否成功
npm config get registry
四. HelloWorld
- 桌面開發
- 桌面建立文本文檔,重命名為helloworld.js
- 滑鼠右鍵選擇編輯
- 輸入
并儲存。console.log("hello world");
- 在桌面按住shift的情況下滑鼠右鍵單擊,選擇
在此處打開Powershell視窗
- 在powershell視窗中輸入
回車(輸入he之後按tab可以自動補全代碼)node helloworld.js
- 第四部如果無法正常執行,則在說明win+r運作窗輸入cmd回車進入指令行編輯器。
- 輸入
回車,表示進入桌面目錄cd C:\Users\計算機使用者名\Desktop
- 在指令編輯行視窗中輸入
回車(輸入he之後按tab可以自動補全代碼)node helloworld.js
五. NodeJs子產品
5.1 子產品系統
5.1.1 什麼是子產品系統
在計算機程式的開發過程中,随着程式代碼越寫越多,在一個檔案裡代碼就會越來越長,越來越不容易維護。為了編寫可維護的代碼,我們把很多函數分組,分别放到不同的檔案裡,這樣,每個檔案包含的代碼就相對較少,很多程式設計語言都采用這種組織代碼的方式。在Node環境中,一個.js檔案就稱之為一個子產品(module)。
5.1.2 子產品系統的優勢
- 提高代碼的可維護性
- 提高代碼的可複用性
- 避免沖突
5.1.3 自定義子產品
- 聲明自定義子產品
/*自定義函數test*/
function test(name) {
console.log(`hello${name}`);
}
/*子產品輸出*/
module.exports=test;
- 使用自定義子產品
/*require函數表示引入子產品,參數為要引入子產品的位址,可以是相對路徑或絕對路徑*/
// 注意,在nodejs中引入子產品使用的是require函數,在浏覽器中使用的import函數。
// 此處引入檔案時同檔案夾下也必須使用以./開頭表示的相對路徑,否則會報錯。
// 如果require中直接寫Hello,那麼Node會依次從内置子產品、全局子產品和目前子產品下尋找Hello.js
//子產品與子產品之間互相不幹擾,其底層實作原理是閉包。
let show = require('./Hello');
/*聲明的變量show可以和require引入的子產品中通過module.exports輸出的函數名不同*/
show("張三");
/*目前子產品中繼續聲明test函數,和Hello子產品中的test是沒有關系的,不會産生沖突*/
function test() {
console.log("第二個test")
}
test();
- module.exports可以通過封裝對象來傳回多個值
function hello() {
console.log('Hello, world!');
}
function greet(name) {
console.log('Hello, ' + name + '!');
}
module.exports = {
hello: hello,
greet: greet
};
5.2 全局對象子產品
5.2.1 全局對象Global和Window對象
NodeJs 中有一個特殊的對象,稱為全局對象(Global Object),它及其所有屬性都可以在程式的任何地方通路,即全局變量。
在浏覽器 JavaScript 中,window 是全局對象, 而 Node.js 中的全局對象是 global,所有全局變量(除了 global 本身以外)都是 global 對象的屬性。
5.2.2 常用全局對象
- __filename 輸出目前執行腳本的檔案名,包含路徑。
- __dirname 輸出目前執行腳本所在目錄,包含路徑。
- setTimeout和clearTimeout
- setInterval和clearInterval
- console
- console.log([data][, …])向标準輸出流列印字元并以換行符結束。該方法接收若幹 個參數,如果隻有一個參數,則輸出這個參數的字元串形式。如果有多個參數,則 以類似于C 語言 printf() 指令的格式輸出。
- console.info([data][, …])該指令的作用是傳回資訊性消息,這個指令與console.log差别并不大,除了在chrome中隻會輸出文字外,其餘的會顯示一個藍色的驚歎号。
- console.error([data][, …])輸出錯誤消息的。控制台在出現錯誤時會顯示是紅色的叉子。
- console.warn([data][, …])輸出警告消息。控制台出現有黃色的驚歎号。
- console.dir(obj[, options])用來對一個對象進行檢查(inspect),并以易于閱讀和列印的格式顯示。
- console.time(label)輸出時間,表示計時開始。
- console.timeEnd(label)結束時間,表示計時結束。
5.3 網絡子產品
5.3.1 http請求示例
/*引入http子產品*/
let http = require('http');
/**
* 建立服務
* request 請求傳入内容
* response 請求輸出内容
*/
let server=http.createServer(function (request,response) {
/*進行中文亂碼*/
request.setEncoding("utf-8");
response.writeHead(200,{"Content-Type":"text/html;charset=utf-8"});
/*處理多請求一次的bug*/
if(request.url!=='/favicon.ico'){
/*輸出html内容到頁面*/
response.write("<h1>hello world</h1>");
/*輸出文本内容到頁面*/
response.write("你好世界");
}
/*關閉輸出流,如果不調用end函數,則頁面會一直處于重新整理狀态*/
response.end();
});
/*設定監聽端口*/
/*端口号範圍:0-65535,1023以内的端口号盡量不要使用!!!*/
server.listen(8888);
console.log(`啟動成功:http://localhost:8888`);
console.log(`常用預設端口号:https://blog.csdn.net/qiucheng_198806/article/details/87375505`);
5.3.2 get請求處理參數
const http = require('http');
const url = require('url');
http.createServer(function(request, response){
request.writeHead(200, {'Content-Type': 'text/json;charset=utf-8'});
// 解析 url 參數
const params = url.parse(request.url, true).query;
response.write("網站名:" + params.name);
response.write("\n");
response.write("網站 URL:" + params.url);
response.end();
}).listen(8888);
console.log("http://localhost:8888/user?name=zhangsan&url=http://www.zhihu.com");
5.3.3 post請求處理參數
const http = require('http');
const querystring = require('querystring');
http.createServer(function(request, response){
//設定允許跨域的域名,*代表允許任意域名跨域
request.header("Access-Control-Allow-Origin","*");
// 定義了一個post變量,用于暫存請求體的資訊
let post = '';
// 通過req的data事件監聽函數,每當接受到請求體的資料,就累加到post變量中
request.on('data', function(chunk){
post += chunk;
});
// 在end事件觸發後,通過querystring.parse将post解析為真正的POST請求格式,然後向用戶端傳回。
request.on('end', function(){
post = querystring.parse(post);
console.log(post);
response.end();
});
}).listen(8888);
- 頁面代碼如下
<form action="http://localhost:8888" method="post">
<div>
<label for="username">使用者名</label>
<input type="text" name="username" id="username">
</div>
<div>
<label for="password">密碼</label>
<input type="password" name="password" id="password">
</div>
<div>
<button type="submit">送出</button>
</div>
</form>
5.4 資料庫子產品
5.4.1 資料庫子產品為第三方子產品,需要單獨安裝
- 在目前項目檔案夾内打開指令行,輸入
回車,會生成package.json檔案。npm install mysql
- 輸入
回車,會生成package-lock.json檔案。npm install
- 此時可在目前項目内的js腳本檔案中使用mysql庫。
5.4.2 示例代碼
/*引入mysql子產品*/
const mysql=require('mysql');
/*配置資料庫連接配接資訊*/
const connection=mysql.createConnection({
host:"localhost", /*連接配接位址*/
user:"root", /*使用者名*/
password:"root", /*密碼*/
database:"test" /*被操作的資料庫名*/
});
/*打開連接配接*/
connection.connect();
/*初始化SQL語句*/
let sql="select * from stu where id = ?";
/**
* 執行SQL
* 參數1:要執行的sql語句
* 參數2:sql語句中占位符的内容數組
* 參數3:回調函數
* 回調參數1:是否異常,無異常傳回null
* 回調參數2:執行結果,查詢操作傳回結果集合數組,增删改傳回受影響行數(affectedRows)
* 回調參數3:資料庫資訊,一般不用
*/
connection.query(sql,[1],function (error,results,field) {
if(error){
throw error;
}
console.log(results[0].name);
console.log(field);
});
/*關閉資料庫連接配接*/
connection.end();
5.5 UUId子產品
這個子產品我感覺還是使用的比較多的,因為我的個人習慣是插入資料庫的時候使用的id主鍵是一串随機數,是以這個子產品我使用的也比較多,這個還是要看個人習慣,畢竟我現在所寫的東西涉及的資料量過小,效率什麼的還是和自增主鍵差不多,其實還有一個是介于自增主鍵和UUID之間的一個算法,叫雪花算法,這個我沒有具體了解過,隻知道有這麼個東西,等以後有需要了解的時候會更新一篇部落格給大家講解
5.5.1 具體步驟
- 在目前項目檔案夾内打開指令行,輸入 `npm install node-uuid 回車,會生成package.json檔案。
- 此時可在目前項目内的js腳本檔案中使用uuid庫。
5.5.2 代碼實作
const uuid = require('node-uuid');
let createuuid1 = uuid.v1().replace(/-/g,"").toUpperCase();
let createuuid2 = uuid.v4().replace(/-/g,"").toUpperCase();
console.log(createuuid1);
console.log(createuuid2);
這樣就可以生成一個随機的uuid值了
v1和v4的差別
- 兩個都是根據時間戳擷取
- 如果同樣時間生成的話v1方法生成的UUID相似度會比較高
- v4方法生成的UUID更偏向于Java中的UUID工具類,每次生成的值都會有很大的差别,辨識度也比較高,但是會有重複的機率
v1方法生成的UUID截圖 下面是v4方法生成的uuid截圖
我個人是偏向于使用v4方法的,個人習慣原因,當然使用v1方法也是挺不錯的選擇
以上就是NodeJS的基礎部分,這篇文章裡面僅僅展現了一些基礎的知識,一些比較深的知識沒有涉及到,僅限于入門階段,當然裡面有些觀點可能也有不對的地方,大家看後如果發現什麼觀點有疑問可以多多提出,大家一起學習,共同進步!!!