摘要:原生應(yīng)用是一個(gè)基于引擎的運(yùn)行環(huán)境使用了一個(gè)事件驅(qū)動(dòng)非阻塞式的模型,使其輕量又高效的包管理器,是全球最大的開(kāi)源庫(kù)生態(tài)系統(tǒng)本文主要介紹構(gòu)建一個(gè)應(yīng)用的基本步驟和模塊,并假定你已經(jīng)對(duì)有一定的了解本文引用部分代碼作為例子,如果希望參看全部源碼,歡迎去
原生 Node.js 應(yīng)用
Node.js 是一個(gè)基于 Chrome V8 引擎的 JavaScript 運(yùn)行環(huán)境
Node.js 使用了一個(gè)事件驅(qū)動(dòng)、非阻塞式 I/O 的模型,使其輕量又高效
Node.js 的包管理器 npm,是全球最大的開(kāi)源庫(kù)生態(tài)系統(tǒng)
? 本文主要介紹構(gòu)建一個(gè) Node.js 應(yīng)用的基本步驟和模塊,并假定你已經(jīng)對(duì) Node.js Api 有一定的了解
? 本文引用部分代碼作為例子,如果希望參看全部源碼,歡迎去 github 查閱(如果覺(jué)得有一定幫助,歡迎star)
模塊架構(gòu)設(shè)計(jì)整個(gè) Node.js 應(yīng)用的架構(gòu)設(shè)計(jì)
node.js 應(yīng)用構(gòu)成引入模塊:通過(guò) require 指令來(lái)引入 Node.js 模塊
創(chuàng)建服務(wù)器:服務(wù)器用來(lái)監(jiān)聽(tīng)客戶端請(qǐng)求
接收請(qǐng)求和響應(yīng)請(qǐng)求:接收客戶端的HTTP請(qǐng)求,返回響應(yīng)數(shù)據(jù)
// 通過(guò) require 引入 http 模塊,并將實(shí)例化的 HTTP 賦值給 http 變量 const http = require("http"); // 引入 url 模塊,用來(lái)解析數(shù)據(jù) const url = require("url"); function ylone(router, handleObj) { const hostname = "127.0.0.1"; const port = 7777; // http.createServer(function(){}) 方法創(chuàng)建服務(wù)器,并返回一個(gè)對(duì)象 const server = http.createServer((req, res) => { const path = url.parse(req.url); const pathName = path.pathname; // 處理node.js每次自動(dòng)請(qǐng)求favicon.ico if (pathName !== "/favicon.ico") { const content = router(handleObj, pathName, res, req); } }); // server.listen() 方法綁定主機(jī)和端口 server.listen(port, hostname, () => { console.log(`服務(wù)運(yùn)行在${hostname}:${port}`); }); } exports.ylone = ylone;基于事件驅(qū)動(dòng)的回調(diào)
http.createServer((req, res) => {...}) 是一個(gè)典型的回調(diào),事實(shí)上,這就是 Node.js 原生的工作方式
這里直接將一個(gè)匿名函數(shù)作為變量進(jìn)行傳遞,繞開(kāi)了“先定義,再傳遞”的圈子
像寫(xiě) PHP 應(yīng)用時(shí):任何時(shí)候當(dāng)有請(qǐng)求進(jìn)入時(shí),服務(wù)器(如Apache)就會(huì)為這個(gè)請(qǐng)求創(chuàng)建一個(gè)新的進(jìn)程,并且從頭至尾執(zhí)行相應(yīng)的 PHP 腳本
在 Node.js 中,無(wú)論何時(shí)當(dāng)一個(gè)新的請(qǐng)求到達(dá)指定端口時(shí),我們?cè)诜?wù)器創(chuàng)建時(shí)傳遞的函數(shù)就會(huì)被調(diào)用
Node.js 模塊模塊意味著將 Node.js 應(yīng)用(如 http.js)抽象成一個(gè)模塊,通過(guò)入口文件 index.js 去調(diào)用相應(yīng)的模塊來(lái)引導(dǎo)和啟動(dòng)應(yīng)用
將代碼模塊化意味著我們將提供其功能的部分(如 一個(gè)函數(shù))導(dǎo)出到請(qǐng)求這個(gè)模塊的腳本內(nèi)
const server = require("./http"); const router = require("./route"); const handle = require("./requestHandle"); var handleObj = {}; // 入口 Case handleObj["/"] = handle.hello; // 非阻塞 Case handleObj["/vlone"] = handle.vlone; // post Case handleObj["/supreme"] = handle.supreme; // get Case handleObj["/adidas"] = handle.adidas; server.ylone(router.router, handleObj);路由
為了處理不同的 http 請(qǐng)求,我們需要通過(guò)創(chuàng)建一個(gè)路由模塊來(lái)進(jìn)行“路由選擇”
為路由提供請(qǐng)求的 url 和其他需要的 get 和 post 參數(shù),隨后路由根據(jù)這些數(shù)據(jù)來(lái)執(zhí)行相應(yīng)的代碼
我們所需要的數(shù)據(jù)都在 http.createServer((req, res) => {...}) 的 req 參數(shù)中,為了解析 req,需要額外引入 url 和 querystring Node.js 模塊
在 index.js 內(nèi)引入路由對(duì)象,將路由方法傳遞給 http 應(yīng)用,在 http.createServer((req, res) => {...}) 內(nèi)解析 req 參數(shù),然后調(diào)用 router 方法
理解以下函數(shù)式編程:將 router 對(duì)象傳遞給 index,在 index 內(nèi)將 router 方法傳遞給 http,因?yàn)?http 并不關(guān)心 router 方法從哪來(lái),只需要執(zhí)行方法,然后完成業(yè)務(wù),但是首先需要保證有這個(gè)對(duì)象
函數(shù)式編程最基本,最核心的即思想轉(zhuǎn)換,由名詞到動(dòng)詞,由對(duì)象到方法,行為驅(qū)動(dòng)執(zhí)行
function router(handleObj, pathName, res, req) { if (typeof handleObj[pathName] === "function") { return handleObj[pathName](res, req); } else { res.writeHead(200, { "Content-type": "text/plain" }); const content = "404 Not Found"; res.write(content); res.end(); } } exports.router = router;將路由分發(fā)到請(qǐng)求處理函數(shù)
需要?jiǎng)?chuàng)建一個(gè)新的 resquestHandlers 模塊,封裝各個(gè)處理函數(shù)來(lái)對(duì)應(yīng)不同的請(qǐng)求
在 JavaScript 中通過(guò)對(duì)象鍵值對(duì)來(lái)封裝 路徑->方法 的映射關(guān)系
在 C++ 或者 C# 中,對(duì)象指的是類或者結(jié)構(gòu)體的實(shí)例,對(duì)象根據(jù)其實(shí)例化的模板會(huì)擁有不同的屬性和方法
在 JavaScript 中,對(duì)象是一個(gè)鍵值對(duì)集合
在入口文件(index.js)內(nèi)引入 requestHandle,同時(shí)聲明一個(gè)操作對(duì)象(handleObj),用來(lái)存儲(chǔ) 路徑->方法 的映射關(guān)系,最后將路由方法和操作對(duì)象傳遞給服務(wù)器應(yīng)用(http.js)
在服務(wù)器應(yīng)用內(nèi),獲得瀏覽器請(qǐng)求的路徑,調(diào)用路由方法(router),將操作對(duì)象(handleObj)和請(qǐng)求路徑作為參數(shù)傳遞
在路由內(nèi)(route.js)獲取路徑對(duì)應(yīng)的函數(shù),自執(zhí)行函數(shù)
因?yàn)槲恼缕?,這里只展示關(guān)鍵代碼,源碼參看 github
const { exec } = require("child_process"); const querystring = require("querystring"); const url = require("url"); function createHttp(type, res, val) { const content = val; const conType = { plain: "text/plain;charset=utf-8", html: "text/html", }; // 為隱式的響應(yīng)頭設(shè)置值 res.writeHead(200, { "Content-type": conType[type] }); // 發(fā)送響應(yīng)主體 res.write(content); // http 完成響應(yīng) res.end(); } ... something else function vlone(res) { exec("node --version", (error, stdout, stderr) => { if (error) { console.log(error, stdout, stderr); return; } const content = stdout; const type = "plain"; createHttp(type, res, content); }); } ... something else阻塞與非阻塞
A() 方法讀取文件,因此需要一定的響應(yīng)時(shí)間,B() 方法代表其他需要執(zhí)行的代碼
阻塞:在A() 執(zhí)行的過(guò)程中,B() 處于等待狀態(tài),當(dāng)A() 訪問(wèn)文件數(shù)據(jù)準(zhǔn)備就緒后,B() 才開(kāi)始執(zhí)行
由上圖可以看出,應(yīng)用程序從進(jìn)行系統(tǒng)調(diào)用到復(fù)制數(shù)據(jù)報(bào)到應(yīng)用進(jìn)程緩沖區(qū)的整段過(guò)程是阻塞的,直到數(shù)據(jù)報(bào)被復(fù)制到用戶空間完成后,用戶進(jìn)程才解除阻塞狀態(tài),繼續(xù)執(zhí)行下一個(gè)應(yīng)用程序
優(yōu)點(diǎn):能夠及時(shí)返回?cái)?shù)據(jù),無(wú)延遲,方便調(diào)試
缺點(diǎn):需要等待
非阻塞:在A() 執(zhí)行的過(guò)程中,B() 同時(shí)執(zhí)行,且當(dāng)A() 訪問(wèn)文件數(shù)據(jù)準(zhǔn)備就緒后,A() 會(huì)被執(zhí)行完成
由上圖可以看出,應(yīng)用程序在調(diào)用過(guò)程中,如果數(shù)據(jù)報(bào)還沒(méi)有準(zhǔn)備就緒,會(huì)先返回一個(gè)錯(cuò)誤信息(EWOULDBLOCK),此時(shí)當(dāng)前進(jìn)程可以執(zhí)行其他方法,而不會(huì)阻塞。而 A() 會(huì)輪詢內(nèi)核,返回緩沖區(qū)數(shù)據(jù)是否準(zhǔn)備就緒
優(yōu)點(diǎn):不需要等待,當(dāng)前線程可以處理多個(gè)任務(wù)
缺點(diǎn):增大了任務(wù)完成的響應(yīng)延遲,因?yàn)槿蝿?wù)可能在兩次輪詢間隔內(nèi)完成,從而導(dǎo)致整體數(shù)據(jù)的吞吐量降低
以非阻塞方式進(jìn)行請(qǐng)求響應(yīng)當(dāng)前的應(yīng)用交互方式:(請(qǐng)求處理程序 -> 請(qǐng)求路由 -> 服務(wù)器)將請(qǐng)求處理程序返回的內(nèi)容(請(qǐng)求處理程序最終要顯示給用戶的內(nèi)容)傳遞給HTTP服務(wù)器
當(dāng)前這種交互方式的問(wèn)題在于,如果請(qǐng)求處理程序中有 Node.js 封裝的非阻塞方法A(),那么A() 在阻塞過(guò)程中,服務(wù)器就已經(jīng)將數(shù)據(jù)返回了,并不會(huì)等到A() 執(zhí)行完畢
為了解決上述問(wèn)題,以非阻塞方式進(jìn)行請(qǐng)求響應(yīng),相對(duì)于之前將數(shù)據(jù)傳遞給服務(wù)器的方式,現(xiàn)在我們需要將服務(wù)器(response對(duì)象)傳遞給生成數(shù)據(jù)的應(yīng)用內(nèi),待數(shù)據(jù)準(zhǔn)備完畢,再返回響應(yīng)數(shù)據(jù)
這樣可以同時(shí)請(qǐng)求兩個(gè)路徑(實(shí)際上就是觸發(fā)兩個(gè)函數(shù)方法),B() 并不會(huì)因?yàn)锳()執(zhí)行時(shí)間長(zhǎng)而處于等待狀態(tài)
處理 post 請(qǐng)求創(chuàng)建一個(gè)表單元素,設(shè)置表單提交方法為 post, 每當(dāng)用戶提交表單時(shí),則觸發(fā) supreme() 方法
處理 post 請(qǐng)求一般采用異步非阻塞方式,因?yàn)?post 請(qǐng)求一般會(huì)比較重,你無(wú)法控制用戶輸入的數(shù)據(jù)量,如果用阻塞的方式處理則必然會(huì)導(dǎo)致用戶操作阻塞
為了實(shí)現(xiàn)非阻塞,Node.js 會(huì)將 post 數(shù)據(jù)拆分成數(shù)據(jù)塊,然后通過(guò)出發(fā)特定事件,將這些數(shù)據(jù)塊傳遞給回調(diào)函數(shù)
常用的 post 兩個(gè)事件:data事件(新的數(shù)據(jù)塊到達(dá)時(shí)觸發(fā)),end事件(所有數(shù)據(jù)都已經(jīng)接收完畢時(shí)觸發(fā))
通過(guò)在 request 對(duì)象上注冊(cè)監(jiān)聽(tīng)器(listener)來(lái)告訴應(yīng)用當(dāng) post 事件觸發(fā)時(shí),應(yīng)該觸發(fā)哪些回調(diào)函數(shù)
處理 get 請(qǐng)求通過(guò) Node.js 封裝的 url對(duì)象來(lái)解析 url 參數(shù),獲取關(guān)鍵數(shù)據(jù)
url.parse() 的第二參數(shù) parseQueryString 如果為 true,則 query 屬性總是會(huì)通過(guò) querystring 模塊的 parse() 方法生成一個(gè)對(duì)象
some pieces當(dāng)寫(xiě)好 Node.js 腳本(如 ylone.js)后,通過(guò) node ylone.js 命令執(zhí)行腳本
在瀏覽器訪問(wèn)指定地址(如 http://localhost:7777/)意味著向服務(wù)器發(fā)出請(qǐng)求,從而觸發(fā)服務(wù)器創(chuàng)建時(shí)的回調(diào)函數(shù)
當(dāng)訪問(wèn)網(wǎng)頁(yè)(如 http://localhost:7777/)時(shí),控制臺(tái)可能會(huì)輸出兩次 req 的數(shù)據(jù),那是因?yàn)榇蟛糠譃g覽器會(huì)在訪問(wèn)網(wǎng)頁(yè)時(shí)嘗試讀取 favicon.ico 文件
針對(duì)瀏覽器每次發(fā)送請(qǐng)求,都會(huì)默認(rèn)請(qǐng)求一次 /favicon.ico 的問(wèn)題,可以在 http 中對(duì)其進(jìn)行過(guò)濾,不執(zhí)行操作
如果希望在 Node.js 內(nèi)的傳遞一個(gè) html 片段,并渲染在瀏覽器上,需要將 res.writeHead(200, {"Content-type": "text/plain"}) 的 Content-type 設(shè)置為 text/html
Node.js 返回?cái)?shù)據(jù)(response)在瀏覽器展示亂碼,通過(guò)在 res.writeHead(200, {"Content-type": "text/plain;charset=utf-8"}) 加上 charset=utf-8 配置解決
--Respect Node.js--
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/93914.html
摘要:前端每周清單第期支付寶前端構(gòu)建工具發(fā)展用加快網(wǎng)頁(yè)響應(yīng)速度餓了么升級(jí)實(shí)踐前端前端每周清單前端每周清單專注前端領(lǐng)域內(nèi)容,分為新聞熱點(diǎn)開(kāi)發(fā)教程工程實(shí)踐深度閱讀開(kāi)源項(xiàng)目巔峰人生等欄目。 前端每周清單第 12 期:支付寶前端構(gòu)建工具發(fā)展、LinkedIn用Brotli加快網(wǎng)頁(yè)響應(yīng)速度、餓了么PWA 升級(jí)實(shí)踐 為InfoQ中文站特供稿件,首發(fā)地址為這里;如需轉(zhuǎn)載,請(qǐng)與InfoQ中文站聯(lián)系。從屬于筆...
摘要:在考慮宇航員的生命安全時(shí),輕微的打嗝或者服務(wù)中斷都會(huì)釀成生死事故。也許最大的挑戰(zhàn)來(lái)自谷歌主導(dǎo)的簡(jiǎn)稱。在最近的開(kāi)發(fā)者峰會(huì),以及今年的會(huì)議上,谷歌都為安排了大量討論。由微軟提供,是廣受歡迎的編輯器,到月份已經(jīng)獲得了超過(guò)五百萬(wàn)用戶。 譯者:安冬 (滬江Web前端開(kāi)發(fā)工程師)本文原創(chuàng)翻譯,轉(zhuǎn)載請(qǐng)注明作者及出處。原文地址:http://developer.telerik.com/... 技術(shù)世界...
摘要:前端每周清單年度總結(jié)與盤點(diǎn)在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽藘杉拢ぷ髋c整理前端每周清單。本文末尾我會(huì)附上清單線索來(lái)源與目前共期清單的地址,感謝每一位閱讀鼓勵(lì)過(guò)的朋友,希望你們能夠繼續(xù)支持未來(lái)的每周清單。 showImg(https://segmentfault.com/img/remote/1460000010890043); 前端每周清單年度總結(jié)與盤點(diǎn) 在過(guò)去的八個(gè)月中,我?guī)缀踔蛔隽?..
摘要:感謝王下邀月熊分享的前端每周清單,為方便大家閱讀,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清單系列,并以年月為單位進(jìn)行分類,具體內(nèi)容看這里前端每周清單年度總結(jié)與盤點(diǎn)。 感謝 王下邀月熊_Chevalier 分享的前端每周清單,為方便大家閱讀,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清單系列,并以年/月為單位進(jìn)行分類,具...
閱讀 2674·2021-10-14 09:42
閱讀 1216·2021-09-22 15:09
閱讀 3629·2021-09-09 09:33
閱讀 3089·2021-09-07 09:59
閱讀 3732·2021-09-03 10:34
閱讀 3682·2021-07-26 22:01
閱讀 2883·2019-08-30 13:06
閱讀 1265·2019-08-30 10:48