中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

了解NodeJS看這一篇就夠了

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬Linux鏡像隨意使用

摘要:這篇文章適合對(duì)Node一無所知或了解不多的初學(xué)者閱讀。全面但不深入地講了包括http模塊、express、mongodb和RESTful API等知識(shí)點(diǎn)。

如果你是前端開發(fā)工作者,那么對(duì)你來說,基于NodeJS編寫web程序已經(jīng)不是什么新聞了。而不管是NodeJS還是web程序都非常依賴JavaScript這門語言。

首先,我們要認(rèn)識(shí)到一點(diǎn):Node并不是銀彈。也就是說,它不是所有項(xiàng)目的最佳解決方案。任何人都可以基于Node創(chuàng)建一個(gè)服務(wù)器,但是這需要你對(duì)編寫web程序的語言具有一定程序的有很深入的理解。

最近,我從學(xué)習(xí)Node的過程中發(fā)現(xiàn)了許多樂趣,同時(shí)我也意識(shí)到我已經(jīng)掌握了一定的知識(shí),應(yīng)該分享出來,并且從社區(qū)獲得反饋來提升自己。

那么就讓我們開始吧。

Node.js出現(xiàn)之前

在Node.js出現(xiàn)之前,web應(yīng)用往往基于客戶端/服務(wù)器模式,當(dāng)客戶端向服務(wù)器請(qǐng)求資源時(shí),服務(wù)器會(huì)響應(yīng)這個(gè)請(qǐng)求并且返回相應(yīng)的資源。服務(wù)器只會(huì)在接收到客戶端請(qǐng)求時(shí)才會(huì)做出響應(yīng),同時(shí)會(huì)在響應(yīng)結(jié)束后關(guān)閉與客戶端的連接。

這種設(shè)計(jì)模式需要考慮到效率問題,因?yàn)槊恳粋(gè)請(qǐng)求都需要處理時(shí)間和資源。因此,服務(wù)器在每一次處理請(qǐng)求的資源后應(yīng)該關(guān)閉這個(gè)連接,以便于響應(yīng)其他請(qǐng)求。

如果同時(shí)有成千上萬個(gè)請(qǐng)求同時(shí)發(fā)往服務(wù)器,服務(wù)器會(huì)變成什么樣子呢?當(dāng)你問出這個(gè)問題時(shí),你一定不想看到一個(gè)請(qǐng)求必須等待其他請(qǐng)求被響應(yīng)后才能輪到他的情形,因?yàn)檫@段延遲實(shí)在是太長(zhǎng)了。

想象一下,當(dāng)你想要打開FaceBook,但因?yàn)樵谀阒耙呀?jīng)有上千人向服務(wù)器發(fā)出過請(qǐng)求,所以你需要等待5分鐘才能看到內(nèi)容。有沒有一種解決方案來同時(shí)處理成百上千個(gè)請(qǐng)求呢?所幸我們有線程這個(gè)工具。

線程是系統(tǒng)能夠并行處理多任務(wù)所使用的方式。每一個(gè)發(fā)給服務(wù)器的請(qǐng)求都會(huì)開啟一個(gè)新的線程,而每個(gè)線程會(huì)獲取它運(yùn)行代碼所需要的一切。

這聽上去很奇怪?讓我們來看看這個(gè)例子:

想象餐館里只有一個(gè)廚師提供食物,當(dāng)食物需求越來越多,事情也會(huì)變得越來越糟。在之前的所有訂單都被處理前,人們不得不等待很長(zhǎng)時(shí)間。而我們能想到的方法就是增加更多的服務(wù)員來解決這個(gè)問題,對(duì)吧?這樣能夠同時(shí)應(yīng)付更多的顧客。

每一個(gè)線程都是一個(gè)新的服務(wù)員,而顧客就是瀏覽器。我想理解這一點(diǎn)對(duì)你來說并不困難。

但是這種系統(tǒng)有一個(gè)副作用,讓請(qǐng)求數(shù)達(dá)到一定數(shù)量時(shí),過多的線程會(huì)占用所有系統(tǒng)內(nèi)存和資源。重新回到我們的例子里,雇傭越來越多的人來供應(yīng)食物必然會(huì)提高人力成本和占用更多的廚房空間。

當(dāng)然,如果服務(wù)器在響應(yīng)完客戶端的請(qǐng)求后立刻切斷連接并釋放所有資源,這對(duì)我們來說自然是極好的。

