首頁  >  文章  >  web前端  >  深入了解NodeJS

深入了解NodeJS

小云云
小云云原創
2018-02-10 16:15:012427瀏覽

如果你是前端開發工作者,那麼對你來說,基於NodeJS編寫web程式已經不是什麼新聞了。而不管是NodeJS還是web程式都非常依賴JavaScript這門語言。首先,我們要認清一點:Node並不是銀彈。也就是說,它不是所有專案的最佳解決方案。任何人都可以基於Node創建一個伺服器,但是這需要你對編寫web程式的語言具有一定程式的有很深入的理解。

Node.js出現之前

在Node.js出現之前,web應用程式往往是基於客戶端/伺服器模式,當客戶端向伺服器請求資源時,伺服器會回應這個請求並且傳回對應的資源。伺服器只會在接收到客戶端請求時才會回應,同時會在回應結束後關閉與客戶端的連線。

這種設計模式需要考慮到效率問題,因為每個請求都需要處理時間和資源。因此,伺服器在每一次處理請求的資源後應該關閉這個連接,以便於回應其他請求。

如果同時有成千上萬個請求同時發送到伺服器,伺服器會變成什麼樣子呢?當你問出這個問題時,你一定不想看到一個請求必須等待其他請求被回應後才能輪到他的情形,因為這段延遲實在是太長了。

想像一下,當你想要開啟FaceBook,但因為在你之前已經有上千人向伺服器發出過請求,所以你需要等待5分鐘才能看到內容。有沒有一種解決方案可以同時處理數百上千個請求呢?所幸我們有線程這個工具。

執行緒是系統能夠並行處理多任務所使用的方式。每一個發給伺服器的請求都會開啟一個新的線程,而每個線程會取得它運行程式碼所需的一切。

這聽起來很奇怪?讓我們來看看這個例子:

想像餐廳裡只有一個廚師提供食物,當食物需求越來越多,事情也會變得越來越糟。在之前的所有訂單都被處理之前,人們必須等待很長時間。而我們能想到的方法就是增加更多的服務生來解決這個問題,對吧?這樣能夠同時應付更多的顧客。

每個主題都是一個新的服務員,而顧客就是瀏覽器。我想理解這一點對你來說並不困難。

但是這種系統有一個副作用,讓請求數達到一定數量時,過多的執行緒會佔用所有系統記憶體和資源。重新回到我們的例子裡,僱用越來越多的人來供應食物必然會提高人力成本和佔用更多的廚房空間。

當然,如果伺服器在回應完客戶端的請求後立刻切斷連線並釋放所有資源,這對我們來說自然是極好的。

多執行緒系統擅長處理CPU密集型操作,因為這些操作需要處理大量的邏輯,而且計算這些邏輯會花費更多的時間。如果每個請求都會被一個新的線程處理,那麼主線程可以被解放出來去處理一些重要的計算,這樣也能讓整個系統變得更快。

讓主執行緒不必忙於所有的運算運算是一種提高效率的好方法,但是能不能在此之上更進一步呢?

NodeJS來了

想像一下我們現在已經有了一個多執行緒伺服器,運行於Ruby on rails環境。我們需要它讀取文件並且發送給請求這個文件的瀏覽器。首先要知道的是Ruby並不會直接讀取文件,而是通知檔案系統去讀取指定文件並回傳它內容。顧名思義,檔案系統就是電腦上一個專門用來存取檔案的程式。

Ruby在向檔案系統發出通知後會一直等待它完成讀取檔案的操作,而不是轉頭去處理其他任務。當檔案系統處理任務完成後,Ruby才會重新啟動去收集文件內容並且傳送給瀏覽器。

這種方式很顯然會造成阻塞的情況,而NodeJS的誕生就是為了解決這個痛點。如果我們使用Node來向檔案系統發出通知,在檔案系統去讀取檔案的這段時間裡,Node會去處理其他要求。而讀取檔案的任務完成後,檔案系統會通知Node去讀取資源然後將它傳回瀏覽器。事實上,這裡的內部實作都是依賴Node的事件循環。

Node的核心就是JavaScript和事件循環。

深入了解NodeJS

簡單地說,事件循環就是一個等待事件然後在需要事件發生時去觸發它們的程式。另外還有一點很重要,就是Node和JavaScript一樣都是單執行緒的。

還記得我們舉過的餐廳範例嗎?不管顧客數量有多少,Node開的餐廳裡永遠只有一個廚師烹調食物。

