面向前端工程師的Nodejs入門手冊(一)

全棧者發表於2019-08-20

前言

本文面向的讀者已經是瞭解JavaScript基本使用的前端程式設計師,但是缺乏服務端的經驗,接下來將帶你走進在服務端的世界,看看執行在服務端的JavaScript是如何工作的,它與執行在瀏覽器端的JavaScript有何異同,相比於瀏覽器能多做哪些事情,有何優勢。


面向前端工程師的Nodejs入門手冊(一)


文章通過例項的方式,讓你瞭解Nodejs能夠做什麼,可以解決一些什麼樣的問題,並且知道它的使用場景。如果你有興趣,請記住一定要自己動手,哪怕照著例項寫一遍,真真實實的感受程式碼執行時的喜悅與興奮,激發自己對新領域的興趣。

預設你已經安裝了Nodejs和npm包管理器,並且熟悉一些簡單的操作,如nodejs程式碼的執行啟動,npm包的安裝命令等基礎操作。

第一個服務端應用

1.hello world


首先通過一個前端工程師最常接觸卻不屬於前端範疇的內容去了解一下Nodejs,建立一個http服務。這裡使用Nodejs自帶的http模組建立一個http服務,你可以使用通過瀏覽器或者命令列來發起一個http請求,直觀的感受服務端的JavaScript。

// http.js
const http = require('http');

http.createServer((req, res) => {
  res.end('Hello World!');
}).listen(8000, ()=> {
  console.log('listen on 8000!');
})複製程式碼
複製程式碼

上面的例子中,通過使用node自帶的http模組,呼叫其http.creatServer方法在本機上開啟了一個http服務,監聽了本地的8000埠,程式碼邏輯很簡單,當接收到一個req請求時,呼叫res.end返回一個字串"hello world"給帶客戶端,旨在讓你對Nodejs有一個直觀的體驗。


可以使用node http.js命令來執行這段程式碼,通過瀏覽器來訪問http://127.0.0.1:8000或者http://localhost:800來檢視結果。

面向前端工程師的Nodejs入門手冊(一)


一個由Nodejs的http服務提供的hello world跑起來了,簡潔快速吧。接下來再來實現一個前端工作中與服務端最緊密的內容來看看Nodejs的魅力,資料介面。


2. 如何提供一個API


首先這裡確定所要提供的api是我們最常用的json格式,所以我們要注意後端返回給前端的資料型別。

const http = require('http');

const data = {
  name: 'Nodejs 入門示例',
  description: '這是返回資訊的描述內容',
  date: new Date()
};

http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json;charset=utf-8');
  const result = JSON.stringify(data);
  res.end(result);
}).listen(8000, ()=> {
  console.log('listen on 8000!');
})複製程式碼

上例子中,先確定了一個資料模型data,內部一共有三個欄位。這裡的資料模型就是前端開發前與後端所定義的返回格式,最終前端要拿到這個JSON格式的資料在客戶端進行處理。程式碼邏輯相比於Hello World,規定了返回的資料格式,以及設定了返回請求的文字型別為application/json,然後呼叫res.end返回到客戶端。


面向前端工程師的Nodejs入門手冊(一)


可以繼續使用瀏覽器發起http請求來檢視結果,一個簡單且熟悉的JSON資料介面已經完成了。你可以按照前端最常用的呼叫方式,如ajax或者axios來請求介面來在你的前端專案使用它。

但是上面例子中的介面和我們常用的介面有一個差異點,就是介面名稱和返回內容均不規範,使用者直接通過沒有路徑的http://127.0.0.1:8000來使用介面,這跟我們日常使用的介面是完全不一樣的,那我們接下來再看一下如何給提供一個規範化的介面呢?


3.一個規範的介面


規範的介面具備哪些條件呢?簡單總結一下。

介面名稱:介面名稱要體現出大致的使用場景,例如增刪改查的動作。

介面返回:介面返回要有規範化的標識,如成功與否錯誤內容等。


接下來通過上面這兩個點,按照標準的介面規範來實現一下上面的介面,看看Nodejs是如何給前端提供介面的。這裡先約定要提供的介面名稱內含api標誌,api所要做的動作等一些關鍵資訊。如下格式:


http://localhost:8000/api/search/data?userId=12345

const http = require('http');
const url = require('url');
const qs = require('querystring');

