Node.js入門筆記——我們踩的不是坑,是技術盲區

近幾年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等。

js全局對象.png

可見,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入門筆記先寫到這里了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容