與其他語言不同,NodeJS不需要為每個請求開啟一個新的線程,它會接收所有請求,然後將大部分任務委託給其他的系統。 <span style="font-size: 14px;">Libuv</span>就是一個依賴OS核心去高效處理這些任務的函式庫。當這些隱藏於幕後的工作者處理完委託給它們的事件後,它們會觸發綁定在這些事件上的回呼函數去通知NodeJS。

這兒我們接觸到了回呼這個概念。回調理解起來並不困難,它是被其他函數當作參數傳遞的函數,並且在某種特定情況下會被呼叫。

NodeJS開發者們做的最多的就是寫事件處理函數,而這些處理函數會在特定的NodeJS事件發生後被呼叫。

NodeJS雖然是單線程,但它比多線程系統快得多。這是因為程式往往不是只有耗時巨長的數學運算和邏輯處理,大部分時間裡它們只是寫入檔案、處理網路請求或是向控制台和外部設備申請權限。這些都是NodeJS擅長處理的問題:當NodeJS在處理這些事情時,它會迅速地將這些事件委託給專門的系統,轉而去處理下一個事件。

如果你繼續深入下去,你也許會意識到NodeJS並不擅長處理消耗CPU的操作。因為CPU密集型操作會佔用大量的主執行緒資源。對於單執行緒系統來說,最理想的情況就是避免這些操作來釋放主執行緒去處理別的事情。

還有一個關鍵點是在JavaScript中,只有你寫的程式碼不是並發執行的。也就是說,你的程式碼每次只能處理一件事,而其他工作者,例如檔案系統可以並行處理它們手邊的工作。

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

很久以前有一個國王,他有一千個官員。國王寫了一個任務清單讓官員去做,清單非常非常非常長。有一個宰相,根據清單將任務委託給其他所有官員。每完成一項任務他就會將結果報告給國王,之後國王又會給他另一份清單。因為在官員工作的時候,國王也在忙著寫其他清單。

這個例子要講的是即使有很多官員在並行處理任務,國王每次也只能做一件事。這裡,國王就是你的程式碼,而官員就是藏於NodeJS幕後的系統工作者。所以說,除了你的程式碼,每件事都是並行發生的。

好了,讓我們繼續這段NodeJS之旅吧。

用NodeJS寫一個web應用程式

#用NodeJS寫一個web應用程式相當於寫事件回呼。讓我們來看看下面的範例:

  1. 新建並進入一個資料夾

  2. 執行<span style="font-size: 14px;">npm init</span>指令,一直回車直到你在資料夾根目錄下建立了一個package.json檔案。

  3. 新建一個名為server.js的文件,複製並貼上下面的程式碼:

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

    <span style="font-size: 14px;">node server.js<br>//Node server started at port 3000<br></span>

    開啟瀏覽器並且進入<span style="font-size: 14px;">localhost:3000</span>#,你應該可以看到一個<span style="font-size: 14px;">Hello world</span>資訊.

首先,我们引入了http模块。这个模块提供了处理htpp操作的接口,我们调用<span style="font-size: 14px;">createServer()</span>方法来创建一个服务器。

之后,我们为request事件绑定了一个事件回调,传递给on方法的第二个参数。这个回调函数有2个参数对象,request代表接收到的请求,response代表响应的数据。

不仅仅是处理request事件,我们也可以让Node去做其他事情。

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

在当面的代码里,我们传给createServer()一个回调函数,Node把它绑定在request事件上。这样我们只需要关心request和response对象了。

我们使用<span style="font-size: 14px;">response.writeHead()</span>来设置返回报文头部字段,比如状态码和内容类型。而<span style="font-size: 14px;">response.write()</span>是对web页面进行写入操作。最后使用<span style="font-size: 14px;">response.end()</span>来结束这个响应。

最后,我们告知服务器去监听3000端口,这样我们可以在本地开发时查看我们web应用的一个demo。listen这个方法要求第二个参数是一个回调函数,服务器一启动,这个回调函数就会被执行。

习惯回调

Node是一个单线程事件驱动的运行环境,也就是说,在Node里,任何事都是对事件的响应。

前文的例子可以改写成下面这样:

<span style="font-size: 14px;">//server.js<br>const http = require('http'),<br>      <br>makeServer = function (request,response){<br>   response.writeHead(200,{'Content-Type':'text/plain'});<br>   response.write('Hello world');<br>   response.end();<br>},<br>      <br>server = http.createServer(makeServer);<br><br>server.listen(3000,()=>{<br>  console.log('Node server created at port 3000');<br></span>