多線程系統(tǒng)擅長(zhǎng)于處理CPU密集型操作,因?yàn)檫@些操作需要處理大量的邏輯,而且計(jì)算這些邏輯會(huì)花費(fèi)更多的時(shí)間。如果每一個(gè)請(qǐng)求都會(huì)被一個(gè)新的線程處理,那么主線程可以被解放出來去處理一些重要的計(jì)算,這樣也能讓整個(gè)系統(tǒng)變得更快。

讓主線程不必忙于所有的運(yùn)算操作是一種提高效率的好辦法,但是能不能在此之上更進(jìn)一步呢?

NodeJS來了

想象一下我們現(xiàn)在已經(jīng)有了一個(gè)多線程服務(wù)器,運(yùn)行于Ruby on rails環(huán)境。我們需要它讀取文件并且發(fā)送給請(qǐng)求這個(gè)文件的瀏覽器。首先要知道的是Ruby并不會(huì)直接讀取文件,而是通知文件系統(tǒng)去讀取指定文件并返回它內(nèi)容。顧名思義,文件系統(tǒng)就是計(jì)算機(jī)上一個(gè)專門用來存取文件的程序。

Ruby在向文件系統(tǒng)發(fā)出通知后會(huì)一直等待它完成讀取文件的操作,而不是轉(zhuǎn)頭去處理其他任務(wù)。當(dāng)文件系統(tǒng)處理任務(wù)完成后,Ruby才會(huì)重新啟動(dòng)去收集文件內(nèi)容并且發(fā)送給瀏覽器。

這種方式很顯然會(huì)造成阻塞的情況,而NodeJS的誕生就是為了解決這個(gè)痛點(diǎn)。如果我們使用Node來向文件系統(tǒng)發(fā)出通知,在文件系統(tǒng)去讀取文件的這段時(shí)間里,Node會(huì)去處理其他請(qǐng)求。而讀取文件的任務(wù)完成后,文件系統(tǒng)會(huì)通知Node去讀取資源然后將它返回給瀏覽器。事實(shí)上,這里的內(nèi)部實(shí)現(xiàn)都是依賴于Node的事件循環(huán)。

Node的核心就是JavaScript和事件循環(huán)。

簡(jiǎn)單地說,事件循環(huán)就是一個(gè)等待事件然后在需要事件發(fā)生時(shí)去觸發(fā)它們的程序。此外還有一點(diǎn)很重要,就是Node和JavaScript一樣都是單線程的。

還記得我們舉過的餐廳例子嗎?不管顧客數(shù)量有多少,Node開的餐廳里永遠(yuǎn)只有一個(gè)廚師烹飪食物。

與其他語言不同,NodeJS不需要為每一個(gè)請(qǐng)求開啟一個(gè)新的線程,它會(huì)接收所有請(qǐng)求,然后將大部分任務(wù)委托給其他的系統(tǒng)。 Libuv 就是一個(gè)依賴于OS內(nèi)核去高效處理這些任務(wù)的庫(kù)。當(dāng)這些隱藏于幕后的工作者處理完委托給它們的事件后,它們會(huì)觸發(fā)綁定在這些事件上的回調(diào)函數(shù)去通知NodeJS。

這兒我們接觸到了回調(diào)這個(gè)概念;卣{(diào)理解起來并不困難,它是被其他函數(shù)當(dāng)作參數(shù)傳遞的函數(shù),并且在某種特定情況下會(huì)被調(diào)用。

NodeJS開發(fā)者們做的最多的就是編寫事件處理函數(shù),而這些處理函數(shù)會(huì)在特定的NodeJS事件發(fā)生后被調(diào)用。

NodeJS雖然是單線程,但它比多線程系統(tǒng)要快得多。這是因?yàn)槌绦蛲⒉皇侵挥泻臅r(shí)巨長(zhǎng)的數(shù)學(xué)運(yùn)算和邏輯處理,大部分時(shí)間里它們只是寫入文件、處理網(wǎng)絡(luò)請(qǐng)求或是向控制臺(tái)和外部設(shè)備申請(qǐng)權(quán)限。這些都是NodeJS擅長(zhǎng)處理的問題:當(dāng)NodeJS在處理這些事情時(shí),它會(huì)迅速將這些事件委托給專門的系統(tǒng),轉(zhuǎn)而去處理下一個(gè)事件。

