近幾年Node.js大火特火,以至于所有程序員都想學它。不過也總有我這樣懶的遲遲未動。那些一樣節奏過慢的小伙伴們,今天我們一起來入個門吧。
一、Node.js簡介
Node.js是什么呢,Node.js和瀏覽器都是js的運行環境。區別在于,Node.js打破了js運行在瀏覽器端的限制,可以讓js運行在服務端。
那么它有哪些優點呢?一是事件驅動,二是非阻塞I/O模型,三是單線程,四是擁有世界最大的開源生態系統NPM。
安裝方法有兩種,一種是官網下載之后配置環境變量,然后通過REPL模式使用;第二種是使用nvm命令安裝使用。不熟悉的小伙伴可以自行搜索。
二、Node全局對象
先了解一下,js的全局對象有四種:
第一種是瀏覽器專屬的,如window、alert等;第二種是Node專屬的,如process / Buffer / __dirname / __filename;第三種是二者共有但實現方式不同的,如console / setTimeout /SetInterval等;第四種是二者共有并且屬于ECMAScript語言定義的部分,如Date / String / Promise等。
可見,Node不存在document和window,我們來了解一下Node的幾個全局對象吧~
1. process
它用來管理當前Node.js的進程狀態,提供與操作系統的簡單接口。它的常用屬性有:進程編號pid, 系統環境變量env, 命令行執行此腳本時的輸入參數argv, 當前操作系統的平臺platform等。
2. Buffer
這個全局對象可以讓js輕松處理二進制數據流
3. __filename和__dirname
當前所運行node腳本的文件路徑和所在目錄路徑。
我們來使用這幾個全局對象看一下node的一大特點吧:
setTimeout(() => { console.log('Hello World!');}, 3000);
console.log('當前進程 ID', process.pid);
console.log('當前腳本路徑', __filename);
const time = new Date();console.log('當前時間', time.toLocaleString());
執行上述代碼會發現,在定時器等待的3秒內,程序并沒有阻塞,而是繼續向下執行。這就是 Node.js 的異步非阻塞!
Node.js 能夠在等待的同時繼續處理新的請求,大大提高了系統的吞吐率。
三、模塊機制
敲黑板啦~~~
這是入門Node的關鍵。
1. 模塊機制的引入原因
為什么要引入模塊機制呢?因為之前js沒有模塊化機制。導入js模塊的方式是使用一系列script標簽,這種方式存在一些明顯的問題,比如容易產生命名沖突,js文件之間不能互相訪問,導入的script標簽不能輕易修改或刪除等。針對js模塊化機制的缺失,提出了AMD和CommonJS兩大規范。AMD在瀏覽器中使用普遍,CommonJS規范致力于提供統一的接口API,Node.js實現的就是這一模塊標準。
2. 是什么
什么是Node模塊呢?Node模塊分為核心模塊(Node內置)和文件模塊。文件模塊是用戶編寫的或npm安裝的模塊,它可以是一個單獨的文件(.js / .node / .json)或目錄。是目錄時,這個模塊的入口就是其中package.json文件里main字段指向的文件,或者是其中的index(.js / .node / .json)文件。
3. 模塊機制的實現
接下來,我們根據node的三個全局對象require / exports / module來分析一下模塊機制是怎么實現的。
(1)reqiure導入模塊
require('模塊名稱')或require('模塊的相對路徑')
為什么這樣導入就能找到對應模塊呢?以前使用的時候一直有這個疑問。實際上每個模塊都有一個路徑搜索列表module.paths,等下我們會講到。
(2)exports導出模塊
將模塊中的函數添加到exports對象中,就可以在外面調用這個函數/方法了。
const myModule = require('./myModule');
myModule.add(1, 2);
上面的add函數還可以用解構賦值的方式獲取:
const { add } = require('./myModule');
(3)module
module對象有id / path / filename / exports / parent / children / loaded / paths等字段。
A. id——模塊的唯一標識符
B. path和filename——模塊所在路徑和文件名
C.exports——模塊導出的內容,即上文說的exports對象是指向module.exports的應用。比如上文導出了add函數,則exports字段里有add函數。
D.parent和children——記錄模塊之間的導入關系,如main.js中require里myModule.js,main就是myModule.js的parent。
E.loaded——模塊是否被加載
F.paths——搜索文件模塊的路徑列表
(4)module.exports
請記住這句話:exports對象本質上是module.exports的引用。兩種導出方式:
第一種:exports.add=add;就是module.exports.add=add;
第二種:module.exports=add; 即把add賦值給module.exports對象
因此,在require時,第一種導出方式要訪問add屬性,第二種可以直接使用add函數。
const myModule = require('myModule');
myModule.add(1,3); // 第一種
const add = require('myModule');
add(1,3); // 第二種
四、命令行開發:接受輸入參數
Node.js還提供了Grunt / Gulp / Webpack等命令行工具。
1. 通過process.argv讀取命令行參數
上文提到過process是node的一個全局對象,它有一個argv屬性能獲取命令行參數的數組。
如果創建一個args.js文件,寫入代碼console.log(process.argv);
再運行命令 node args.js --time 5 --message "Hello world!"
這個數組的第0個元素是node的實際路徑,第1個元素是創建的js文件的路徑,后面的是輸入的所有參數。
2. npm
(1)npm -v 查看版本
(2)npm init 初始化npm項目,創建package.json文件
(3)npm install xxx 安裝npm包(安裝之后,package.json文件里會多一個dependencies字段記錄項目的直接依賴。denpendencies字段中還可以指定版本號)
(4)package-lock文件:鎖定全局依賴的精確版本號
3. npm scripts
(1)預定義腳本:運行npm <scriptName>,如test / start / install / publish等
(2)自定義腳本:運行npm run <scriptName>,如npm run custom
五、監聽exit事件
Node中的事件都是通過events核心模塊中的EventEmitter這個類實現的。EventEmitter包括on(監聽事件發生)和emit(觸發新事件)兩個方法。
const EventEmitter = require('events').EventEmitter;
const emitter = new EventEmitter();
// 監聽 connect 事件,注冊回調函數
emitter.on('connect', function (username) {
console.log(username + '已連接');
});
// 觸發 connect 事件,并且加上一個參數(即上面的 username)
emitter.emit('connect', '一只圖雀');
六、基礎API
我們來說三個常用的模塊——path、fs和http。使用這些模塊之前,都要先引用。引用方式以path為例,如:
const path = require('path');
1. path:路徑,用來處理處理文件路徑和目錄路徑,使用方法上代碼:
path.basename('/foo/bar/baz/asdf/quee.html'); // 文件名
path.dirname('/foo/bar/baz/asdf/quee'); // 目錄名
path.extname('index.html'); // extname后綴名
path.format({ // 對象格式化為字符串路徑
dir: 'C:\\path\\dir',
base: 'file.txt'
});
path.parse('C:\\path\dir\\file.txt'); // 字符串路徑格式化為對象
path.join('/foo', 'bar', 'baz/asdf', 'quee', '..')
2. fs:文件系統,用于與文件系統進行交互。常用的API有writeFile、readFile、mkdir、opendir、readdir、rmdir等。
可以傳3個參數,分別是path、data、callback。來個栗子:
fs.writeFile(path.join(__dirname, 'message.txt'), '您好呀', function(err){
if(err) throw err;
console.log('寫入成功!')
})
3. http:發送請求,常用的API如:http.createServer();
七、搭建靜態服務器
搭建靜態服務器一般需要四步:加載模塊--->創建http服務--->監聽請求事件--->監聽接口, 開啟服務。
const http = require('http');
const server = http.createServer();
server.on('request', function(req, res){
res.setHeader('Content-Type','text/html;charset=utf-8'); // 解決亂碼
res.write('<h1>hello world!</h1>');
res.end();
});
server.listen(8081, function(){
console.log('http://localhost:8081');
});
讓我們來練習一下,根據用戶的不同請求,響應現有的html文件:
const http = require('http');
const fs = require('fs');
const path = require('path');
http.createServer(function(req,res){
if(req.url==='/' || req.url==='/index'){
fs.readFile(path.join(__dirname, 'htmls', 'index.html'), function(){
if(err) throw err;
res.end(data);
})
}else if(req.url === '/resourcestyle.css'){
fs.readFile(path.join(__dirname, 'htmls','resource','style.css'),
function(){
if(err) throw err;
res.end(data);
}
)
}else{
fs.readFile(path.join(__dirname, 'htmls','404.html'), function(){
if(err) throw err;
res.end(data);
})
}
}).listen(8080, function(){
console.log('服務已啟動,請訪問:http://localhost:8080/');
});
好啦,Node.js入門筆記先寫到這里了。