<span style="font-size: 14px;">makeServer</span>是一个回调函数,由于JavaScript把函数当作一等公民,所以他们可以被传给任何变量或是函数。如果你还不了解JavaScript,你应该花点时间去了解什么是事件驱动程序。

当你开始编写一些重要的JavaScript代码时,你可能会遇到“回调地狱”。你的代码变得难以阅读因为大量的函数交织在一起,错综复杂。这时你想要找到一种更先进、有效的方法来取代回调。看看Promise吧,Eric Elliott 写了一篇文章来讲解什么是Promise,这是一个好的入门教程。

NodeJS路由

一个服务器会存储大量的文件。当浏览器发送请求时,会告知服务器他们需要的文件,而服务器会将相应的文件返回给客户端。这就叫做路由。

在NodeJS中,我们需要手动定义自己的路由。这并不麻烦,看看下面这个基本的例子:

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

粘贴这段代码,输入<span style="font-size: 14px;">node server.js</span>命令来运行。在浏览器中打开<span style="font-size: 14px;">localhost:3000</span><span style="font-size: 14px;">localhost:3000/abou</span>,然后在试试打开<span style="font-size: 14px;">localhost:3000/somethingelse</span>,是不是跳转到了我们的错误页面?

虽然这样满足了我们启动服务器的基本要求,但是要为服务器上每一个网页都写一遍代码实在是太疯狂了。事实上没有人会这么做,这个例子只是让你了解路由是怎么工作的。

如果你有注意到,我们引入了url这个模块,它能让我们处理url更加方便。

为parse()方法传入一个url字符串参数,这个方法会将url拆分成<span style="font-size: 14px;">protocol</span><span style="font-size: 14px;">host</span><span style="font-size: 14px;">path</span><span style="font-size: 14px;">querystring</span>等部分。如果你不太了解这些单词,可以看看下面这张图:

深入了解NodeJS

所以当我们执行<span style="font-size: 14px;">url.parse(request.url).pathname</span>语句时,我们得到一个url路径名,或者是url本身。这些都是我们用来进行路由请求的必要条件。不过这件事还有个更简单的方法。

使用Express进行路由

如果你之前做过功课,你一定听说过Express。这是一个用来构建web应用以及API的NodeJS框架,它也可以用来编写NodeJS应用。接着往下看,你会明白为什么我说它让一切变得更简单。

在你的终端或是命令行中,进入电脑的根目录,输入<span style="font-size: 14px;">npm install express --save</span>来安装Express模块包。要在项目中使用Express,我们需要引入它。

<span style="font-size: 14px;">const express = require('express');<br></span>

欢呼吧,生活将变得更美好。

现在,让我们用express进行基本的路由。

<span style="font-size: 14px;">//server.js<br>const express = require('express'),<br>      server = express();<br><br>server.set('port', process.env.PORT || 3000);<br><br>//Basic routes<br>server.get('/', (request,response)=>{<br>   response.send('Home page');<br>});<br><br>server.get('/about',(request,response)=>{<br>   response.send('About page');<br>});<br><br>//Express error handling middleware<br>server.use((request,response)=>{<br>   response.type('text/plain');<br>   response.status(505);<br>   response.send('Error page');<br>});<br><br>//Binding to a port<br>server.listen(3000, ()=>{<br>  console.log('Express server started at port 3000');<br>});<br></span>
译者注:这里不是很理解为什么代码中错误状态码是505。

现在的代码是不是看上去更加清晰了?我相信你很容易就能理解它。

首先,当我们引入express模块后,得到的是一个函数。调用这个函数后就可以开始启动我们的服务器了。

接下来,我们用<span style="font-size: 14px;">server.set()</span>来设置监听端口。而<span style="font-size: 14px;">process.env.PORT</span>是程序运行时的环境所设置的。如果没有这个设置,我们默认它的值是3000.

然后,观察上面的代码,你会发现Express里的路由都遵循一个格式:

<span style="font-size: 14px;">server.VERB('route',callback);<br></span>

这里的VERB可以是GET、POST等动作,而pathname是跟在域名后的字符串。同时,callback是我们希望接收到一个请求后触发的函数。

最后我们再调用<span style="font-size: 14px;">server.listen()</span>,还记得它的作用吧?