如果你繼續(xù)深入下去,你也許會(huì)意識(shí)到NodeJS并不擅長(zhǎng)處理消耗CPU的操作。因?yàn)镃PU密集型操作會(huì)占用大量的主線程資源。對(duì)于單線程系統(tǒng)來說,最理想的情況就是避免這些操作來釋放主線程去處理別的事情。

還有一個(gè)關(guān)鍵點(diǎn)是在JavaScript中,只有你寫的代碼不是并發(fā)執(zhí)行的。也就是說,你的代碼每次只能處理一件事,而其他工作者,比如文件系統(tǒng)可以并行處理它們手頭的工作。

如果你還不能理解的話,可以看看下面的例子:

很久以前有一個(gè)國(guó)王,他有一千個(gè)官員。國(guó)王寫了一個(gè)任務(wù)清單讓官員去做,清單非常非常非常長(zhǎng)。有一個(gè)宰相,根據(jù)清單將任務(wù)委托給其他所有官員。每完成一項(xiàng)任務(wù)他就將結(jié)果報(bào)告給國(guó)王,之后國(guó)王又會(huì)給他另一份清單。因?yàn)樵诠賳T工作的時(shí)候,國(guó)王也在忙于寫其他清單。

這個(gè)例子要講的是即使有很多官員在并行處理任務(wù),國(guó)王每次也只能做一件事。這里,國(guó)王就是你的代碼,而官員就是藏于NodeJS幕后的系統(tǒng)工作者。所以說,除了你的代碼,每件事都是并行發(fā)生的。

好了,讓我們繼續(xù)這段NodeJS之旅吧。

用NodeJS寫一個(gè)web應(yīng)用

用NodeJS寫一個(gè)web應(yīng)用相當(dāng)于編寫事件回調(diào)。讓我們來看看下面的例子:

  1. 新建并進(jìn)入一個(gè)文件夾
  2. 執(zhí)行 npm init 命令,一直回車直到你在文件夾根目錄下創(chuàng)建了一個(gè)package.json文件。
  3. 新建一個(gè)名為server.js的文件,復(fù)制并粘貼下面的代碼:

    //server.js
    const http = require('http'),
          server = http.createServer();
    
    server.on('request',(request,response)=>{
       response.writeHead(200,{'Content-Type':'text/plain'});
       response.write('Hello world');
       response.end();
    });
    
    server.listen(3000,()=>{
      console.log('Node server created at port 3000');
    });
  4. 在命令行中,輸入 node server.js ,你會(huì)看到下面的輸出:

    node server.js
    //Node server started at port 3000

    打開瀏覽器并且進(jìn)入 localhost:3000 ,你應(yīng)該能夠看到一個(gè) Hello world 信息。

首先,我們引入了http模塊。這個(gè)模塊提供了處理htpp操作的接口,我們調(diào)用 createServer() 方法來創(chuàng)建一個(gè)服務(wù)器。

之后,我們?yōu)閞equest事件綁定了一個(gè)事件回調(diào),傳遞給on方法的第二個(gè)參數(shù)。這個(gè)回調(diào)函數(shù)有2個(gè)參數(shù)對(duì)象,request代表接收到的請(qǐng)求,response代表響應(yīng)的數(shù)據(jù)。

不僅僅是處理request事件,我們也可以讓Node去做其他事情。

//server.js
const http = require('http'),
server = http.createServer((request,response)=>{
    response.writeHead(200,{'Content-Type':'text/plain'});
    response.write('Hello world');
    response.end();
});
server.listen(3000,()=>{
    console.log('Node server created at port 3000');
});

在當(dāng)面的代碼里,我們傳給createServer()一個(gè)回調(diào)函數(shù),Node把它綁定在request事件上。這樣我們只需要關(guān)心request和response對(duì)象了。

我們使用 response.writeHead() 來設(shè)置返回報(bào)文頭部字段,比如狀態(tài)碼和內(nèi)容類型。而 response.write() 是對(duì)web頁(yè)面進(jìn)行寫入操作。最后使用 response.end() 來結(jié)束這個(gè)響應(yīng)。

