node.js簡介
概要:nodejs是一個基于Google V8引擎開發(fā)的一個javascript運(yùn)行環(huán)境
特性:事件驅(qū)動 非阻塞 IO
nodejs命令行的 REPL模式 (交互式解釋器)
- 進(jìn)入方式:在命令行輸入node回車即可進(jìn)入
- REPL指的是這四個主要功能
R => read : 用來讀取用戶輸入的javascript邏輯 并儲存到內(nèi)存中
E => eval : 執(zhí)行輸入的javascript語句
P => print : 打印執(zhí)行的結(jié)果
L => loop : 循環(huán)操作前三個步驟直到退出REPL模式
運(yùn)行js文件
直接在命令行通過 node命令 + 文件名的方式來執(zhí)行js文件
可以省略文件后綴名 因?yàn)閚odejs就是用來運(yùn)行js文件的
nodejs的模塊
理解:每一個js文件就是一個模塊
使用:通過require來引入模塊 exports或者module導(dǎo)出模塊
模塊化規(guī)范:
commonJS (nodejs所使用的規(guī)范 require/export)
定義:一個單獨(dú)的js文件就是一個模塊,加載模塊需要使用require方法、而加載過來的是改模塊中使用exports所導(dǎo)出的對象
特性:commonJs加載模塊是同步的所以只有模塊記載完畢后才會去執(zhí)行、而想nodejs這種做服務(wù)的的加載模塊都是從本地硬盤去加載所以相對較快不同考慮異步問題 但如果是web端的話加載模塊都要到服務(wù)器加載比較慢不可能等所有模塊加載完后再執(zhí)行。AMD (異步模塊 requireJs所使用的規(guī)范)
定義:AMD是通過difine方法定義模塊通過require方法加載模塊 AMD加載模塊是通過回調(diào)函數(shù)來加載所有模塊 把每個模塊它所依賴的模塊都傳到參數(shù)中后再加載 也就是在解析和執(zhí)行當(dāng)前模塊之前,必須指明當(dāng)前模塊所依賴的模塊
使用方式:在html中使用requireJs加載:
<script data-main="main" src="require.js"></script>
我們需要使用 data-main來指定一個主模塊來作為加載模塊的入口文件
CMD (通用模塊 懶加載 Common Module Definition)
定義:與CommonJS Modules/1.1 和 Node Modules 規(guī)范類似 也是一個文件為一個模塊 CMD相對比較簡單清晰 就是在你什么時候要使用就通過define(factory)加載 factory是一個函數(shù) 里面會默認(rèn)傳入3個參數(shù) require exports module 可以使用require在來加載模塊CMD與AMD區(qū)別:
- 對于其各自所依賴的模塊AMD是先加載的而CMD是延遲加載(懶加載)
- CMD 推崇依賴就近,AMD 推崇依賴前置。
AMD也支持CMD的寫法同時也可以把require當(dāng)做依賴項傳遞
例:
// AMD
define(['./a,' .'/b'], function(a ,b) { // 依賴已經(jīng)明確寫好
..........
})
// CMD
define(function(require, exports, module) {
var a = require('./a'); // 隨時加載
var b = require('./b');
})
nodejs運(yùn)行js文件的過程分析 (個人理解)
問題1:在web端js中為什么沒有require、exports等變量而nodejs中可以使用這些變量呢?
其實(shí)每一個js文件都運(yùn)行在一個函數(shù)里 我們可以打印出這個函數(shù)看看長啥樣。
創(chuàng)建一個js文件其內(nèi)容如下:
console.log(arguments.callee.toString());
在命令行運(yùn)行該文件
返回以下內(nèi)容:
function (exports, require, module, __filename, __dirname) { console.log(arguments.callee.toString());}
可以看到這個函數(shù)里有五個參數(shù):
1 export 就是我們用來導(dǎo)出的變量
2 require 用來引入模塊的函數(shù)
3 module 模塊對象
4 __filename 當(dāng)前文件路徑 包含自身文件名
5 __dirname 當(dāng)前文件絕對路徑
這樣可以使我們的每個模塊之間的變量相互獨(dú)立互不影響
問題2:require和exports又是怎么實(shí)現(xiàn)導(dǎo)入導(dǎo)出模塊的呢?
首先一個js文件的執(zhí)行必然只有兩種情況
- 其為主模塊 通過命令行直接運(yùn)行
- 該文件被當(dāng)做模塊引入使用
而主模塊文件的運(yùn)行不會有導(dǎo)出的情況
導(dǎo)入導(dǎo)出過程分析:
1.執(zhí)行js文件前先在外部定義一個module對象大致內(nèi)容如下:
var module = {
exports:{}
filename:""
......
}
2.把文件內(nèi)容放到一個如上面的函數(shù)里執(zhí)行并傳入module、 module.export、require、filename、dirname五個參數(shù);
這樣我們在使用module.exports或者exports導(dǎo)出變量的時候
因?yàn)閙odule是引用值 所以就是在修改外部定義好的module對象的exports屬性
例:在module.js文件導(dǎo)出內(nèi)容如下:
exports.a = 10;
這時的module 對象:
module = {
exports:{
a: 10
}
}
而我們在使用require導(dǎo)入的時候就是用以上步驟執(zhí)行這個js文件后把module.exports的值返回
例:在module1.js中內(nèi)容如下:
const a = require("./module.js"); // 返回 {a:10}
接下來重復(fù)上面的步驟:
1.外部定義一個module對象
2.把文件內(nèi)容放到一個function (exports, require, module, __filename, __dirname) {}函數(shù)里執(zhí)行
3.最后把module.exports對象返回 所以我們只需要在引入的時候定義一個變量接收即可
個人理解的node模塊運(yùn)行過程大概就這樣的吧!
問題3:nodejs中引入自定義模塊(自己編寫的js文件)需要在路徑前加上 "./ " 是什么原因?
首先我們想想為什么引入其他內(nèi)置模塊或第三方模塊為啥不需要加 "./ "
其實(shí)通常我們在頁面引入文件時的默認(rèn)路徑就是當(dāng)前路徑
不過在nodejs中它的默認(rèn)路徑為node_modules文件夾 所以引入自定義模塊時我們需要通過加 "./ "來把路徑指回當(dāng)前路徑
而引入其他模塊就會默認(rèn)從node_modules文件夾中查找
問題4:使用require引入同一個模塊多次怎么樣?
首先我們先來看個例子:
// a.js中
var a = 10;
console.log(a++);
module.exports = a;
// b.js中
var a1 = require("./a");
var a2 = require("./a");
var a3 = require("./a");
console.log(a1);
console.log(a2);
console.log(a3);
- 在這個例子中我們在命令行運(yùn)行b.js后 大家可以想想會打印出什么呢?
要知道這個例子的答案我們需要知道一個nodejs加載模塊的緩存機(jī)制:
其實(shí)nodejs在加載我們使用require方法指定的文件時會先去我們內(nèi)存里找 看是否已有對應(yīng)得結(jié)果
如果有則直接返回所查詢到的結(jié)果 并不會執(zhí)行文件
如果沒有 會執(zhí)行一次該文件后把它返回的exports存在內(nèi)存里再把結(jié)果返回給我們加載的地方,這樣之后如果在有加載同樣的文件情況下就會直接從緩存里讀取不會多次執(zhí)行同個文件
[這段看著比較懵 原本想畫個圖的但是試著畫了下感覺慘不忍睹就放棄了,還是用文字表達(dá)吧。。]
上面這個例子執(zhí)行過程:
- 在b.js中第一行 var a1 = require("./a"); 在執(zhí)行a.js文件之前會到內(nèi)存里去找看是否已有a.js的執(zhí)行結(jié)果 發(fā)現(xiàn)沒有才會執(zhí)行該文件,而再a.js執(zhí)行完后返回 11 node在拿到這個結(jié)果后會存在內(nèi)存中再把結(jié)果返回給 定義的變量a1
- b.js 第二行 執(zhí)行前同樣會去內(nèi)存找但是這次內(nèi)存已有結(jié)果所以會直接返回 11
- 與第二回相同 拿到的依然是內(nèi)存里已有的值 11
所以上面例子執(zhí)行結(jié)果為:
10 11 11 11
NPM(全稱 Node Package Manager 是基于nodejs的一個包管理器)
作用:可通過npml來安裝第三方模塊
常用命令:
$ npm install 《模塊名》 // 安裝指定模塊
$ npm install 《模塊名》-g // 安裝指定模塊到全局的node_modules目錄
$ npm install 《模塊名》--save // 將安裝的模塊寫入到package.json配置信息描述文件中
$ npm install // 根據(jù)package.json文件來安裝所依賴的模塊
$ npm update // 更新模塊
$ npm uninstall 《模塊名》 // 卸載指定模塊
$ npm version // 查看模塊版本
$ npm view // 查看模塊的注冊信息
$ npm ls // 查看安裝的模塊
使用CNPM (淘寶NPM鏡像)
因?yàn)槭褂胣pm安裝模塊需要從npm官網(wǎng)上到github上下載 服務(wù)器在國外所以比較慢
后來淘寶自己做了一個npm的鏡像供開發(fā)者使用 因?yàn)槭褂玫氖菄鴥?nèi)服務(wù)器所以下載速度比npm快很多 該鏡像每10分鐘同步一次 所以絕大多數(shù)資源都相同的
使用方式也和npm一樣只是使用的時候用cnpm代替npm
package.json (配置信息描述文件)
用來描述和配置項目的一個json格式的文件
通過命令行創(chuàng)建:
$ npm init
輸入后會出現(xiàn)配置信息需要確認(rèn)和填寫:
package name : // 模塊的名稱 默認(rèn)為所在的文件夾目錄名稱
version:(1.0.0) // 版本號 默認(rèn) 1.0.0
description: // 模塊的描述信息 默認(rèn)為空
entry point: // 入口文件 默認(rèn)index.js
test command: // 可執(zhí)行命令
git repository: // git倉庫
keywords: // 關(guān)鍵字
author: // 作者
license: // 開源協(xié)議 默認(rèn)ISC
如果覺得麻煩可以使用下面命令全部使用默認(rèn)值
$ npm init -y
除了上面的一些配置信息外還有些比較重要的:
- dependencies: 依賴的模塊
- devDependencies : 開發(fā)時所依賴的模塊
package-look.json文件的作用:
package-lock.json用來文件鎖定所有模塊的版本號,包括主模塊和所有依賴子模塊,當(dāng)使用npm install 安裝模塊時 會根據(jù)package.json的中的依賴模塊信息安裝對應(yīng)模塊 再從package-lock.json中獲取版本信息來安裝。
有了package-lock.json后通過npm install 安裝的模塊不會自動更新package.json文件中的模塊 可以使用npm install packagename(自動更新小版本號)或者npm install packagename@x.x.x(指定版本號) 來進(jìn)行安裝才會更新。package-lock.json文件中的版本號也會隨著更新。
當(dāng)package.json與package-lock.json都不存在,執(zhí)行 npm install 時,node會重新生成package-lock.json文件,然后把node_modules中的模塊信息全部記入package-lock.json文件,但不會生成package.json文件,可以通過 npm init -y 來生成package.json文件
nodejs的作用域
在nodejs中因?yàn)槠涿恳粋€模塊都運(yùn)行在一個函數(shù)中的特效 所以每個模塊中定義的變量都為其的內(nèi)部變量
而如果我們需要使用其他模塊中的變量可以有兩種方式:
- 使用module.exports導(dǎo)出后再通過require 引入
- 在nodejs中因?yàn)槠渌拗鳝h(huán)境不是瀏覽器端所以沒有window對象但是nodejs提供了一個global對象該對象在全局中都可使用所以我們也可以把需要使用的變量掛到global全局對象上供其他模塊使用(但這種方式盡量少用)
最后
內(nèi)容較多寫的挺亂的 不易閱讀望海涵
我也是初學(xué)nodejs 有不對的地方望指出~~^_^~~