以上就是Node程序里的路由,下面我们来挖掘一下Node如何调用数据库。

NodeJS里的数据库

很多人喜欢用JavaScript来做所有事。刚好有一些数据库满足这个需求,比如MongoDB、CouchDB等待。这些数据库都是NoSQL数据库。

一个NoSQL数据库以键值对的形式作为数据结构,它以文档为基础,数据都不以表格形式保存。

我们来可以看看MongoDB这个NoSQL数据库。如果你使用过MySQL、SQLserver等关系型数据库,你应该熟悉数据库、表格、行和列等概念。 MongoDB与他们相比并没有特别大的区别,不过还是来比较一下吧。

译者注:这儿应该有个表格显示MongoDB与MySQL的区别,但是原文里没有显示。

为了让数据更加有组织性,在向MongoDB插入数据之前,我们可以使用Mongoose来检查数据类型和为文档添加验证规则。它看上去就像Mongo与Node之间的中介人。

由于本文篇幅较长,为了保证每一节都尽可能的简短,请你先阅读官方的MongoDB安装教程。

此外,Chris Sevilleja写了一篇Easily Develop Node.js and MongoDB Apps with Mongoose,我认为这是一篇适合入门的基础教程。

使用Node和Express编写RESTful API

API是应用程序向别的程序发送数据的通道。你有没有登陆过某些需要你使用facebook账号登录的网页?facebook将某些函数公开给这些网站使用,这些就是API。

一个RESTful API应该不以服务器/客户端的状态改变而改变。通过使用一个REST接口,不同的客户端,即使它们的状态各不相同,但是在访问相同的REST终端时,应该做出同一种动作,并且接收到相同的数据。

API终端是API里返回数据的一个函数。

编写一个RESTful API涉及到使用JSON或是XML格式传输数据。让我们在NodeJS里试试吧。我们接下来会写一个API,它会在客户端通过AJAX发起请求后返回一个假的JSON数据。这不是一个理想的API,但是能帮助我们理解在Node环境中它是怎么工作的。

  1. 创建一个叫node-api的文件夹;

  2. 通过命令行进入这个文件夹,输入<span style="font-size: 14px;">npm init</span>。这会创建一个收集依赖的文件;

  3. 输入<span style="font-size: 14px;">npm install --save express</span>来安装express;

  4. 在根目录新建3个文件:<span style="font-size: 14px;">server.js</span><span style="font-size: 14px;">index.html</span><span style="font-size: 14px;">users.js</span>

  5. 复制下面的代码到相应的文件:

<span style="font-size: 14px;">//users.js<br>module.exports.users = [<br> {<br>  name: 'Mark',<br>  age : 19,<br>  occupation: 'Lawyer',<br>  married : true,<br>  children : ['John','Edson','ruby']<br> },<br>  <br> {<br>  name: 'Richard',<br>  age : 27,<br>  occupation: 'Pilot',<br>  married : false,<br>  children : ['Abel']<br> },<br>  <br> {<br>  name: 'Levine',<br>  age : 34,<br>  occupation: 'Singer',<br>  married : false,<br>  children : ['John','Promise']<br> },<br>  <br> {<br>  name: 'Endurance',<br>  age : 45,<br>  occupation: 'Business man',<br>  married : true,<br>  children : ['Mary']<br> },<br>]<br></span>

这是我们传给别的应用的数据,我们导出这份数据让所有程序都可以使用。也就是说,我们将users这个数组保存在<span style="font-size: 14px;">modules.exports</span>对象中。

<span style="font-size: 14px;">//server.js<br>const express = require('express'),<br>      server = express(),<br>      users = require('./users');<br><br>//setting the port.<br>server.set('port', process.env.PORT || 3000);<br><br>//Adding routes<br>server.get('/',(request,response)=>{<br> response.sendFile(__dirname + '/index.html');<br>});<br><br>server.get('/users',(request,response)=>{<br> response.json(users);<br>});<br><br>//Binding to localhost://3000<br>server.listen(3000,()=>{<br> console.log('Express server started at port 3000');<br>});<br></span>

我们执行<span style="font-size: 14px;">require('express')</span>语句然后使用<span style="font-size: 14px;">express()</span>创建了一个服务变量。如果你仔细看,你还会发现我们引入了别的东西,那就是<span style="font-size: 14px;">users.js</span>。还记得我们把数据放在哪了吗?要想程序工作,它是必不可少的。