最后,我們告知服務(wù)器去監(jiān)聽3000端口,這樣我們可以在本地開發(fā)時(shí)查看我們web應(yīng)用的一個(gè)demo。listen這個(gè)方法要求第二個(gè)參數(shù)是一個(gè)回調(diào)函數(shù),服務(wù)器一啟動(dòng),這個(gè)回調(diào)函數(shù)就會(huì)被執(zhí)行。

習(xí)慣回調(diào)

Node是一個(gè)單線程事件驅(qū)動(dòng)的運(yùn)行環(huán)境,也就是說,在Node里,任何事都是對(duì)事件的響應(yīng)。

前文的例子可以改寫成下面這樣:

//server.js
const http = require('http'),
      
makeServer = function (request,response){
   response.writeHead(200,{'Content-Type':'text/plain'});
   response.write('Hello world');
   response.end();
},
      
server = http.createServer(makeServer);

server.listen(3000,()=>{
  console.log('Node server created at port 3000');

makeServer 是一個(gè)回調(diào)函數(shù),由于JavaScript把函數(shù)當(dāng)作一等公民,所以他們可以被傳給任何變量或是函數(shù)。如果你還不了解JavaScript,你應(yīng)該花點(diǎn)時(shí)間去了解什么是事件驅(qū)動(dòng)程序。

當(dāng)你開始編寫一些重要的JavaScript代碼時(shí),你可能會(huì)遇到“回調(diào)地獄”。你的代碼變得難以閱讀因?yàn)榇罅康暮瘮?shù)交織在一起,錯(cuò)綜復(fù)雜。這時(shí)你想要找到一種更先進(jìn)、有效的方法來取代回調(diào)?纯碢romise吧, Eric Elliott 寫了一篇文章來 講解什么是Promise ,這是一個(gè)好的入門教程。

NodeJS路由

一個(gè)服務(wù)器會(huì)存儲(chǔ)大量的文件。當(dāng)瀏覽器發(fā)送請(qǐng)求時(shí),會(huì)告知服務(wù)器他們需要的文件,而服務(wù)器會(huì)將相應(yīng)的文件返回給客戶端。這就叫做路由。

在NodeJS中,我們需要手動(dòng)定義自己的路由。這并不麻煩,看看下面這個(gè)基本的例子:

//server.js
const http = require('http'),
      url = require('url'),
 
makeServer = function (request,response){
   let path = url.parse(request.url).pathname;
   console.log(path);
   if(path === '/'){
      response.writeHead(200,{'Content-Type':'text/plain'});
      response.write('Hello world');
   }
   else if(path === '/about'){
     response.writeHead(200,{'Content-Type':'text/plain'});
     response.write('About page');
   }
   else if(path === '/blog'){
     response.writeHead(200,{'Content-Type':'text/plain'});
     response.write('Blog page');
   }
   else{
     response.writeHead(404,{'Content-Type':'text/plain'});
     response.write('Error page');
   }
   response.end();
 },
server = http.createServer(makeServer);
server.listen(3000,()=>{
 console.log('Node server created at port 3000');
});

粘貼這段代碼,輸入 node server.js 命令來運(yùn)行。在瀏覽器中打開 localhost:3000 和 localhost:3000/abou ,然后在試試打開 localhost:3000/somethingelse ,是不是跳轉(zhuǎn)到了我們的錯(cuò)誤頁(yè)面?

雖然這樣滿足了我們啟動(dòng)服務(wù)器的基本要求,但是要為服務(wù)器上每一個(gè)網(wǎng)頁(yè)都寫一遍代碼實(shí)在是太瘋狂了。事實(shí)上沒有人會(huì)這么做,這個(gè)例子只是讓你了解路由是怎么工作的。

如果你有注意到,我們引入了url這個(gè)模塊,它能讓我們處理url更加方便。

為parse()方法傳入一個(gè)url字符串參數(shù),這個(gè)方法會(huì)將url拆分成 protocol 、 host 、 path 和 querystring 等部分。如果你不太了解這些單詞,可以看看下面這張圖:

所以當(dāng)我們執(zhí)行 url.parse(request.url).pathname 語句時(shí),我們得到一個(gè)url路徑名,或者是url本身。這些都是我們用來進(jìn)行路由請(qǐng)求的必要條件。不過這件事還有個(gè)更簡(jiǎn)單的方法。

使用Express進(jìn)行路由

如果你之前做過功課,你一定聽說過Express。這是一個(gè)用來構(gòu)建web應(yīng)用以及API的NodeJS框架,它也可以用來編寫NodeJS應(yīng)用。接著往下看,你會(huì)明白為什么我說它讓一切變得更簡(jiǎn)單。