// 生成一段返回值
const genResponse = userId => ({
  success: true,
  data: {
    userId,
    name: 'Nodejs 入門示例',
    description: '這是返回資訊的描述內容',
    date: new Date()
  }
});

// http服務
http.createServer((req, res) => {
  res.setHeader('Content-Type', 'application/json;charset=utf-8');
  const reqUrl = url.parse(req.url);
  // 判斷介面路徑是否是約定好的
  if (reqUrl.pathname === '/api/search/data') {
    // 獲取連結上傳來的userId引數
    const uid = qs.parse(reqUrl.query).userId;
    // 生成返回值
    const result = JSON.stringify(genResponse(uid));
    res.end(result);
  } else {
    res.writeHead(404);
    res.end('NotFund');
  }
}).listen(8000, ()=> {
  console.log('listen on 8000!');
})
複製程式碼


上面的例子中,新使用Nodejs另一個自帶的模組url,url模組顧名思義是一個處理href的庫,它將href拆分成各個子內容,同時為了能處理客戶端帶來的userId引數還使用了自帶的querystring模組,它可以將連結上問號後的query引數獲取到,以便服務端程式碼能使用他們,他們均屬於工具庫,下面看看官方對於URL模組將href拆分的顆粒度圖,清晰的瞭解一下一個請求連結,可以被拆分成什麼顆粒度。

面向前端工程師的Nodejs入門手冊(一)


例項中的原始碼簡單解析一下,當服務接收到請求時,先判斷請求的api名稱是不是事先約定好的/api/search/data,判斷通過後,將前端傳遞在連結上的引數userId獲取到,處理後插入到返回的json資料中,即可通過res.end來下發資料,如果路徑判斷失敗,則返回404的狀態碼,並且進入Notfund頁面,

面向前端工程師的Nodejs入門手冊(一)


一個規範的介面已經開發完成了,簡單總結一下,上面以漸進式的方式瞭解了Nodejs如何給前端來提供一個規範化的http介面,瞭解了服務端的JavaScript所具備的能力,接下來再從另一個前端工程師比較少接觸的內容——檔案操作,來進一步瞭解Nodejs。


檔案操作那些事

對於檔案操作相關的內容,前端程式設計師一般是不會觸及的,而且JavaScript語言本身也並沒有暴露操作檔案的方法。而在Nodejs中,本身便提供了fs檔案操作模組,這個模組模組底層並不是JavaScript來編寫的,是具備操作檔案的C++語言編寫的,其封裝完成後將上層暴露給Nodejs,然後便可以使用JavaScript的語法來呼叫它。


1.讀一個檔案

在Nodejs中,讀檔案有兩種形式,一種是同步的另一種是非同步的,同步可以理解為讀檔案這個過程要等待,就是一旦執行的讀這個操作的時候,你的程式碼就被”卡“住了,直到檔案讀完才能繼續執行,來看看下面的例子。

先新增一個test.md檔案配合讀操作,檔案內容為:### 我是一個檔案

// fsread.js
const fs = require('fs');

const file = fs.readFileSync('./test.md', 'utf8');

console.log(file);複製程式碼


使用node fsread.js來執行上面的程式碼,從上面簡單的幾行你就可以發現成功將test.md檔案裡的內容讀出來,可以列印到了命令列console裡,突然發現JavaScript語言的強大了吧,很神奇吧。


但是Nodejs天生是為非同步而生的,所以必須要體驗一下非同步讀檔案是怎麼的過程,與同步的表現有何異同。所以 下面示例一個非同步回撥的方式去讀一個檔案,非同步的意思就是讀檔案這個操作進行的同時,讀操作下面的JavaScript程式碼也在執行,就如同我們熟悉的軟體後臺執行一樣,你可以繼續你的桌面操作。

const fs = require('fs');

console.log('sync start');

fs.readFile('./test.md', 'utf8', (err, data) => {
  console.log('test.md 的內容');
  console.log(data);
});

console.log('sync progress');

fs.readFile('./test2.md', 'utf8', (err, data) => {
  console.log('test2.md 的內容');
  console.log(data);
});

console.log('sync finish');複製程式碼
複製程式碼

在上面的例子中,要求是一次讀取兩個檔案,兩個檔案之間並沒有相關依賴性,所以這種 場景下我們更希望他們各做各的,無需去等。所以沒有必要像上面同步的方式,等第一個結束再進入第二個的讀取,所以使用非同步方式更合適。


