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