在你的終端或是命令行中,進(jìn)入電腦的根目錄,輸入 npm install express --save 來安裝Express模塊包。要在項(xiàng)目中使用Express,我們需要引入它。

const express = require('express');

歡呼吧,生活將變得更美好。

現(xiàn)在,讓我們用express進(jìn)行基本的路由。

//server.js
const express = require('express'),
      server = express();

server.set('port', process.env.PORT || 3000);

//Basic routes
server.get('/', (request,response)=>{
   response.send('Home page');
});

server.get('/about',(request,response)=>{
   response.send('About page');
});

//Express error handling middleware
server.use((request,response)=>{
   response.type('text/plain');
   response.status(505);
   response.send('Error page');
});

//Binding to a port
server.listen(3000, ()=>{
  console.log('Express server started at port 3000');
});

譯者注:這里不是很理解為什么代碼中錯(cuò)誤狀態(tài)碼是505。

現(xiàn)在的代碼是不是看上去更加清晰了?我相信你很容易就能理解它。

首先,當(dāng)我們引入express模塊后,得到的是一個(gè)函數(shù)。調(diào)用這個(gè)函數(shù)后就可以開始啟動(dòng)我們的服務(wù)器了。

接下來,我們用 server.set() 來設(shè)置監(jiān)聽端口。而 process.env.PORT 是程序運(yùn)行時(shí)的環(huán)境所設(shè)置的。如果沒有這個(gè)設(shè)置,我們默認(rèn)它的值是3000.

然后,觀察上面的代碼,你會(huì)發(fā)現(xiàn)Express里的路由都遵循一個(gè)格式:

server.VERB('route',callback);

這里的VERB可以是GET、POST等動(dòng)作,而pathname是跟在域名后的字符串。同時(shí),callback是我們希望接收到一個(gè)請(qǐng)求后觸發(fā)的函數(shù)。

最后我們?cè)僬{(diào)用 server.listen() ,還記得它的作用吧?

以上就是Node程序里的路由,下面我們來挖掘一下Node如何調(diào)用數(shù)據(jù)庫(kù)。

NodeJS里的數(shù)據(jù)庫(kù)

很多人喜歡用JavaScript來做所有事。剛好有一些數(shù)據(jù)庫(kù)滿足這個(gè)需求,比如MongoDB、CouchDB等待。這些數(shù)據(jù)庫(kù)都是NoSQL數(shù)據(jù)庫(kù)。

一個(gè)NoSQL數(shù)據(jù)庫(kù)以鍵值對(duì)的形式作為數(shù)據(jù)結(jié)構(gòu),它以文檔為基礎(chǔ),數(shù)據(jù)都不以表格形式保存。

我們來可以看看MongoDB這個(gè)NoSQL數(shù)據(jù)庫(kù)。如果你使用過MySQL、SQLserver等關(guān)系型數(shù)據(jù)庫(kù),你應(yīng)該熟悉數(shù)據(jù)庫(kù)、表格、行和列等概念。 MongoDB與他們相比并沒有特別大的區(qū)別,不過還是來比較一下吧。

譯者注:這兒應(yīng)該有個(gè)表格顯示MongoDB與MySQL的區(qū)別,但是原文里沒有顯示。

為了讓數(shù)據(jù)更加有組織性,在向MongoDB插入數(shù)據(jù)之前,我們可以使用Mongoose來檢查數(shù)據(jù)類型和為文檔添加驗(yàn)證規(guī)則。它看上去就像Mongo與Node之間的中介人。

由于本文篇幅較長(zhǎng),為了保證每一節(jié)都盡可能的簡(jiǎn)短,請(qǐng)你先閱讀官方的 MongoDB安裝教程 。

此外, Chris Sevilleja 寫了一篇 Easily Develop Node.js and MongoDB Apps with Mongoose ,我認(rèn)為這是一篇適合入門的基礎(chǔ)教程。

使用Node和Express編寫RESTful API

API是應(yīng)用程序向別的程序發(fā)送數(shù)據(jù)的通道。你有沒有登陸過某些需要你使用facebook賬號(hào)登錄的網(wǎng)頁(yè)?facebook將某些函數(shù)公開給這些網(wǎng)站使用,這些就是API。

一個(gè)RESTful API應(yīng)該不以服務(wù)器/客戶端的狀態(tài)改變而改變。通過使用一個(gè)REST接口,不同的客戶端,即使它們的狀態(tài)各不相同,但是在訪問相同的REST終端時(shí),應(yīng)該做出同一種動(dòng)作,并且接收到相同的數(shù)據(jù)。

API終端是API里返回?cái)?shù)據(jù)的一個(gè)函數(shù)。

編寫一個(gè)RESTful API涉及到使用JSON或是XML格式傳輸數(shù)據(jù)。讓我們?cè)贜odeJS里試試吧。我們接下來會(huì)寫一個(gè)API,它會(huì)在客戶端通過AJAX發(fā)起請(qǐng)求后返回一個(gè)假的JSON數(shù)據(jù)。這不是一個(gè)理想的API,但是能幫助我們理解在Node環(huán)境中它是怎么工作的。

  1. 創(chuàng)建一個(gè)叫node-api的文件夾;
  2. 通過命令行進(jìn)入這個(gè)文件夾,輸入 npm init 。這會(huì)創(chuàng)建一個(gè)收集依賴的文件;
  3. 輸入 npm install --save express 來安裝express;
  4. 在根目錄新建3個(gè)文件: server.js , index.html 和 users.js ;
  5. 復(fù)制下面的代碼到相應(yīng)的文件:
//users.js
module.exports.users = [
 {
  name: 'Mark',
  age : 19,
  occupation: 'Lawyer',
  married : true,
  children : ['John','Edson','ruby']
 },
  
 {
  name: 'Richard',
  age : 27,
  occupation: 'Pilot',
  married : false,
  children : ['Abel']
 },
  
 {
  name: 'Levine',
  age : 34,
  occupation: 'Singer',
  married : false,
  children : ['John','Promise']
 },
  
 {
  name: 'Endurance',
  age : 45,
  occupation: 'Business man',
  married : true,
  children : ['Mary']
 },
]

這是我們傳給別的應(yīng)用的數(shù)據(jù),我們導(dǎo)出這份數(shù)據(jù)讓所有程序都可以使用。也就是說,我們將users這個(gè)數(shù)組保存在 modules.exports 對(duì)象中。

//server.js
const express = require('express'),
      server = express(),
      users = require('./users');

//setting the port.
server.set('port', process.env.PORT || 3000);

//Adding routes
server.get('/',(request,response)=>{
 response.sendFile(__dirname + '/index.html');
});

server.get('/users',(request,response)=>{
 response.json(users);
});

//Binding to localhost://3000
server.listen(3000,()=>{
 console.log('Express server started at port 3000');
});

我們執(zhí)行 require('express') 語句然后使用 express() 創(chuàng)建了一個(gè)服務(wù)變量。如果你仔細(xì)看,你還會(huì)發(fā)現(xiàn)我們引入了別的東西,那就是 users.js 。還記得我們把數(shù)據(jù)放在哪了嗎?要想程序工作,它是必不可少的。

express有許多方法幫助我們給瀏覽器傳輸特定類型的內(nèi)容。 response.sendFile() 會(huì)查找文件并且發(fā)送給服務(wù)器。我們使用 __dirname 來獲取服務(wù)器運(yùn)行的根目錄路徑,然后我們把字符串 index.js 加在路徑后面保證我們能夠定位到正確的文件。

response.json() 向網(wǎng)頁(yè)發(fā)送JSON格式內(nèi)容。我們把要分享的users數(shù)組傳給它當(dāng)參數(shù)。剩下的代碼我想你在之前的文章中已經(jīng)很熟悉了。

//index.html

<!DOCTYPE html>
<html>
<head>
 <meta charset="utf-8">
 <title>Home page</title>
</head>
<body>
 <button>Get data</button>
<script src="/uploads/201802/10/15182277063.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
 
  <script type="text/javascript">
  
    const btn = document.querySelector('button');
    btn.addEventListener('click',getData);
    function getData(e){
        $.ajax({
        url : '/users',
        method : 'GET',
        success : function(data){
           console.log(data);
        },
      
        error: function(err){
          console.log('Failed');
        }
   });
  } 
 </script>
</body>
</html>

在文件夾根目錄中執(zhí)行 node server.js ,現(xiàn)在打開你的瀏覽器訪問 localhost:3000 ,按下按鈕并且打開你的瀏覽器控制臺(tái)。

