- Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。
- Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型。
- Node使用包管理器NPM。
Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。
運行環境, 不是一門語言,不是一個框架。只是可以做爲JavaScript代碼運行的一個環境。 javascript
而這個運行環境主要是由V8提供的。html
建立了一個callstack。vue
function main(){ func1(); } function func1(){ func2(); } function func2(){ console.log(1); } main();
除去V8,Node中另一個比較重要的組成就是 libuv。java
What? libuv
是什麼鬼?node
先說說,關於Node的另一句話:webpack
Node is designed to build scalable network applications.
這句話的底氣在哪兒,就是Node自己採用的 事件驅動,非阻塞I/O模型。c++
在 併發模型構建網絡的應用中,每一個鏈接都會生成一個新線程,每一個新線程可能須要 2MB 的配套內存。在一個擁有 8 GB RAM 的系統上,理論上最大的併發鏈接數量是 4,000 個用戶。隨着您的客戶羣的增加,若是但願您的 Web 應用程序支持更多用戶,那麼,您必須添加更多服務器。因此在傳統的後臺開發中,整個 Web 應用程序架構(包括流量、處理器速度和內存速度)中的瓶頸是:服務器可以處理的併發鏈接的最大數量。這個不一樣的架構承載的併發數量是不一致的。並且,多個線程存在的話,也會衍生出線程切換的問題,這也會佔用必定資源。 git
併發存在的兩個問題:github
Node的解決這個問題思路:web
市場上已經有一些使用一樣處理思路的框架。
具體是怎麼作的?
在程序運行時,V8 會把I/O操做等耗時較多操做及相關回調一併交給libuv去處理。而V8繼續執行後面的代碼。等到I/O操做完成後,libuv會將回調方法放到事件隊列中。
const fs = require('fs'); const readFile = (file) => { fs.readFile(file, (err, data) => { if(!err) console.log(data); }); } console.log('program start ......'); readFile('file.json'); console.log('readFile has put the I/O '); console.log('program end!!!!!');
負責異步程序調度的工做就是libuv
作的事情。
Libuv is a multi-platform support library with a focus on asynchronous I/O.
在上述程序中,當遇到文件讀取操做時,V8會把js接口轉成C++接口 同時把回調方法一併交給libvu。此時libuv接管這個文件讀取任務。當文件讀取完成後,libuv就會把這個事件的回調函數扔到事件隊列裏。當下一次檢查事件隊列時,就會執行該回調函數。
question: 既然這樣,怎麼理解node中的單線程?
再捋一捋Node, V8 和libuv的關係。
1) Node主要由V8 javascript引擎和libuv組成;
2) v8引擎主要負責解釋執行js代碼,碰到須要異步的操做會交給libuv處理;
3) libuv自己是獨立的c語言庫,能夠直接使用c/c++來調用;
在瀏覽器端,JS是沒有能力進行文件讀取操做的。js最初的能力也就限制在表單校驗上。而在Node中,JS能夠操做本地文件,創建網絡鏈接。這確定是Node乾的好事!
再來講說Node中其它一些組成部分
builtin modules是由C++代碼寫成各種模塊,包含了crypto,zlib, file, net等基礎功能。
除了builtin modules, 還有一個native modules。它們是用js編寫的內建模塊,提供給程序開發者使用。
builtin modules 和 native modules都屬於核心模塊。核心模塊在Node源碼編譯的過程當中,編譯進了二進制文件。因此在Node進程啓動的時候,部分核心模塊就已經被直接加載到了內存當中。
至此,Node的基本構成和運行原理已經講完了。
補充一句:Node.js的單線程並非真正的單線程,只是開啓了單個線程(能夠定義爲主線程)進行業務處理,同時開啓了其餘線程專門處理I/O。當一個指令到達主線程,主線程發現有I/O以後,直接把這個事件傳給libuv處理。libuv會管理一個線程池,I/O操做就是由線程池裏面的線程完成的。等I/O 操做完成,libuv,會把對應I/O操做的回調放到事件循環當中。在線程上,不會等待I/O 操做完成,繼續執行後續的代碼。這就是「單線程」、「異步I/O」。
IO.js
var fork = require('child_process').fork; var fs = require('fs'); console.log('start......'); // blocking // console.log(fs.readFileSync('test.json', 'utf-8')); // non blocking var childProcess = fork('another-thread.js'); childProcess.on('message', function(data){ console.log(data); }) console.log('end !!!');
another-child.js
var fs = require('fs'); process.send( fs.readFileSync('test.json', 'utf-8'));
Everything runs in parallel except your code! 在Node中)除了代碼,一切都是並行的!
因爲node中主任務的執行是以單線程的方式進行,若是程序出錯致使崩潰,就會終止整個流程。爲此,市場上有些Node進程管理工具,它們會維護Node程序的狀態,當程序掛掉時,會自動重啓。好比咱們使用的pm2
。
對於node沒有的一些模塊(native modules),能夠引入外部模塊。這些外部模塊一般是其它開發者貢獻的。
那麼問題來了,對於數量衆多的模塊中,如何快速找到本身想要的並可以快速的引進到本身的項目當中。
這就是npm幫咱們作的工做。
Use npm to install, share, and distribute code; manage dependencies in your projects; and share & receive feedback with others.
NPM是JavaScript包的管理器。
npm consists of three distinct components:
Use the website
to discover packages, set up profiles, and manage other aspects of your npm experience. For example, you can set up Orgs (organizations) to manage access to public or private packages.
The CLI
runs from a terminal. This is how most developers interact with npm.
The registry
is a large public database of JavaScript software and the meta-information surrounding it.
npm默認隨node一塊兒安裝,在Node安裝完成後,npm已經安裝。
去npm官網,按關鍵詞查找。
npm install [module name]
:普通安裝方式,包安裝完成後,會在當前目錄生成一個node_modules
目錄。這是一個存放外部js模塊的地方,經過npm安裝的包都放在node_modules
下。npm install -g [module name]
:全局安裝,模塊被安裝在node安裝路徑下的 node_modules
中。npm install [folder path]
:能夠指定npm 安裝某個目錄folder path
下的的文件,前提是這個目錄下包含package.json
文件。npm install [module name]@[version]
: 安裝包的時候,指定對應的版本號。
npm install --save-prod [module name]
: 在本地安裝包,並將安裝信息寫入 package.json
文件中的dependencies
中, 不寫--save-prod 或者只寫 --save 默認跟 --save-prod同樣。npm install --save-dev [module name]
:在本地安裝包,並將安裝信息寫入 package.json
文件中的devDependencies
中
dependencies
:在生產環境中須要用到的依賴devDependencies
:在開發、測試環境中用到的依賴npm update [module name]
: 更新本地模塊npm uninstall [module name]
: 卸載模塊package.json
是一個node和npm都會自動讀取的配置文件,它裏面是個標準的JSON格式字符串。
對於NPM而言, package.json
作了如下工做:
對於你的項目而言,package.json
定義了一些基礎信息,好比項目名稱,版本等等。
pakcage.json必須具備的兩個字段: name
和 version
。這倆個字段有什麼意義呢?
NPM 做爲一個包管理平臺,當有開發者提交(publish)模塊時,必須提供一些基本信息便於管理。
npm run + 屬性名
執行{ "name": "vue-todo", "version": "1.0.0", "description": "a simply todolist using vuejs", "scripts": { "start": "node server.js", "stop": "egg-scripts stop --title=egg-server-example", "dev": "egg-bin dev" }, "dependencies": { // 線上生產環境必須,固然開發環境也會用到 "babel-runtime": "^6.23.0", "vue": "^2.0.1", "vue-localstorage": "^0.1.1", "vuex": "^2.2.1" }, "devDependencies": { // 開發環境會用到的東東 "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2", "webpack-merge": "^0.14.1" } }
版本格式:主版本號.次版本號.修訂號, 例如 1.2.3
版本號遞增規則以下:
在package.json定義版本規則的時候,能夠這麼作:
若是隻打算接受補丁版本的更新(也就是最後一位的改變),就能夠這麼寫:
若是接受小版本的更新(第二位的改變),就能夠這麼寫:
若是能夠接受大版本的更新(天然接受小版本和補丁版本的改變),就能夠這麼寫:
在使用npm install --save
|| npm install --save-dev
安裝的時候,寫入pakcage.json中的依賴,默認接受小版本的更新,即在版本號前添加 '^'。
Node中的模塊分爲兩類:
fs
、http
等)文件模塊是在運行的時候,動態加載。須要進行路徑分析,文件定位 和 編譯執行。
Node加載模塊時,優先從緩存中加載,若是緩存中不存在該模塊,纔會按照上述的三個步驟進行模塊加載。
模塊標識符在Node中,主要有如下幾類:
fs
, http
等.
或 ..
開頭的相對路徑文件模塊/
開頭的絕對路徑文件模塊這幾類模塊的加載速度是依次下降的。
module.js
同瀏覽器中的window
同樣,在Node中的全局變量都掛在global
下。
先說一下,經常使用到一些變量:
上面5個變量,貌似全局變量,但不是全局變量。他們都是模塊系統下的東西。
question: 它們不在全局變量下,那它們爲什麼能夠在模塊中直接調用?
在Node中,引入模塊分爲三個步驟:
編譯和執行是引入文件模塊的最後一個階段。
.js
文件 : Node 會對js源碼進行一個首尾的封裝。返回一個function,並將當前的環境的exports
,require
,module
,__dirname
,__filename
做爲形參傳遞給這個function。包裝後的代碼:
(function(exports, require, module, __filename, __dirname){ // js文件中的源碼 })
這就是爲何 它們不在 全局變量下,卻能夠在模塊當中使用的緣由。
.node
文件: 對於.node
文件, 實際上並不須要編譯過程。由於.node
文件自己就是C/C++編譯後的文件,它只有加載和執行過程。.json
文件: Node 會讀取json文件內容,並將它賦予exports對象,直接傳遞給第三方調用。
在NPM中的模塊,基本屬於Node中文件模塊裏的Javascript模塊。
Node的模塊系統參照CommonJS規範實現。
若是參數字符串不以./
或/
或../
開頭,說明要加載的不是一個文件,而是一個默認提供的核心模塊。
而後再尋找NPM模塊(即第三方模塊包,或本身寫的模塊包)
若是require當中的參數字符串以./(從當前目錄出發)或../(從上一級目錄出發)開頭:表示按照相對路徑,從當前文件所在的文件夾開始尋找要載入的模塊文件。
process
對象是一個全局變量,它提供當前 Node.js 進程的有關信息,以及控制當前 Node.js 進程。
process.argv
: 包含命令行參數的數組。第一個元素會是'node',第二個元素將是.js文件的名稱,接下來的參數依次是命令行參數process.execArgv
: 啓動進程所需的 node 命令行參數。這些參數不會在 process.argv 裏出現,而且不包含 node 執行文件的名字,或者任何在名字以後的參數。這些用來生成子進程,使之擁有和父進程有相同的參數process.env
: 獲取當前系統環境信息的對象,輸出內容是環境變量等內容,這個對象能夠修改nextTick(callback)
: 將callback放到事件輪詢隊列首位,下一次事件輪詢開始時,先執行callbackprocess.abort
:結束當前進程process.kill(pid)
:結束一個進程process.js
console.log(process.argv); console.log(process.execArgv);
node process.js # [ '/usr/local/bin/node', '/root/node-demo/process.js' ] # [] node process.js abc 234 cvb=cvb # [ '/usr/local/bin/node', # '/root/node-demo/process.js', # 'abc', # '234', # 'cvb=cvb' ] # [] node --harmony --use-openssl-ca process.js abc 234 cvb=cvb # [ '/usr/local/bin/node', # '/root/node-demo/process.js', # 'abc', # '234', # 'cvb=cvb' ] # [ '--harmony', '--use-openssl-ca' ]
file.js
questions require('../fs/file.js')
這裏是異步仍是同步?
等等............
[深刻淺出Node.js]()