1.Node.js 概述
1.1 Node.js 不是一門編程語(yǔ)言,它是一個(gè)執(zhí)行 JavaScript 代碼的工具。工具是指可以安裝在計(jì)算機(jī)操作系統(tǒng)之上的軟件。
1.2 瀏覽器和 Node.js 都可以運(yùn)行 JavaScript ,因?yàn)樗鼈兌純?nèi)置了 JavaScript V8 Engine ,它可以將 JavaScript 代碼編譯為計(jì)算機(jī)能夠識(shí)別的機(jī)器碼。
1.3 瀏覽器中運(yùn)行的 JavaScript 和 node.js 中運(yùn)行的 JavaScript:
- 在內(nèi)置了 JavaScript V8 Engine 以后實(shí)際上只能執(zhí)行 ECMAScript ,就是語(yǔ)言中的語(yǔ)法部分;
- 瀏覽器為了能夠讓 JavaScript 操作瀏覽器窗口以及 HTML 文檔,所以在 JavaScript V8 Engine 中添加了控制它們的 API ,就是 DOM 和 BOM ,所以JavaScript 在瀏覽器中運(yùn)行時(shí)是可以控制瀏覽器窗口對(duì)象和 DOM 文檔對(duì)象的;
- 和瀏覽器不同,在 Node.js 中是沒(méi)有 DOM 和 BOM 的,所以在 Node.js 中不能執(zhí)行和它們相關(guān)的代碼,比如 window.alert() 或者 document.getElementbyId() 。DOM 和 BOM 是瀏覽器環(huán)境中特有的。
- 在 Node.js 中,作者向其添加了很多系統(tǒng)級(jí)別的 API ,比如對(duì)操作系統(tǒng)中的文件和文件夾進(jìn)行操作。獲取操作系統(tǒng)信息,比如系統(tǒng)內(nèi)存總量是多少,系統(tǒng)臨時(shí)目錄在哪,對(duì)系統(tǒng)的進(jìn)程操作等等。
- JavaScript 運(yùn)行在瀏覽器中控制的是瀏覽器窗口和 DOM 文檔。
- JavaScript 運(yùn)行在 Node.js 中控制的操作系統(tǒng)級(jí)別的內(nèi)容。
- 瀏覽器中的 JavaScript 不能控制系統(tǒng)級(jí)別的 API:瀏覽器是運(yùn)行在用戶的操作系統(tǒng)中的,如果能控控制系統(tǒng)級(jí)別的 API 就會(huì)存在安全的問(wèn)題; Node.js 是運(yùn)行在遠(yuǎn)程的服務(wù)器中的,訪問(wèn)的是服務(wù)器系統(tǒng) API,不存在這方面的的安全問(wèn)題。
- Node.js 能夠做的:我們通常使用它來(lái)構(gòu)建服務(wù)器端應(yīng)用和創(chuàng)建前端工程化工具;
- JavaScript 運(yùn)行在瀏覽器中我們就叫它客戶端 JavaScript;
- JavaScript 運(yùn)行在 Node.js 中我們就叫它服務(wù)器端 JavaScript
2、系統(tǒng)環(huán)境變量
- 系統(tǒng)環(huán)境變量是指在操作系統(tǒng)級(jí)別上定義的變量,變量中儲(chǔ)存了程序運(yùn)行時(shí)所需要的參數(shù)
- 比如在使用 webpack 構(gòu)建前端應(yīng)用時(shí)就使用到了系統(tǒng)環(huán)境變量,因?yàn)?webpack 需要根據(jù)系統(tǒng)環(huán)境變量判斷當(dāng)前為開發(fā)環(huán)境還是生產(chǎn)環(huán)境,根據(jù)環(huán)境決定如何構(gòu)建應(yīng)用
在開發(fā)環(huán)境的操作系統(tǒng)中定義 NODE——ENV 變量,值為 development ,在生產(chǎn)環(huán)境的操作系統(tǒng)中定義 NODE_EVN 變量,值為 production 。 - webpack 在運(yùn)行時(shí)通過(guò) process.env.NODE_EVN 獲取變量的值,從而得出當(dāng)前代碼的運(yùn)行環(huán)境是什么。
- 環(huán)境變量 PATH:系統(tǒng)環(huán)境變量 PATH 中存儲(chǔ)的都是應(yīng)用程序路徑。當(dāng)要求系統(tǒng)運(yùn)行某一個(gè)應(yīng)用程序又沒(méi)有告訴它完整程序路徑時(shí),此時(shí)操作系統(tǒng)會(huì)先在當(dāng)前文件夾中查找應(yīng)用程序,如果查找不到就會(huì)去系統(tǒng)環(huán)境變量 PATH 中指定的路徑中查找。
3、安裝 Node.js
- 官網(wǎng)下載: https://nodejs.org/zh-cn/
- 有兩個(gè)版本。LTS:長(zhǎng)期支持版(穩(wěn)定版)可以運(yùn)行在生產(chǎn)環(huán)境中;Current: 最新版(預(yù)覽版)不建議運(yùn)行在生產(chǎn)環(huán)境中,因?yàn)榭赡苡?BUG
- 安裝過(guò)程中可能出現(xiàn)的兩個(gè)問(wèn)題:
在運(yùn)行 node 命令時(shí)提示“不是內(nèi)部或外部命令,也不是可運(yùn)行的程序或批處理文件”:
將 Node 應(yīng)用程序目錄添加到系統(tǒng)環(huán)境變量中,然后重新啟動(dòng)命令行工具再次執(zhí)行 node 命令 - 安裝 Node 的過(guò)程中出現(xiàn)代碼為 2502 和 2503 的錯(cuò)誤
1.通過(guò)管理員權(quán)限打開命令行工具;
2.切換到 node 安裝包所在的目錄;
3.通過(guò) mesiexec/package node-v10.15.0-x64.mis 運(yùn)行 node 應(yīng)用程序安裝包(node-v10.15.0-x64.mis 安裝包的名字)
4、全局變量
- 在 Node.js 環(huán)境中是沒(méi)有 window 的,所以 window 對(duì)象自然是未定義的
- 在 Node.js 環(huán)境中全局對(duì)象為 global ,在 global 對(duì)象中會(huì)存在一些和 window 對(duì)象名字相同且作用相同的方法
- 在 Node.js 環(huán)境中聲明的變量不會(huì)被添加到全局對(duì)象中,變量聲明后只能在當(dāng)前文件中使用
- process 對(duì)象是一個(gè)全局變量,提供了有關(guān)當(dāng)前 Node.js 進(jìn)程的信息并對(duì)其進(jìn)行控制。
// process是全局變量,使用時(shí),無(wú)需引入,引入也不報(bào)錯(cuò),但是沒(méi)有必須引入
// const process= require("process");
console.log(process)
//輸出當(dāng)前版本
console.log(process.version) //v14.16.0
//輸出操作系統(tǒng)架構(gòu)
console.log(process.arch) // x64
//輸出操作系統(tǒng)平臺(tái)
console.log(process.platform) // win32
//環(huán)境變量
console.log(process.env)
//自定義環(huán)境變量
process.env.NODE_ENV = 'develop'
console.log(process.env.NODE_ENV) // develop
//獲取進(jìn)程編號(hào)
console.log(process.pid) //(系統(tǒng)分配,每次執(zhí)行不一樣)
//殺死進(jìn)程
process.kill() //參數(shù):進(jìn)程編號(hào)
5、模塊成員導(dǎo)出與導(dǎo)入
- 模塊概述:在 Node.js 環(huán)境中,默認(rèn)就支持模塊系統(tǒng),該模塊系統(tǒng)遵循 CommonJS 規(guī)范;一個(gè) JavaScript 文件就是一個(gè)模塊,在模塊文件中定義的變量和函數(shù)默認(rèn)只能在模塊文件內(nèi)部使用,如果需要在其他文件中使用,必須顯式聲明將其進(jìn)行導(dǎo)出
- 模塊成員導(dǎo)出:
在每一個(gè)模塊文件中,都會(huì)存在一個(gè) module 對(duì)象,即模塊對(duì)象。在模塊對(duì)象中保存了和當(dāng)前模塊相關(guān)信息;
在模塊對(duì)象中有一個(gè)屬性 exports ,它的值是一個(gè)對(duì)象,模塊內(nèi)部需要被導(dǎo)出的成員都應(yīng)該存儲(chǔ)到這個(gè)對(duì)象中 - 模塊成員導(dǎo)入:
在其他文件中通過(guò) require 方法引入模塊,require 方法的返回值就是對(duì)應(yīng)模塊的 module.exports 對(duì)象 - 在導(dǎo)入模塊時(shí),模塊文件后綴 .js 可以省略,文件路徑不可省略
- require 方法屬于同步導(dǎo)入模塊,模塊導(dǎo)入后可以立即使用
- 通過(guò) require 方法引入模塊時(shí)會(huì)執(zhí)行該模塊中的代碼
- 在導(dǎo)入其他模塊時(shí),建議使用 const 關(guān)鍵字聲明常量,防止模塊被重置
- 有時(shí)在一個(gè)模塊中只會(huì)導(dǎo)出一個(gè)成員,為方便其他模塊使用,導(dǎo)出時(shí)可以直接把值賦給 module.exports;導(dǎo)入直接獲取,不需要打點(diǎn)調(diào)用了
6、 Module Wrapper Function 模塊包裝函數(shù)
- 在模塊文件執(zhí)行之前,模塊文件中的代碼會(huì)被包裹在模塊包裝函數(shù)中,這樣每個(gè)模塊文件中的代碼就都擁有了自己的作用域,所以在模塊外部就不能訪問(wèn)模塊內(nèi)部的成員了。
(function (exports, require, module, __filename, __dirname) {
// entire module code lives here
})() - module 和 require 是模塊內(nèi)部成員,不是全局對(duì)象 global 下面的屬性
__filename : 當(dāng)前模塊文件名稱
__dirname :當(dāng)前文件所在路徑 - exports: 引用地址指向了 module.exports 對(duì)象的簡(jiǎn)寫形式
- 在導(dǎo)入模塊時(shí)最終導(dǎo)入的是 module.exports 對(duì)象,所以在使用 exports 對(duì)象添加導(dǎo)出成員時(shí)不能修改引用地址
7、Node.js 內(nèi)置模塊
在 Node.js 安裝完成后,會(huì)內(nèi)置一些非常有用的模塊
Path: 模塊內(nèi)提供了一些路徑操作相關(guān)的方法
// 引入
const path = require("path")
//獲取當(dāng)前文件所在的路徑
console.log(process.cwd())
console.log(__dirname)
//獲取當(dāng)前文件所在的路徑
console.log(process.cwd())
//獲取當(dāng)前文件所在的路徑
// dir = directory 目錄 C:\Users\ROG\Desktop\拉鉤教育\fed-e-task-base-03-01\code
console.log(__dirname)
//獲取當(dāng)前文件的完整路徑 C:\Users\ROG\Desktop\拉鉤教育\fed-e-task-base-03-01\code
console.log(__filename)
//獲取文件的擴(kuò)展名 .js
console.log(path.extname(__filename))
// 獲取路徑中的目錄部分 C:\Users\ROG\Desktop\拉鉤教育\fed-e-task-base-03-01\code
console.log(path.dirname(__filename))
//獲取路徑中的文件名 path.js
console.log(path.basename(__filename));
// 相當(dāng)于../
const t = path.join(__dirname, '..')
console.log(t) // C:\Users\ROG\Desktop\拉鉤教育\fed-e-task-base-03-01
//將多個(gè)路徑合并起來(lái)
const a = path.join("d:/",'a', 'b')
console.log(a) // d:\a\b
fs: 文件操作系統(tǒng),提供了操作文件的方法
// 文件寫入
const fs = require("fs");
fs.writeFile('./1.txt','我是寫入的內(nèi)容', (err)=>{
if(err){
throw err;
}
console.log("寫入成功")
});
// 文件讀取
var flieName = path.join(__dirname, './1.txt');
fs.readFile(flieName, (err, data)=>{
if(err) throw err;
//data是二進(jìn)制數(shù)據(jù),但是以16進(jìn)制數(shù)據(jù)輸出
console.log(data);
console.log(data.toString()) // 我是寫入的內(nèi)容
})
// 文件刪除
var flieName = path.join(__dirname, './1.txt');
fs.unlink(flieName, (err)=>{
if(err) throw err;
console.log("刪除成功")
})
//追加寫入
fs.appendFile(__dirname + '/2.txt', '曾經(jīng)有一首歌是這樣唱的', err => {
if(err) throw err;
console.log("追加成功")
})
//創(chuàng)建目錄
fs.mkdir('./a', err => {
if(err){
throw err;
}
console.log("success");
})
// 刪除目錄 rmdir 只能刪除空目錄
// 先刪除目錄下的普通文件,再通過(guò)rmdir刪除空目錄
fs.rmdir('./a', err => {
if (err) {
throw err;
}
console.log("刪除成功");
})
//重命名
fs.rename(__dirname + "/a1", __dirname + "/a11", err => {
if (err) {
throw err;
}
console.log("重命名成功");
});
// 寫文件
fs.readdir(__dirname, (err, data) => {
if(err) throw err;
console.log(data.length); //文件夾和文件都可以讀取出來(lái)
data.map((d)=>{
console.log(d);
fs.stat(__dirname + '/' + d, (err, stat) => {
if(err) throw err;
if(stat.isDirectory()){ //判斷是否是目錄
console.log("目錄:", d);
}else if(stat.isFile()){
console.log("文件:", d);
}
})
})
- http
const http = require("http")
/**
* 創(chuàng)建服務(wù)器
*/
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader("Content-type", "text/plain;charset=utf-8")
res.end("你好,node.js")
})
/**
* 發(fā)布web服務(wù)
*/
const port = 3000;
const host = "localhost"
server.listen(port, host, ()=>{
console.log(`服務(wù)器運(yùn)行在htp://${host}:${port}`)
})
在引入內(nèi)置模塊時(shí),使用的是模塊的名字,前面不需要加任何路徑
8、npm 概述
- Node.js 軟件包: 每一個(gè)基于 Node.js 平臺(tái)開發(fā)的應(yīng)用程序都是 Node.js 軟件包;
- 所有 Node.js 軟件包都被托管在 www.npmjs.com 中
- npm:Node Package Manager , Node.js 環(huán)境中的軟件包管理器,隨 Node.js 一起被安裝;
- 它可以將 Node 軟件包添加到我們的應(yīng)用程序中并對(duì)齊進(jìn)行管理,比如下載,刪除,更新,查看版本等等;
- 它沒(méi)有用戶界面,需要在命令行工具中通過(guò)命令的方式使用,對(duì)應(yīng)的命令就是 npm ;
- NPM 和 Node 是兩個(gè)獨(dú)立的應(yīng)用程序,只是被捆綁安裝了,可以通過(guò)版本號(hào)證明
9、package.json
- Node.js 規(guī)定在每一個(gè)軟件包中都必須包含一個(gè)叫做 package.json 的文件;
- 它是應(yīng)用程序的描述文件,包含的應(yīng)用程序相關(guān)的信息,比如應(yīng)用名稱,應(yīng)用版本,應(yīng)用作者等等;
- 通過(guò) package.json 文件可以方便管理應(yīng)用和發(fā)布應(yīng)用; 創(chuàng)建 package.json 文件: npm init ;
- 快速創(chuàng)建 package.json 文件: npm init --yes
10、下載 Node.js 軟件包
- 在應(yīng)用程序的根目錄執(zhí)行命令, npm install <pkg> 或者 npm i <pkg> 如:npm install lodash
- 軟件包下載完成后會(huì)發(fā)生三件事:
1.軟件包會(huì)被存儲(chǔ)在 node_modules 文件夾中,如果在應(yīng)用中不存在此文件夾, npm 會(huì)自動(dòng)創(chuàng)建
2.軟件包會(huì)被記錄在 package.json 文件中,包含軟件包的名字以及版本號(hào)
3.npm 會(huì)在應(yīng)用中創(chuàng)建 package-lock.json 文件,用于記錄元件包及軟件包的依賴包的下載地址及版本
11、使用 Node.js 軟件包
- 在引入第三方軟件包時(shí),在 require 方法中不需要加入路徑信息,只需要使用軟件包的名字即可,require 方法會(huì)自動(dòng)去 node_modules 文件夾中進(jìn)行查找
- 軟件包依賴問(wèn)題說(shuō)明:
1. 比如在我的應(yīng)用中要依賴 mongoose 軟件包,于是我下載了它,但是在 node_modules 文件夾中除了包含 mongoose 以外還多出了很多其他軟件包,為什么會(huì)多出這么多軟件包呢?
實(shí)際上它們又是 mongoose 依賴的軟件包。
2. 為什么 mongoose 依賴的軟件包不放在 mongoose 文件夾中呢?
在早期的 npm 版本中, 某個(gè)軟件包依賴的其他軟件包都會(huì)被放置在該軟件包內(nèi)部的node_modules 文件夾中,但是這樣做存在兩個(gè)問(wèn)題,第一個(gè)問(wèn)題是很多軟件包都會(huì)有相同的依賴,導(dǎo)致開發(fā)者在一個(gè)項(xiàng)目中會(huì)下載很多重復(fù)的軟件包,比如 A 依賴 X,B 依賴 X,C 依賴 X,在這種情況下 X 就會(huì)被重復(fù)下載三次。第二個(gè)問(wèn)題是文件夾嵌套層次太深,導(dǎo)致文件夾在 windows系統(tǒng)中不能被直接刪除。比如 A 依賴 B, B 依賴 C, C 依賴 D ... , 就會(huì)發(fā)生文件夾依次嵌套的情況。
3. 所有的軟件包都放置在 node_modules 文件夾中不會(huì)導(dǎo)致軟件包的版本沖突嗎?
在目前的 npm 版本中,所有的軟件包都會(huì)被直接放置在應(yīng)用根目錄的 node_modules 文件夾中,這樣雖然解決了文件夾嵌套層次過(guò)深和重復(fù)下載軟件包的問(wèn)題,但如果只這樣做肯定會(huì)導(dǎo)致軟件包版本沖突的問(wèn)題,如何解決呢?
比如 A 依賴 X 的 1 版本,B 依賴 X 的 2 版本,如果你先下載的是 A,那么 A 依賴的 X 會(huì)被放置在根目錄的 node_modules 文件夾中, 當(dāng)下載 B 時(shí),由于在根目錄中已經(jīng)存在 X 并且版本不一致,那么 B 依賴的 X 就會(huì)被放置在 B 軟件包中的 node_module 文件夾中,通過(guò)此方式解決軟件包版本沖突的問(wèn)題。
4. node_modules 文件夾中的軟件包都需要提交到 git 倉(cāng)庫(kù)中嗎?
在 node_modules 文件夾中有很多軟件包,隨著應(yīng)用程序的增長(zhǎng),軟件包也會(huì)越來(lái)越多,甚至?xí)_(dá)到幾百兆。當(dāng)我們將應(yīng)用提交到版本庫(kù)時(shí),我們不想提交它,因?yàn)樗鼈儾皇俏覀儜?yīng)用中的源代碼,而且由于碎文件比較多,其他人在檢出代碼時(shí)需要等待的時(shí)間會(huì)很久。當(dāng)其他人拿到應(yīng)用程序時(shí)沒(méi)有依賴軟件包應(yīng)用程序是運(yùn)行不起來(lái)的,如何解決呢?
實(shí)際上應(yīng)用程序依賴了哪些軟件包在 package.json 文件中都會(huì)有記錄,其他人可以通過(guò) npm install 命令重新下載它們。為了保持下載版本一直,npm 還會(huì)根據(jù) package-lock.json 文件中的記錄的地址進(jìn)行下載。將應(yīng)用程序提交到版本庫(kù)之前,將 node_modules 文件夾添加到 .gitignore 文件中。
12、語(yǔ)義版本控制
1.版本號(hào)規(guī)范: 2.3.4
2:添加新功能(破壞現(xiàn)有API)==> 2.2.4
3:添加新功能(不會(huì)破壞現(xiàn)有 API,在現(xiàn)有API 的基礎(chǔ)上進(jìn)行添加) ==> 2.4.4
4:用于修復(fù) bug ==> 2.3.5
2.版本號(hào)更新規(guī)范
^2.3.4:主要版本不變,更新次要版本和補(bǔ)丁版本
~2.3.4:主要版本和次要版本不變,更新補(bǔ)丁版本
2.3.4:使用確切版本,即主要版本、次要版本、補(bǔ)丁版本固定
13、查看軟件包實(shí)際版本
- 在 node_module 文件夾中找到對(duì)應(yīng)的依賴軟件包,找到它的package.json文件,可以在這個(gè)文件中的 version 字段中找到它的具體版本
- 通過(guò) npm list 命令查看所有依賴軟件包的具體版本,--depth 選項(xiàng)指定查看依賴包的層級(jí)
14、查看軟件包的元數(shù)據(jù)
- npm view mongoose
- npm view mongoose versions 查看具體的某一項(xiàng)的元數(shù)據(jù)
- npm view mongoose dist-tags dependencies 查看多個(gè)項(xiàng),空格分開
15、下載特定版本的軟件包
- npm i <pkg>@<version> npm i mongoose@2.4.2 lodash@4.7.0
- cat package.json npm list --depth 0
- 刪除軟件包
npm uninstall <pkg> npm uninstall mongoose npm un mongoose
16、更新軟件包
- 通過(guò) npm outdated 命令可以查看哪些軟件包已經(jīng)過(guò)期,對(duì)應(yīng)的新版本是什么
- 通過(guò) npm update 更新過(guò)期的軟件包,更新操作遵循語(yǔ)義定義版本控制規(guī)則
17、項(xiàng)目依賴和開發(fā)依賴
- 項(xiàng)目依賴:無(wú)論在開發(fā)環(huán)境還是線上環(huán)境只要程序在運(yùn)行的過(guò)程中需要使用的軟件包就是項(xiàng)目依賴。比如 lodash, mongoose
- 開發(fā)依賴:在應(yīng)用開發(fā)階段使用,在生產(chǎn)環(huán)境中不需要使用的軟件包,比如 TypeScript 中的類型聲明文件
- 在 package.json 文件中,項(xiàng)目依賴和開發(fā)依賴要分別記錄,項(xiàng)目依賴被記錄在 dependencies 對(duì)象中,開發(fā)依賴都被記錄在 devDependencies 中,使開發(fā)者可以在不同的環(huán)境中下載不同的依賴軟件包
- 在下載開發(fā)依賴時(shí),要在命令的后面加上 --save-dev 選項(xiàng)或者 -D 選項(xiàng)。 npm i eslint -D
- 在開發(fā)環(huán)境中下載的所有依賴軟件包 : npm install
- 在生產(chǎn)環(huán)境中只下載項(xiàng)目依賴軟件包: npm install --prod
18、全局安裝與本地安裝
- 本地安裝:將軟件包下載到應(yīng)用根目錄下的 node_modules 文件夾中,軟件包只能在當(dāng)前應(yīng)用中使用
- 全局安裝:將軟件包下載到操作系統(tǒng)的指定目錄中,可以在任何應(yīng)用中使用
- 通過(guò) -g 選項(xiàng)將軟件包安裝到全局: npm install <pkg> -g
- 查看全局軟件包安裝位置: npm root -g
- 刪除全局中的軟件包: npm un npm-check-updates -g
- 查看全局中安裝了哪些軟件包: npm list -g --depth 0
- 查看全局中有哪些過(guò)期軟件包: npm outdated -g
- nodemon :命令工具軟件包,可以監(jiān)控文件變化,自動(dòng)重新執(zhí)行文件 npm install nodemon@2.0.7 -g
19、npm-check-updates 強(qiáng)制更新
- npm-check-updates 可以查看應(yīng)用中有哪些軟件包過(guò)期,可以強(qiáng)制更新 package.json 文件中軟件包版本
1.將 npm-check-updates 安裝到全局: npm install npm-check-undates -g
2.查看過(guò)期軟件包: npm-check-updates
3.更新: package.json : ncu -u
4.安裝軟件包: npm i
5.檢測(cè): npm outdated 或 npm-check-updates
20、發(fā)布軟件包
- 注冊(cè) npm 賬號(hào) https://www.npmjs.com/ yao_xiao_yao
- 創(chuàng)建軟件包 npm init -y 生成package.json
- 創(chuàng)建模塊
- 登錄 npm (npm 鏡像地址必須為 npmjs.com) npm login
- 發(fā)布軟件包 npm publish
- 測(cè)試:在其他應(yīng)用中使用該軟件包; npm install 包名
21、更新版本號(hào)
- 在軟件包的源代碼發(fā)生更改后,是不能直接發(fā)布的,應(yīng)該更新軟件包的版本號(hào)再進(jìn)行發(fā)布
- 更新主要版本號(hào): npm version major
- 更新次要版本號(hào): npm version minor
- 更新補(bǔ)丁版本號(hào): npm verison patch
22、撤銷已發(fā)布的軟件包
- 只有在發(fā)布軟件包的24小時(shí)內(nèi)才允許撤銷
- 軟件包撤銷后 24 小時(shí)以后才能重新發(fā)布
- 重新發(fā)布時(shí)需要修改包名稱和版本號(hào)
npm unpublish <pkg> --force
23、更改 npm 鏡像地址
- 由于 npmjs.com 是國(guó)外的網(wǎng)站,大多數(shù)的時(shí)候下載軟件包的速度比較慢,可以通過(guò)配置的方法更改 npm 工具的下載地址
- 獲取 npm 配置 npm config list -l --json
-l 列表表示所有默認(rèn)配置選項(xiàng)
--json 以 json 格式顯示配置選項(xiàng) - 設(shè)置 npm 配置
獲取 npm 下載地址: npm config get registry
獲取 npm 用戶配置文件: npm config get userconfig - 更改 npm 鏡像地址
npm config set registry https://registry.npm.taobao.org
npm config set registry https://registry.npmjs.org
24、 npx 命令
- npx 是 npm 軟件包提供的命令,它是 Node.js 平臺(tái)下軟件包執(zhí)行器。主要用途有兩個(gè),第一個(gè)是臨時(shí)安裝軟件包執(zhí)行后刪除它,第二個(gè)是執(zhí)行本地安裝的提供命令的軟件包
1.臨時(shí)安裝軟件包執(zhí)行后刪除軟件包
有些提供命令的軟件包使用的頻率并不高,比如 creat-react-app 腳手架工具,我們不能臨時(shí)下載使用,然后刪除掉它。
npx create-react-app react-test
2.執(zhí)行本地安裝的軟件包
現(xiàn)在有兩個(gè)項(xiàng)目都依賴了某個(gè)命令工具軟件包,但是項(xiàng)目 A 依賴的是它的1版本,項(xiàng)目 B 版本依賴的是它的 2 版本,該軟件包可以在本地進(jìn)行安裝,在 A 項(xiàng)目中安裝它的 1 版本,在 B 項(xiàng)目中安裝它的 2 版本,在應(yīng)用中可以通過(guò) npx 調(diào)用 node_modules 文件夾中安裝的命令工具
將所有軟件包安裝到應(yīng)用本地是現(xiàn)在最推薦的做法,一是可以防止軟件包的版本沖突問(wèn)題,二是其他開發(fā)者在恢復(fù)應(yīng)用依賴時(shí)可以回復(fù)全部依賴,因?yàn)檐浖惭b到本地后會(huì)被 package.json 文件記錄,其他開發(fā)者在運(yùn)行項(xiàng)目時(shí)不會(huì)因?yàn)槿鄙僖蕾嚩鴪?bào)錯(cuò)
25、配置入口文件的作用
- 應(yīng)用程序入口文件就是應(yīng)用程序執(zhí)行的起點(diǎn),就是啟動(dòng)應(yīng)用程序時(shí)執(zhí)行的文件
場(chǎng)景一:其他開發(fā)者拿到你的軟件包之后,通過(guò)該文件可以直到應(yīng)用的入口文件是誰(shuí),通過(guò)入口文件啟動(dòng)應(yīng)用
場(chǎng)景二:通過(guò) node 應(yīng)用文件夾 命令啟動(dòng)應(yīng)用。 node 命令執(zhí)行 package.json 文件中 main 選項(xiàng)指定的入口文件,如果沒(méi)有指定的入口文件,則執(zhí)行 index.js
26、模塊查找規(guī)則
- 在指定了查找路徑的情況下: require("./server")
查找 server.js ; 查找 server.json ; 查找 server 文件夾,查看入口文件(package.json -> main) ; 查找 server 文件夾 中的 index.js 文件 - 在沒(méi)有指定查找路徑的情況下: require("server")
27、異步編程
- CPU 與 存儲(chǔ)器
- CPU 中央處理器,計(jì)算機(jī)核心部件,負(fù)責(zé)運(yùn)算和指令調(diào)用;開發(fā)者編寫的
- JavaScript 代碼在被編譯為機(jī)器碼以后就是通過(guò)CPU執(zhí)行的
存儲(chǔ)器:
內(nèi)存 用于臨時(shí)存儲(chǔ)數(shù)據(jù),斷電后數(shù)據(jù)丟失,由于數(shù)據(jù)讀寫速度快,計(jì)算機(jī)中的應(yīng)用都是在內(nèi)存中運(yùn)行的;
磁盤 用于持久存儲(chǔ)數(shù)據(jù),斷電后數(shù)據(jù)不丟失,內(nèi)部有磁頭依靠馬達(dá)轉(zhuǎn)動(dòng)在盤片上讀寫數(shù)據(jù),速度比內(nèi)存慢 - 計(jì)算機(jī)應(yīng)用程序在沒(méi)有運(yùn)行時(shí)是存儲(chǔ)在磁盤中的,當(dāng)我們啟動(dòng)應(yīng)用程序后,應(yīng)用程序會(huì)被加載到內(nèi)存中運(yùn)行,應(yīng)用程序中的指令會(huì)被中央處理器CPU來(lái)執(zhí)行
28、I/O
- I 就是 Input 表示輸入, O 就是 Output 表示輸出, I/O操作就是輸入輸出操作
- 比如數(shù)據(jù)庫(kù)的讀寫操作就是I/O操作,因?yàn)閿?shù)據(jù)庫(kù)文件是存儲(chǔ)在磁盤中的,而我們編寫的程序是運(yùn)行在內(nèi)存中的,將內(nèi)存中的數(shù)據(jù)寫入數(shù)據(jù)庫(kù)對(duì)于內(nèi)存來(lái)說(shuō)就是輸出,查詢數(shù)據(jù)庫(kù)中的數(shù)據(jù)就是將磁盤中的數(shù)據(jù)讀取到內(nèi)存中,對(duì)如內(nèi)存來(lái)說(shuō)就是輸入
- I/O模型
1. CPU 等待I/O操作完成獲取到操作結(jié)構(gòu)后再去執(zhí)行其他命令,這是同步I/O操作(阻塞I/O)
2. CPU 不等待I/O操作完成, CPU 在發(fā)出I/O 指令后,內(nèi)存和磁盤開始工作,CPU繼續(xù)執(zhí)行其他命令。當(dāng)I/O操作完成后再通知 CPU I/O操作的結(jié)果是什么。這是異步I/O操作(非阻塞I/O) - 同步I/O和異步I/O區(qū)別就是是否等待I/O結(jié)果
29、進(jìn)程和線程
- 每當(dāng)我們運(yùn)行應(yīng)用程序中,操作系統(tǒng)會(huì)創(chuàng)建該應(yīng)用程序的實(shí)例對(duì)象,該實(shí)例對(duì)象就是應(yīng)用程序的進(jìn)程,操作系統(tǒng)會(huì)操作系統(tǒng)會(huì)按照進(jìn)程為單位為應(yīng)用程序分配資源,比如內(nèi)存,這樣程序才能夠在計(jì)算機(jī)的操作系統(tǒng)中運(yùn)行起來(lái)。
- 線程被包裹在進(jìn)程之中,是進(jìn)程中的實(shí)際運(yùn)作單位,一條線程指的就是進(jìn)程中的一個(gè)單一順序的控制流。也就是說(shuō),應(yīng)用程序要做的事情都存儲(chǔ)在線程之中。可以這樣認(rèn)為,一條線程就是一個(gè)待辦列表,供 CPU 執(zhí)行。
30、回調(diào)函數(shù)
- 回調(diào)函數(shù)是指通過(guò)函數(shù)參數(shù)的方式將一個(gè)函數(shù)傳遞到另一個(gè)函數(shù)中,參數(shù)函數(shù)就是回調(diào)函數(shù)
- 在主函數(shù)中調(diào)用回調(diào)函數(shù)時(shí),可以為回調(diào)函數(shù)傳遞參數(shù)
- 在異步編程中,異步 API 執(zhí)行的結(jié)果就是通過(guò)回調(diào)函數(shù)傳遞參數(shù)的方式傳遞到上層代碼中的。
- 回調(diào)地獄:回調(diào)地獄是回調(diào)函數(shù)多層嵌套導(dǎo)致代碼難以維護(hù)的問(wèn)題。
基于回調(diào)函數(shù)的異步編程一不小心就會(huì)產(chǎn)生回調(diào)地獄的問(wèn)題。
31、基于 Promise 的異步編程
- Promise 是 JavaScript 中異步編程解決方案,可以解決回調(diào)函數(shù)方案中的回調(diào)地獄問(wèn)題。可以將 Promise 理解為容器,用于包裹異步 API 的容器,當(dāng)容器中的異步 API 執(zhí)行完成后,Promise 允許我們?cè)谌萜鞯耐饷娅@取異步 API 的執(zhí)行結(jié)果,從而避免回調(diào)函數(shù)嵌套。
- Promise 翻譯為承若,表示它承若幫我們做一些事情,既然它承若了它就要去做,做就會(huì)有一個(gè)過(guò)程,就會(huì)有一個(gè)結(jié)果,結(jié)果要么是成功要么是失敗。
- 所以在 Promise 中有三種狀態(tài), 分別為等待(pending),成功(fulfilled),失敗(rejected)。
- 默認(rèn)狀態(tài)為等待,等待可以變?yōu)槌晒?,等待可以變?yōu)槭 ?/li>
- 狀態(tài)一旦更改不可改變,成功不能變回等待,失敗不能變回等待,成功不能變成失敗,失敗不能變成成功。
- Promise 基礎(chǔ)語(yǔ)法
const promise = new Promise(function (resolve, reject) {
fs.readFile("./x.txt", "utf-8", function(error, data) {
if (error) {
// 將狀態(tài)從等待變?yōu)槭? reject(error)
} else {
// 將狀態(tài)從等待變?yōu)槌晒? resolve(data)
})
});
promise.then(function (data) {
console.log(data)
}).catch(function (error) {
console.log(error)
})
Promise 鏈?zhǔn)秸{(diào)用
Promise.all 并發(fā)異步操作
const fs = require("fs")
Promise.all([
readFile("./x.txt"),
readFile("./y.txt"),
readFile("./z.txt")
]).then(function (data) {
console.log(data)
})
基于異步函數(shù)的異步編程
Promise 雖然解決了回調(diào)地獄的問(wèn)題,但是代碼看起來(lái)仍然不簡(jiǎn)潔。
使用異步函數(shù)簡(jiǎn)化代碼提高異步編程體驗(yàn)。
##### 1. 異步函數(shù)概述
function readFile(path) {
return new Promise(function (resolve, reject) {
fs.readFile(path, "utf-8", function (error, data) {
if (error) return reject(error)
resolve(data)
})
})
}
async function getFileContent() {
let x = await readFile("./x.txt")
let y = await readFile("./y.txt")
let z = await readFile("./z.txt")
return [x, y, z]
}
getFileContent().then(console.log)
- async 聲明異步函數(shù)的關(guān)鍵字,異步函數(shù)的返回值會(huì)被自動(dòng)填充到 Promise 對(duì)象中。
- await 關(guān)鍵字后面只能放置返回 Promise 對(duì)象的 API。
- await 關(guān)鍵字可以暫停函數(shù)執(zhí)行,等待 Promise 執(zhí)行完后返回執(zhí)行結(jié)果。
- await 關(guān)鍵字只能出現(xiàn)在異步函數(shù)中。
- await 關(guān)鍵字后面只能放置返回 Promise 對(duì)象的 API。
##### 2. util.promisify
在 Node.js 平臺(tái)下,所有異步方法使用的都是基于回調(diào)函數(shù)的異步編程。為了使用異步函數(shù)提高異步編程體驗(yàn),可以使用 util 模塊下面的 promisify 方法將基于回調(diào)函數(shù)的異步 API 轉(zhuǎn)換成返回Promise 的API。
const util = require("util")
const readFile = util.promisify(fs.readFile)
async function getFileContent() {
let x = await readFile("./x.txt", "utf-8")
let y = await readFile("./y.txt", "utf-8")
let z = await readFile("./z.txt", "utf-8")
return [x, y, z]
}
getFileContent().then(console.log)
32、事件循環(huán)機(jī)制 Event Loop 機(jī)制概述
- 事件循環(huán)機(jī)制用于管理異步 API 的回調(diào)函數(shù)什么時(shí)候回到主線程中執(zhí)行。
- Node.js 采用的是異步 I/O 模型。同步 API 在主線程中執(zhí)行,異步 API 在底層的 C++ 維護(hù)的線程中執(zhí)行,異步 API 的回調(diào)函數(shù)在主線程中執(zhí)行。在 JavaScript 應(yīng)用運(yùn)行時(shí),眾多異步 API 的回調(diào)函數(shù)什么時(shí)候能回到主線程中調(diào)用呢?這就是事件循環(huán)機(jī)制做的事情,管理異步 API 的回調(diào)函數(shù)什么時(shí)候回到主線程中執(zhí)行。
- 因?yàn)?Node.js 是事件驅(qū)動(dòng)的。事件驅(qū)動(dòng)就是當(dāng)什么時(shí)候做什么事情,做的事情就定義在回調(diào)函數(shù)中,可以將異步 API 的回調(diào)函數(shù)理解為事件處理函數(shù),所以管理異步API回調(diào)函數(shù)什么時(shí)候回到主線程中調(diào)用的機(jī)制叫做事件循環(huán)機(jī)制。
33、 Event Loop 的六個(gè)階段
事件循環(huán)是一個(gè)循環(huán)體,在循環(huán)體中有六個(gè)階段,在每個(gè)階段中,都有一個(gè)事件隊(duì)列,不同的事件隊(duì)列存儲(chǔ)了不同類型的異步API 的回調(diào)函數(shù)。
- Timers:用于存儲(chǔ)定時(shí)器的回調(diào)函數(shù)(setInterval, setTimeout)。
- Pending callbacks:執(zhí)行與操作系統(tǒng)相關(guān)的回調(diào)函數(shù),比如啟動(dòng)服務(wù)器端應(yīng)用時(shí)監(jiān)聽(tīng)端口操作的回調(diào)函數(shù)就在這里調(diào)用。
- Idle, prepare:系統(tǒng)內(nèi)部使用。
- IO Poll:存儲(chǔ) I/O 操作的回調(diào)函數(shù)隊(duì)列,比如文件讀寫操作的回調(diào)函數(shù)。如果事件隊(duì)列中有回調(diào)函數(shù),執(zhí)行它們直到清空隊(duì)列。否則事件循環(huán)將在此階段停留一段時(shí)間以等待新的回調(diào)函數(shù)進(jìn)入,這個(gè)等待取決于以下兩個(gè)條件:
1. setImmediate 隊(duì)列(check 階段)中存在要執(zhí)行的回調(diào)函數(shù).
2. timers 隊(duì)列中存在要執(zhí)行的回調(diào)函數(shù). 在這種情況下, 事件循環(huán)將移至 check 階段, 然后移至Closing callbacks 階段, 并最終從 timers 階段進(jìn)入下一次循環(huán)。 - Check:存儲(chǔ) setImmediate API 的回調(diào)函數(shù)。
- Closing callbacks:執(zhí)行與關(guān)閉事件相關(guān)的回調(diào),例如關(guān)閉數(shù)據(jù)庫(kù)連接的回調(diào)函數(shù)等。
循環(huán)體會(huì)不斷運(yùn)行以檢測(cè)是否存在沒(méi)有調(diào)用的回調(diào)函數(shù),事件循環(huán)機(jī)制會(huì)按照先進(jìn)先出的方式執(zhí)行他們直到隊(duì)列為空。
34、宏任務(wù)與微任務(wù)(異步API的類型)
- 宏任務(wù):setInterval, setTimeout, setImmediate, I/O
- 微任務(wù):Promise.then Promise.catch Promise.finally, process.nextTick
- 微任務(wù)與宏任務(wù)的區(qū)別
1. 微任務(wù)的回調(diào)函數(shù)被放置在微任務(wù)隊(duì)列中,宏任務(wù)的回調(diào)函數(shù)被放置在宏任務(wù)隊(duì)列中。
2. 微任務(wù)優(yōu)先級(jí)高于宏任務(wù)。
當(dāng)微任務(wù)事件隊(duì)列中存在可以執(zhí)行的回調(diào)函數(shù)時(shí),事件循環(huán)在執(zhí)行完當(dāng)前階段的回調(diào)函數(shù)后會(huì)暫停進(jìn)入事件循環(huán)的下一個(gè)階段,事件循環(huán)會(huì)立即進(jìn)入微任務(wù)的事件隊(duì)列中開始執(zhí)行回調(diào)函數(shù),當(dāng)微任務(wù)隊(duì)列中的回調(diào)函數(shù)執(zhí)行完成后,事件循環(huán)再進(jìn)入到下一個(gè)階段開始執(zhí)行回調(diào)函數(shù)。
nextTick 的優(yōu)先級(jí)高于 microTask,在執(zhí)行任務(wù)時(shí),只有 nextTick 中的所有回調(diào)函數(shù)執(zhí)行完成后才會(huì)開始執(zhí)行 microTask。
不同階段的宏任務(wù)的回調(diào)函數(shù)被放置在了不同的宏任務(wù)隊(duì)列中,宏任務(wù)與宏任務(wù)之間沒(méi)有優(yōu)先級(jí)的概念,他們的執(zhí)行順序是按照事件循環(huán)的階段順序進(jìn)行的。
35、 Event Loop 代碼解析
- 在 Node 應(yīng)用程序啟動(dòng)后,并不會(huì)立即進(jìn)入事件循環(huán),而是先執(zhí)行輸入代碼,從上到下開始執(zhí)行,同步API 立即執(zhí)行,異步 API 交給 C++ 維護(hù)的線程執(zhí)行,異步 API 的回調(diào)函數(shù)被注冊(cè)到對(duì)應(yīng)的事件隊(duì)列中。當(dāng)所有輸入代碼執(zhí)行完成后,開始進(jìn)入事件循環(huán)。
36、process.nextTick()
- 此方法的回調(diào)函數(shù)優(yōu)先級(jí)最高,會(huì)在事件循環(huán)之前被調(diào)用。
- 如果你希望異步任務(wù)盡可能早地執(zhí)行,那就使用 process.nextTick。
37、setImmediate()
- setImmediate 表示立即執(zhí)行,它是宏任務(wù),回調(diào)函數(shù)會(huì)被會(huì)放置在事件循環(huán)的 check 階段。
- 在應(yīng)用中如果有大量的計(jì)算型任務(wù),它是不適合放在主線程中執(zhí)行的,因?yàn)橛?jì)算任務(wù)會(huì)阻塞主線程,主線程一旦被阻塞,其他任務(wù)就需要等待,所以這種類型的任務(wù)最好交給由 C++ 維護(hù)的線程去執(zhí)行。
- 可以通過(guò) setImmediate 方法將任務(wù)放入事件循環(huán)中的 check 階段,因?yàn)榇a在這個(gè)階段執(zhí)行不會(huì)阻塞主線程,也不會(huì)阻塞事件循環(huán)。
- Node 適合 I/O 密集型任務(wù),不適合 CPU 密集型任務(wù),因?yàn)橹骶€程一旦阻塞,程序就卡主了。
38、web 網(wǎng)站的組成
- 從開發(fā)者的角度來(lái)看,web 應(yīng)用主要由三部分組成:用戶界面,業(yè)務(wù)邏輯,數(shù)據(jù)。
- 用戶界面 (視圖層):用于將數(shù)據(jù)展示給用戶的地方,采用 HTML,CSS,JavaScript 編寫。
- 業(yè)務(wù)邏輯 (控制層):實(shí)現(xiàn)業(yè)務(wù)需求和控制業(yè)務(wù)流程的地方,可以采用 Java, PHP, Python, JavaScript編寫。
- 數(shù)據(jù) (模型層):應(yīng)用的核心部分, 應(yīng)用業(yè)務(wù)邏輯的實(shí)現(xiàn),用戶界面的展示都是基于數(shù)據(jù)的, web 應(yīng)用中的數(shù)據(jù)通常是存儲(chǔ)在數(shù)據(jù)庫(kù)中的,數(shù)據(jù)庫(kù)可以采用 MySql, Mongodb 等。
39、什么是 web 服務(wù)器
- 服務(wù)器是指能夠向外部(局域網(wǎng)或者萬(wàn)維網(wǎng))提供服務(wù)的機(jī)器(計(jì)算機(jī))就是服務(wù)器。
- 在硬件層面,web 服務(wù)器就是能夠向外部提供網(wǎng)站訪問(wèn)服務(wù)的計(jì)算機(jī)。
- 在這臺(tái)計(jì)算機(jī)中存儲(chǔ)了網(wǎng)站運(yùn)行所必須的代碼文件和資源文件。
- 在軟件層面,web 服務(wù)器控制著用戶如何訪問(wèn)網(wǎng)站中的資源文件,控制著用戶如何與網(wǎng)站進(jìn)行交互。
- 客戶端:web 應(yīng)用中的客戶端是指用戶界面的載體,實(shí)際上就是瀏覽器。用戶可以通過(guò)瀏覽器這個(gè)客戶端訪問(wèn)網(wǎng)站應(yīng)用的界面,通過(guò)用戶界面與網(wǎng)站應(yīng)用進(jìn)行交互。
- 網(wǎng)站的運(yùn)行:web 應(yīng)用是基于請(qǐng)求和響應(yīng)模型的。
40、IP和域名
- IP:Internet Protocol address:互聯(lián)網(wǎng)協(xié)議地址,標(biāo)識(shí)網(wǎng)絡(luò)中設(shè)備的地址,具有唯一性。例如:45.113.192.101
- 域名 (Domain Name):是由一串用點(diǎn)分隔的字符組成的互聯(lián)網(wǎng)上某一臺(tái)計(jì)算機(jī)或計(jì)算機(jī)組的名稱,用于在數(shù)據(jù)傳輸時(shí)標(biāo)識(shí)計(jì)算機(jī)的電子方位 (摘自維基百科)。
41、DNS 服務(wù)器
- Domain Name Server:域名服務(wù)器,互聯(lián)網(wǎng)域名解析系統(tǒng),它可以將"人類可識(shí)別"的標(biāo)識(shí)符映射為系統(tǒng)內(nèi)部通常為數(shù)字形式的標(biāo)識(shí)碼。(摘自維基百科)
42、端口
- 端口:是設(shè)備與外界通訊交流的出口,此處特指計(jì)算機(jī)中的虛擬端口。0 ~ 65535
- 比如在一座大廈當(dāng)中有很多房間,每間房間都提供著不同的服務(wù),我們可以通過(guò)房間號(hào)找到提供不同服務(wù)的房間。
- 服務(wù)器就是這座大廈,在服務(wù)器中可以提供很多服務(wù),比如 web 訪問(wèn)服務(wù),郵件的收發(fā)服務(wù),文件的上傳下載服務(wù),用戶在找到服務(wù)器以后如何去找具體的服務(wù)呢?答案就是端口號(hào),端口號(hào)就是大廈中的房間號(hào),在服務(wù)器中通過(guò)端口號(hào)區(qū)分不同的服務(wù)。
- 也就是說(shuō),服務(wù)器中的各種應(yīng)用,要想向外界提供服務(wù),必須要占用一個(gè)端口號(hào)。
- 通常 web 應(yīng)用占用 80 端口,在瀏覽器中訪問(wèn)應(yīng)用時(shí) 80 可以省略,因?yàn)槟J(rèn)就訪問(wèn) 80。
43、URL
- URL:統(tǒng)一資源定位符,表示我們要訪問(wèn)的資源在哪以及要訪問(wèn)的資源是什么。
- protocol :// hostname [:port可選] / path
- http :// www.example.com : 80/ index.html
44、前臺(tái)和后臺(tái),前端和后端
- 前臺(tái)和后臺(tái)都是指用戶界面。前臺(tái)是為客戶準(zhǔn)備的,每個(gè)人都可以訪問(wèn)的用戶界面。后臺(tái)是為網(wǎng)站管理員準(zhǔn)備的,只有登錄以后才能訪問(wèn)的用戶界面,用于管理網(wǎng)站應(yīng)用中的數(shù)據(jù)。
- 前端是指開發(fā)客戶端應(yīng)用的程序員。
- 后端是指開發(fā)服務(wù)器端應(yīng)用程序的程序員。
45、開發(fā)環(huán)境的說(shuō)明
- 在開發(fā)環(huán)境中,開發(fā)者機(jī)器既充當(dāng)了客戶端的角色又充當(dāng)了服務(wù)器的角色。
- 本機(jī)IP: 127.0.0.1
- 本機(jī)域名: localhost
46、創(chuàng)建 web server
- 創(chuàng)建軟件層面的 web 服務(wù)器,用于控制資源要如何被訪問(wèn)。