在 btn.addEventListent('click',getData); 這行代碼里,getData通過AJAX發(fā)出一個(gè)GET請(qǐng)求,它使用了 $.ajax({properties}) 函數(shù)來設(shè)置 url , success 和 error 等參數(shù)。

在實(shí)際生產(chǎn)環(huán)境中,你要做的不僅僅是讀取JSON文件。你可能還想對(duì)數(shù)據(jù)進(jìn)行增刪改查等操作。express框架會(huì)將這些操作與特定的http動(dòng)詞綁定,比如POST、GET、PUT和DELETE等關(guān)鍵字。

要想深入了解使用express如何編寫API,你可以去閱讀 Chris Sevilleja 寫的 Build a RESTful API with Express 4 。

使用socket進(jìn)行網(wǎng)絡(luò)連接

計(jì)算機(jī)網(wǎng)絡(luò)是計(jì)算機(jī)之間分享接收數(shù)據(jù)的連接。要在NodeJS中進(jìn)行連網(wǎng)操作,我們需要引入 net 模塊。

const net = require('net');

在TCP中必須有兩個(gè)終端,一個(gè)終端與指定端口綁定,而另一個(gè)則需要訪問這個(gè)指定端口。

如果你還有疑惑,可以看看這個(gè)例子:

以你的手機(jī)為例,一旦你買了一張sim卡,你就和sim的電話號(hào)碼綁定。當(dāng)你的朋友想要打電話給你時(shí),他們必須撥打這個(gè)號(hào)碼。這樣你就相當(dāng)于一個(gè)TCP終端,而你的朋友是另一個(gè)終端。

現(xiàn)在你明白了吧?

為了更好地吸收這部分知識(shí),我們來寫一個(gè)程序,它能夠監(jiān)聽文件并且當(dāng)文件被更改后會(huì)通知連接到它的客戶端。

  1. 創(chuàng)建一個(gè)文件夾,命名為 node-network ;
  2. 創(chuàng)建3個(gè)文件: filewatcher.js 、 subject.txt 和 client.js 。把下面的代碼復(fù)制進(jìn) filewatcher.js 。

    //filewatcher.js
    
    const net = require('net'),
       fs = require('fs'),
       filename = process.argv[2],
          
    server = net.createServer((connection)=>{
     console.log('Subscriber connected');
     connection.write(`watching ${filename} for changes`);
      
    let watcher = fs.watch(filename,(err,data)=>{
      connection.write(`${filename} has changed`);
     });
      
    connection.on('close',()=>{
      console.log('Subscriber disconnected');
      watcher.close();
     });
      
    });
    server.listen(3000,()=>console.log('listening for subscribers'));
  3. 接下來我們提供一個(gè)被監(jiān)聽的文件,在 subject.txt 寫下下面一段話:

    Hello world, I'm gonna change
  4. 然后,新建一個(gè)客戶端。下面的代碼復(fù)制到 client.js 。

    const net = require('net');
    let client = net.connect({port:3000});
    client.on('data',(data)=>{
     console.log(data.toString());
    });
  5. 最后,我們還需要兩個(gè)終端。第一個(gè)終端里我們運(yùn)行 filename.js ,后面跟著我們要監(jiān)聽的文件名。

    //subject.txt會(huì)保存在filename變量中
    node filewatcher.js subject.txt
    //監(jiān)聽訂閱者

在另一個(gè)終端,也就是客戶端,我們運(yùn)行 client.js 。

node client.js

現(xiàn)在,修改 subject.txt ,然后看看客戶端的命令行,注意到多出了一條額外信息:

//subject.txt has changed.

網(wǎng)絡(luò)的一個(gè)主要的特征就是許多客戶端都可以同時(shí)接入這個(gè)網(wǎng)絡(luò)。打開另一個(gè)命令行窗口,輸入 node client.js 來啟動(dòng)另一個(gè)客戶端,然后再修改 subject.txt 文件?纯摧敵隽耸裁?

我們做了什么?

如果你沒有理解,不要擔(dān)心,讓我們重新過一遍。

