談談對Node的理解
Node.js 在瀏覽器外運行V8 JavaScript引擎,單線程 非阻塞I/O 事件驅動,適應於數據高並發,適合多請求,但不適合高運算,有權限讀取操作系統級別的API,無法直接渲染靜態頁面,提供靜態服務,沒有根目錄的概念,必須通過路由程序指定文件才能渲染文件,比其他服務端性能更好,速度更快,npm 倉庫,常用框架:Express,koa,Socket.io,AdonisJs,NestJS
什么是gulp?作用?機制是什么?常用命令有哪些?
gulp是基於node的自動化構建工具
作用:
1 自動壓縮JS文件
2 自動壓縮CSS文件
3 自動合並文件
4 自動編譯sass
5 自動壓縮圖片
6 自動刷新瀏覽器
機制:
Unix操作系統的管道(pipe)思想 前一級輸出 后一級輸入
常用命令:
.src 輸出(Emits)符合所提供的匹配模式(glob)或者匹配模式的數組(array of globs)的文件。 將返回一個 Vinyl files 的 stream 它可以被 piped 到別的插件中。
.watch 監視文件,並且可以在文件發生改動時候做一些事情。它總會返回一個 EventEmitter 來發射(emit) change 事件。
.dest 能被 pipe 進來,並且將會寫文件。並且重新輸出(emits)所有數據,因此你可以將它 pipe 到多個文件夾。如果某文件夾不存在,將會自動創建它。
.pipe 傳入方法的是一個function,這個function作用無非是接受上一個流(stream)的結果,並返回一個處理后流的結果(返回值應該是一個stream對象)。
.task 定義一個使用 Orchestrator 實現的任務(task)
如何判斷當前腳本運行在瀏覽器還是node環境中?
this === window ? 'browser' : 'node',通過判斷Global對象是否為window,如果不為window,當前腳本沒有運行在瀏覽器中
node.js有哪些常用模塊?
util是node 里面一個工具模塊,node里面幾乎所有的模塊都會用到這個模塊
功能:
1:實現繼承這是主要功能
2:實現對象的完整輸出
3:實現判斷數據類型
path模塊
功能:格式規范化路徑
fs模塊
功能:
1:操作文件
2:操作目錄
http模塊:用於搭建HTTP服務端和客戶端
url模塊:用戶解析和處理URL字符串
url.parse(將url字符串解析並返回一個url的對象)
url.format(將url對象編程一個url字符串並返回)
url.resolve(將url中的參數用/進行拼接)
zlib模塊:提供了用Gzip和Deflate/Inflate實現的壓縮功能
socket.io: 實現客服端與服務端之間的實時通信方式
uglify-js: 用來壓縮合並js文件
child_process:新建子進程。
querystring:解析URL中的查詢字符串。
crypto:提供加密和解密功能。
Express框架的核心特性是什么
1.可以設置中間件來響應http請求
2.定義了路由表用於執行不同的HTTP請求動作
3.可以通過向模板傳遞參數來動態渲染html頁面
對Node的思想一切皆異步的理解
node本身就是非阻塞I/O,與其他后端編程思想不同,雖然php, python, java中也有異步方法,但是編程人員的思想是同步的,node的思想目的是可以讓開發者輕松編寫高性能的web服務端,而不會通過同步思想api阻塞了服務器從而影響性能。而且node.js大部分api都是異步的,只有小量同步api,這與其他大部分語言剛好相反。
node如何實現異步非阻塞(I/O)
在node中,I/O(輸入輸出)是異步非堵塞的關鍵,I/O操作通常比較耗時但不會獨占CPU,典型的I/O比如文件讀寫,遠程數據庫讀寫,網絡請求等,如果用同步API來進行I/O操作,在返回結果之前就只能等待,此時阻塞代碼會霸占cpu,導致本進程所有代碼都等待,而node.js里面的I/O API都是不會霸占CPU的(原因:node中的核心庫libuv會將建立的所有I/O操作內容綁定到單個線程上。只要每個事件循環在不同的線程中,就可以運行多個事件循環,libuv為Node.js提供了跨平台、線程池、事件池、異步I/O等能力),所以是非阻塞的。拿JS中的setTimeout來打比方,當用戶使用setTimeout時,JS會開辟出一個異步線程池,與主線程分開執行,結果就是之前的代碼繼續執行,setTimeout的代碼延時執行,等成功后再調用主線程的方法
node中的exports如何實現的,它和module.exports有什么關系
exports實現:exports = module.exports = {};就好像是var a = { } var b = a,看上去沒有太大區別,但使用起來卻又不同
module是一個對象,當我們在控制台輸入node並執行,在node中執行module或者執行js文件打印module時會發現以下log
Module {
id: '<repl>',
path: '.',
exports: {},
parent: undefined,
filename: null,
loaded: false,
children: [],
paths: [
...
]
}
不難發現,module是Module的實例,exports是其中一個屬性,也就是說當你在node中執行一個js文件或者使用require引入模塊時,nodejs都會新建一個var module = new Module(),並執行exports = module.exports,這也就是為什么直接打印exports和exports時,控制台不會報錯,如果在node中執行以下代碼,就能清楚的看出這二者的引用關系了
console.log(module.exports) // {}
console.log(exports) // {}
module.exports.name = '張三'
exports.age = 22
console.log(module.exports) // { name: '張三', age: 22 }
console.log(exports) // { name: '張三', age: 22 }
談談Node.js加載模塊機制
node.js中模塊有兩種類型:核心模塊和文件模塊
核心模塊直接使用名稱獲取,文件模塊只能按照路徑加載(可以省略默認的.js拓展名,不是js文件的話需要顯示聲明書寫)
模塊加載規則:
- 核心模塊優先級最高,直接使用名字加載,在有命名沖突的時候首先加載核心模塊
- 可通過絕對路徑和相對路徑查找
- 查找node_modules目錄,我們知道在調用 npm install <name> 命令的時候會在當前目錄下創建node_module目錄(如果不存在) 安裝模塊,當 require 遇到一個既不是核心模塊,又不是以路徑形式表示的模塊名稱時,會試圖 在當前目錄下的 node_modules 目錄中來查找是不是有這樣一個模塊。如果沒有找到,則會 在當前目錄的上一層中的 node_modules 目錄中繼續查找,反復執行這一過程,直到遇到根 目錄為止
對Node的優點和缺點提出了自己的看法
優點:因為Node是基於事件驅動和無阻塞的,所以非常適合處理並發請求, 因此構建在Node上的代理服務器相比其他技術實現(如Ruby)的服務器表現要好得多。 此外,與Node代理服務器交互的客戶端代碼是由javascript語言編寫的, 因此客戶端和服務器端都用同一種語言編寫,這是非常美妙的事情。
缺點:Node是一個相對新的開源項目,所以不太穩定,它總是一直在變, 而且缺少足夠多的第三方庫支持(第三方庫現在已經很豐富了,所以這個缺點可以說不存在了)。看起來,就像是Ruby/Rails當年的樣子。
Node.js的適用場景
- 實時應用:如在線聊天,實時通知推送等等(如socket.io)
- 分布式應用:通過高效的並行I/O使用已有的數據
- 工具類應用:海量的工具,小到前端壓縮部署(如grunt),大到桌面圖形界面應用程序
- 游戲類應用:游戲領域對實時和並發有很高的要求(如網易的pomelo框架)
- 利用穩定接口提升Web渲染能力
- 前后端編程語言環境統一:前端開發人員可以非常快速地切入到服務器端的開發(如著名的純Javascript全棧式MEAN架構)
原生Node如何解決跨域
const http = require('http');
http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
}).listen(8080);
反向代理是什么,如何實現
反向代理是指代理服務器來接受客戶端的網絡訪問連接請求,然后服務器將請求有策略的轉發給網絡中實際工作的業務服務器,並將從業務服務器處理的結果,返回給網絡上發起連接請求的客戶端
實現過程(這里的目標服務器是用getman產生的假數據):
前端部分:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$.post('http://127.0.0.1:1024', {// 訪問代理服務端,獲取目標服務器的數據
token: '1234'
}, function (res) {
console.log(JSON.parse(res))
})
</script>
</body>
</html>
//服務端
const http = require('http');
const https = require('https')
const reqOption = { // getman產生的虛擬數據的請求地址
protocol: 'https:',
host: 'getman.cn',
path: '/mock/shopList',
method: 'POST',
headers: {
"content-type": "application/json",
}
}
let server = http.createServer((req, res) => {
// 寫請求頭,解決跨域
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500'); // 若允許所有域名和ip,則設置成*
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With");
res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
let _data = ''
req.on('data', data => _data += data)
req.on('end', () => {
proxyApi(_data).then((_res) => { // 服務端收到前端請求后,請求目標服務器,將結果返回至前端
res.write(_res)
res.end()
})
})
})
function proxyApi(_data) {
return new Promise((resolve, reject) => {
let req = https.request(reqOption, (res) => {
let data = '';
res.on('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
resolve(data)
});
})
req.write(_data)
req.end();
})
}
server.listen(1024, () => console.log("1024服務開啟,開始偵聽"));
Node事件循環的流程是什么,在事件循環中,如何判斷是否有事件需要處理呢
事件循環的流程:在進程啟動時,node會生成一個循環(類似於while(true)),每執行一次循環被稱為一次Tick,每次的循環體Tick的過程會對事件進行判斷,若發現存在事件,則執行相關操作,並進入下一個Tick,如果不再有事件,則退出進程
判斷Tick是否有事件:node中的Tick通過觀察者判斷是否有需要處理的事件,主要來源於網絡請求的網絡I/O觀察者,和文件操作的文件I/O觀察者,事件循環從觀察者中取出事件並處理
webSocket相對http的優勢
- 客戶端與服務器只需要一個TCP連接,比http長輪詢使用更少的連接
- webSocket服務端可以推送數據到客戶端
- 更輕量的協議頭,減少數據傳輸量
簡述明文、密文、密碼、密鑰、對稱加密、非對稱加密、摘要、數字簽名、數字證書的概念
- 明文(plaintext)是加密之前的原始數據
- 密文是通過密碼(cipher)運算后得到的結果成為密文(ciphertext)
- 密碼學中的密碼(cipher)和我們日常生活中所說的密碼不太一樣,計算機術語 ' 密碼 cipher ' 是一種用於加密或者解密的算法,而我們日常所使用的 密碼 (password)是一種口令,它是用於認證用途的一組文本字符串,這里我們要討論的是前者:cipher。
- 密鑰(key)是一種參數,它是在明文轉換為密文或將密文轉換為明文的算法中輸入的參數。密鑰分為對稱密鑰與非對稱密鑰,分別應用在對稱加密和非對稱加密上。
- 對稱密鑰(Symmetric-key algorithm)又叫做私鑰加密,即信息的發送方和接收方使用同一個密鑰去加密和解密數據。對稱加密的特點是算法公開、加密和解密速度快,適合於對大數據量進行加密,常見的對稱加密算法有DES、3DES、TDEA、Blowfish、RC5和IDEA。
- 非對稱密鑰(public-key cryptography)也叫做公鑰加密。非對稱加密與對稱加密相比,其安全性更好。對稱加密的通信雙方使用相同的密鑰,如果一方的密鑰遭泄露,那么整個通信就會被破解。而非對稱加密使用一對密鑰,即公鑰和私鑰,且二者成對出現。私鑰被自己保存,不能對外泄露。公鑰指的是公共的密鑰,任何人都可以獲得該密鑰。用公鑰或私鑰中的任何一個進行加密,用另一個進行解密。
- 摘要算法又稱哈希/散列算法。它通過一個函數,把任意長度的數據轉換為一個長度固定的數據串(通常用16進制的字符串表示)。算法不可逆。
- 數據在瀏覽器和服務器之間傳輸時,有可能在傳輸過程中被冒充的盜賊把內容替換了,那么如何保證數據是真實服務器發送的而不被調包呢,同時如何保證傳輸的數據沒有被人篡改呢,要解決這兩個問題就必須用到數字簽名,數字簽名就如同日常生活的中的簽名一樣,一旦在合同書上落下了你的大名,從法律意義上就確定是你本人簽的字兒,這是任何人都沒法仿造的,因為這是你專有的手跡,任何人是造不出來的。那么在計算機中的數字簽名怎么回事呢?數字簽名就是用於驗證傳輸的內容是不是真實服務器發送的數據,發送的數據有沒有被篡改過,它就干這兩件事,是非對稱加密的一種應用場景。不過他是反過來用私鑰來加密,通過與之配對的公鑰來解密。
- 數字證書是指在互聯網通訊中標志通訊各方身份信息的一個數字認證,人們可以在網上用它來識別對方的身份。因此數字證書又稱為數字標識。數字證書對網絡用戶在計算機網絡交流中的信息和數據等以加密或解密的形式保證了信息和數據的完整性和安全性。
什么是中間件,好處是什么
中間件是一類連接軟件組件和應用的計算機軟件,它包括一組服務。以便於運行在一台或多台機器上的多個軟件通過網絡進行交互。使用node作為中間件更好提升了性能。
好處:
- 代理,處理前端產生的訪問接口跨域,通過node反向代理,訪問目標服務器
- 緩存,用戶觸發數據更新時,使用node作為暫時緩存,節省后端資源
- 限流,針對接口和路由做出響應路由
- 監控,高並發請求特點
- 鑒權,對頁面路由權限做出判斷
- 渲染,使用node對前端頁面進行預渲染
- 等等...
node中的Connect模塊是什么,Koa與Express的中間件有什么區別
Connect是一個node中間件(middleware)框架,每個中間件在http處理過程中通過改寫request或(和)response的數據、狀態,實現了特定的功能
Koa與Express中間件的區別:
Express主要基於Connect中間件框架,中間件一個接一個的順序執行,通常會將 response 響應寫在最后一個中間件中
而koa主要基於co中間件框架,它的中間件是通過 async await
實現的,中間件執行順序是“洋蔥圈”模型。執行效果類似於Promise.all