express有许多方法帮助我们给浏览器传输特定类型的内容。<span style="font-size: 14px;">response.sendFile()</span>会查找文件并且发送给服务器。我们使用<span style="font-size: 14px;">__dirname</span>来获取服务器运行的根目录路径,然后我们把字符串<span style="font-size: 14px;">index.js</span>加在路径后面保证我们能够定位到正确的文件。

<span style="font-size: 14px;">response.json()</span>向网页发送JSON格式内容。我们把要分享的users数组传给它当参数。剩下的代码我想你在之前的文章中已经很熟悉了。

<span style="font-size: 14px;">//index.html<br><br><!DOCTYPE html><br><html><br><head><br> <meta charset="utf-8"><br> <title>Home page</title><br></head><br><body><br> <button>Get data</button><br><script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script><br> <br>  <script type="text/javascript"><br>  <br>    const btn = document.querySelector('button');<br>    btn.addEventListener('click',getData);<br>    function getData(e){<br>        $.ajax({<br>        url : '/users',<br>        method : 'GET',<br>        success : function(data){<br>           console.log(data);<br>        },<br>      <br>        error: function(err){<br>          console.log('Failed');<br>        }<br>   });<br>  } <br> </script><br></body><br></html><br></span>

在文件夹根目录中执行<span style="font-size: 14px;">node server.js</span>,现在打开你的浏览器访问<span style="font-size: 14px;">localhost:3000</span>,按下按钮并且打开你的浏览器控制台。

深入了解NodeJS

<span style="font-size: 14px;">btn.addEventListent('click',getData);</span>这行代码里,getData通过AJAX发出一个GET请求,它使用了<span style="font-size: 14px;">$.ajax({properties})</span>函数来设置<span style="font-size: 14px;">url</span><span style="font-size: 14px;">success</span><span style="font-size: 14px;">error</span>等参数。

在实际生产环境中,你要做的不仅仅是读取JSON文件。你可能还想对数据进行增删改查等操作。express框架会将这些操作与特定的http动词绑定,比如POST、GET、PUT和DELETE等关键字。

要想深入了解使用express如何编写API,你可以去阅读Chris Sevilleja写的Build a RESTful API with Express 4。

使用socket进行网络连接

计算机网络是计算机之间分享接收数据的连接。要在NodeJS中进行连网操作,我们需要引入<span style="font-size: 14px;">net</span>模块。

<span style="font-size: 14px;">const net = require('net');<br></span>

在TCP中必须有两个终端,一个终端与指定端口绑定,而另一个则需要访问这个指定端口。

如果你还有疑惑,可以看看这个例子:

以你的手机为例,一旦你买了一张sim卡,你就和sim的电话号码绑定。当你的朋友想要打电话给你时,他们必须拨打这个号码。这样你就相当于一个TCP终端,而你的朋友是另一个终端。

现在你明白了吧?

为了更好地吸收这部分知识,我们来写一个程序,它能够监听文件并且当文件被更改后会通知连接到它的客户端。

  1. 创建一个文件夹,命名为<span style="font-size: 14px;">node-network</span>

  2. 创建3个文件:<span style="font-size: 14px;">filewatcher.js</span><span style="font-size: 14px;">subject.txt</span><span style="font-size: 14px;">client.js</span>。把下面的代码复制进<span style="font-size: 14px;">filewatcher.js</span>

    <span style="font-size: 14px;">//filewatcher.js<br><br>const net = require('net'),<br>   fs = require('fs'),<br>   filename = process.argv[2],<br>      <br>server = net.createServer((connection)=>{<br> console.log('Subscriber connected');<br> connection.write(`watching ${filename} for changes`);<br>  <br>let watcher = fs.watch(filename,(err,data)=>{<br>  connection.write(`${filename} has changed`);<br> });<br>  <br>connection.on('close',()=>{<br>  console.log('Subscriber disconnected');<br>  watcher.close();<br> });<br>  <br>});<br>server.listen(3000,()=>console.log('listening for subscribers'));<br></span>
  3. 接下来我们提供一个被监听的文件,在<span style="font-size: 14px;">subject.txt</span>写下下面一段话:

    <span style="font-size: 14px;">Hello world, I'm gonna change<br></span>
  4. 然后,新建一个客户端。下面的代码复制到<span style="font-size: 14px;">client.js</span>

    <span style="font-size: 14px;">const net = require('net');<br>let client = net.connect({port:3000});<br>client.on('data',(data)=>{<br> console.log(data.toString());<br>});<br></span>
  5. 最后,我们还需要两个终端。第一个终端里我们运行<span style="font-size: 14px;">filename.js</span>,后面跟着我们要监听的文件名。

    <span style="font-size: 14px;">//subject.txt会保存在filename变量中<br>node filewatcher.js subject.txt<br>//监听订阅者<br></span>