我們的 filewatcher.js 做了三件事:

  1. net.createServer() 創(chuàng)建一個(gè)服務(wù)器并向許多客戶端發(fā)送信息。
  2. 通知服務(wù)器有客戶端連接,并且告知客戶端有一個(gè)文件被監(jiān)聽。
  3. 最后,使用wactherbianl監(jiān)聽文件,并且當(dāng)客戶端端口連接時(shí)關(guān)閉它。

    再來看一次 filewatcher.js 。

//filewatcher.js

const net = require('net'),
   fs = require('fs'),
   filename = process.argv[2],
      
server = net.createServer((connection)=>{
 console.log('Subscriber connected');
 connection.write(`watching ${filename} for changes`);
  
let watcher = fs.watch(filename,(err,data)=>{
  connection.write(`${filename} has changed`);
 });
  
connection.on('close',()=>{
  console.log('Subscriber disconnected');
  watcher.close();
 });
  
});
server.listen(3000,()=>console.log('listening for subscribers'));

我們引入兩個(gè)模塊:fs和net來讀寫文件和執(zhí)行網(wǎng)絡(luò)連接。你有注意到 process.argv[2] 嗎?process是一個(gè)全局變量,提供NodeJS代碼運(yùn)行的重要信息。 argv[] 是一個(gè)參數(shù)數(shù)組,當(dāng)我們獲取 argv[2] 時(shí),希望得到運(yùn)行代碼的第三個(gè)參數(shù)。還記得在命令行中,我們?cè)斎胛募鳛榈谌齻(gè)參數(shù)嗎?

node filewatcher.js subject.txt

此外,我們還看到一些非常熟悉的代碼,比如 net.createServer() ,這個(gè)函數(shù)會(huì)接收一個(gè)回調(diào)函數(shù),它在客戶端連接到端口時(shí)觸發(fā)。這個(gè)回調(diào)函數(shù)只接收一個(gè)用來與客戶端交互的對(duì)象參數(shù)。

connection.write() 方法向任何連接到3000端口的客戶端發(fā)送數(shù)據(jù)。這樣,我們的 connetion 對(duì)象開始工作,通知客戶端有一個(gè)文件正在被監(jiān)聽。

wactcher包含一個(gè)方法,它會(huì)在文件被修改后發(fā)送信息給客戶端。而且在客戶端斷開連接后,觸發(fā)了close事件,然后事件處理函數(shù)會(huì)向服務(wù)器發(fā)送信息讓它關(guān)閉watcher停止監(jiān)聽。

//client.js
const net = require('net'),
      client = net.connect({port:3000});
client.on('data',(data)=>{
  console.log(data.toString());
});

client.js 很簡(jiǎn)單,我們引入net模塊并且調(diào)用connect方法去訪問3000端口,然后監(jiān)聽每一個(gè)data事件并打印出數(shù)據(jù)。

當(dāng)我們的 filewatcher.js 每執(zhí)行一次 connection.write() ,我們的客戶端就會(huì)觸發(fā)一次data事件。

以上只是網(wǎng)絡(luò)如何工作的一點(diǎn)皮毛。主要就是一個(gè)端點(diǎn)廣播信息時(shí)會(huì)觸發(fā)所有連接到這個(gè)端點(diǎn)的客戶端上的data事件。

如果你想要了解更多Node的網(wǎng)絡(luò)知識(shí),可以看看官方NodeJS的文檔: net模塊 。你也許還需要閱讀 Building a Tcp service using Node 。

作者總結(jié)

好了,這就是我要講的全部。如果你想要使用NodeJS來編寫web應(yīng)用程序,你要知道的不僅僅是編寫一個(gè)服務(wù)器和使用express進(jìn)行路由。

下面是我推薦的一些書:

NodeJs the right way Web Development With Node and Express

如果你還有什么見解,可以在下面發(fā)表評(píng)論。

譯者注:這個(gè)翻譯項(xiàng)目才開始,以后會(huì)翻譯越來越多的作品。我會(huì)努力堅(jiān)持的。

項(xiàng)目地址: https://github.com/WhiteYin/translation

 

來自:https://segmentfault.com/a/1190000013241874

 

標(biāo)簽: Mysql 代碼 服務(wù)器 開發(fā)者 權(quán)限 數(shù)據(jù)庫(kù) 網(wǎng)絡(luò) 域名

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:微服務(wù)在2018年的5個(gè)發(fā)展趨勢(shì)

下一篇:國(guó)際象棋版AlphaZero出來了誒,還開源了Keras實(shí)現(xiàn)ヽ( `0′)ノ