面向前端工程師的Nodejs入門手冊(一)


從上面的執行結果也可以看出來,fs.readFile這個非同步回撥操作均在三個同步程式碼 console的後面,並沒有像同步等待讀操作的結束。


下面來個圖看看這個同步API和非同步API有何異同。


面向前端工程師的Nodejs入門手冊(一)


圖片上半部分是非同步讀檔案,可以看出來,讀的操作可以理解為同時刻執行的。

圖片下半部分是同步讀檔案,第二個讀的操作需要先等待第一個讀完才可以。

舉個現實生活中同步的場景,運動會接力賽,A,B兩個班比賽接力,雖然A,B兩個班無依賴,但是A班的第二名同學則需要第一名同學的接力棒拿到後才可繼續跑,此時A班的運動員之間就屬於同步阻塞型別。

在舉個非同步的場景,老闆通過全員會議下達了一個任務,任務是大家做一百個俯臥撐,誰先做完任務則可以領取10000元紅包的獎勵,大家聽到後紛紛原地做起,在這時候各個員工之間就是非同步的,他們各做各的,誰先做完就可以執行老闆給他們的開始說的領取獎勵操作,這個操作可以理解為非同步回撥函式。


2. 寫一個檔案

Nodejs寫檔案也是有兩個型別,同步與非同步,實際執行流程與上面的“讀”是一樣的。

下面進入同步讀檔案的例子,執行下面的程式碼你會發現多了一個test3.md檔案,並且寫入'### 我是測試檔案test3'的內容

const fs = require('fs');

const body = '### 我是測試檔案test3的內容';
fs.writeFileSync('./test3.md', body);複製程式碼

再來看一下非同步寫一個檔案的例子,做一個對比。

// 來一個非同步的看看。
const fs = require('fs');

const body = '### 我是測試檔案test4的內容';
fs.writeFile('./test4.md', body, (err) => {
  if (err) throw err;
  console.log('檔案test4已被儲存');
});

const body2 = '### 我是測試檔案test5的內容';
fs.writeFile('./test5.md', body2, (err) => {
  if (err) throw err;
  console.log('檔案test5已被儲存');
});複製程式碼

這是寫檔案的結果,這時候你的本地會多出兩個檔案test4.md和test5.md出來。

面向前端工程師的Nodejs入門手冊(一)


3.其他檔案操作

其實Nodejs提供了豐富的檔案操作介面,除了讀寫,還有像複製,給檔案授權,刪除一個檔案,資料夾的操作,檔案內容的監聽等,如果你有對檔案的操作需求,請先在文件查一下看是否能夠滿足你。

文件地址:http://nodejs.cn/api/fs.html

下面來一個檔案內容監聽的例子,帶你看看一個檔案變化時也能被觀察到樂趣。

const fs = require('fs');

fs.watch('./test6.md', 'utf8', (eventType, filename)=>{
  // eventType 是 'rename' 或 'change',
  // filename 是觸發事件的檔案的名稱
  console.log('eventType', eventType);
  console.log('filename', filename);
});複製程式碼

我將test6.md的內容進行手動的變化,並且改了名字,這裡都被監聽到了,是不是很有趣,跟我一起來練習吧。

面向前端工程師的Nodejs入門手冊(一)


總結

本文從前端工程師們最常接觸卻又不屬於前端領域的兩個方面,http服務與檔案操作展開了學習,從幾個簡單易懂的例子帶領去了解了Nodejs。回想當初我為什麼學習Nodejs,其實就是因為它的簡單便捷,幾行程式碼就能做出一些我想要的效果,能快速的完成我的要求。

如果上面的入門示例也讓你對Nodejs有了濃厚的興趣,那麼請快速動手學習起來吧,看再多文章不如自己手寫一遍,從零到一,跟我一起學習吧。

文章用到的程式碼均可在此獲取:

https://github.com/FantasyGao/Practice-book/tree/master/nodejs


如上內容均為自己總結,難免會有錯誤或者認識偏差,如有問題,希望大家留言指正,以免誤人,若有什麼問題請留言,會盡力回答之。如果對你有幫助不要忘了分享給你的朋友或者點選右下方的“在看”哦!也可以關注作者,檢視歷史文章並且關注最新動態,助你早日成為一名全棧工程師!

面向前端工程師的Nodejs入門手冊(一)



相關文章