在另一个终端,也就是客户端,我们运行<span style="font-size: 14px;">client.js</span>

<span style="font-size: 14px;">node client.js<br></span>

现在,修改<span style="font-size: 14px;">subject.txt</span>,然后看看客户端的命令行,注意到多出了一条额外信息:

<span style="font-size: 14px;">//subject.txt has changed.<br></span>

网络的一个主要的特征就是许多客户端都可以同时接入这个网络。打开另一个命令行窗口,输入<span style="font-size: 14px;">node client.js</span>来启动另一个客户端,然后再修改<span style="font-size: 14px;">subject.txt</span>文件。看看输出了什么?

我们做了什么?

如果你没有理解,不要担心,让我们重新过一遍。

我们的<span style="font-size: 14px;">filewatcher.js</span>做了三件事:

  1. <span style="font-size: 14px;">net.createServer()</span>创建一个服务器并向许多客户端发送信息。

  2. 通知服务器有客户端连接,并且告知客户端有一个文件被监听。

  3. 最后,使用wactherbianl监听文件,并且当客户端端口连接时关闭它。

    再来看一次<span style="font-size: 14px;">filewatcher.js</span>

<span style="font-size: 14px;">//filewatcher.js<br><br>const net = require('net'),<br>   fs = require('fs'),<br>   filename = process.argv[2],<br>      <br>server = net.createServer((connection)=>{<br> console.log('Subscriber connected');<br> connection.write(`watching ${filename} for changes`);<br>  <br>let watcher = fs.watch(filename,(err,data)=>{<br>  connection.write(`${filename} has changed`);<br> });<br>  <br>connection.on('close',()=>{<br>  console.log('Subscriber disconnected');<br>  watcher.close();<br> });<br>  <br>});<br>server.listen(3000,()=>console.log('listening for subscribers'));<br></span>

我们引入两个模块:fs和net来读写文件和执行网络连接。你有注意到<span style="font-size: 14px;">process.argv[2]</span>吗?process是一个全局变量,提供NodeJS代码运行的重要信息。<span style="font-size: 14px;">argv[]</span>是一个参数数组,当我们获取<span style="font-size: 14px;">argv[2]</span>时,希望得到运行代码的第三个参数。还记得在命令行中,我们曾输入文件名作为第三个参数吗?

<span style="font-size: 14px;">node filewatcher.js subject.txt<br></span>

此外,我们还看到一些非常熟悉的代码,比如<span style="font-size: 14px;">net.createServer()</span>,这个函数会接收一个回调函数,它在客户端连接到端口时触发。这个回调函数只接收一个用来与客户端交互的对象参数。

<span style="font-size: 14px;">connection.write()</span>方法向任何连接到3000端口的客户端发送数据。这样,我们的<span style="font-size: 14px;">connetion</span>对象开始工作,通知客户端有一个文件正在被监听。

wactcher包含一个方法,它会在文件被修改后发送信息给客户端。而且在客户端断开连接后,触发了close事件,然后事件处理函数会向服务器发送信息让它关闭watcher停止监听。

<span style="font-size: 14px;">//client.js<br>const net = require('net'),<br>      client = net.connect({port:3000});<br>client.on('data',(data)=>{<br>  console.log(data.toString());<br>});<br></span>

<span style="font-size: 14px;">client.js</span>很简单,我们引入net模块并且调用connect方法去访问3000端口,然后监听每一个data事件并打印出数据。

当我们的<span style="font-size: 14px;">filewatcher.js</span>每执行一次<span style="font-size: 14px;">connection.write()</span>,我们的客户端就会触发一次data事件。

以上只是网络如何工作的一点皮毛。主要就是一个端点广播信息时会触发所有连接到这个端点的客户端上的data事件。

如果你想要了解更多Node的网络知识,可以看看官方NodeJS的文档:net模块。你也许还需要阅读Building a Tcp service using Node。

相关推荐:

Nodejs调用WebService的详解

NodeJs数据库异常处理解析

nodejs基础知识

以上是深入了解NodeJS的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn