[學習之路] Node.js 入門教學

文章推薦指數: 80 %
投票人數:10人

注意每個node 版本都是獨立的npm 套件不共用。

USB 免安裝. 因為工作需要不同電腦上執行,因此我習慣用USB 作免安裝環境。

因此VScode、GIT、Nodejs ... Node.js是能夠在伺服器上面運行JavaScript的應用平台環境,透過Node.js提供的函式庫與執行環境能完成伺服器端服務,提供各種網路應用。

本篇資源版本與環境如下: 主題教材: Node.js官方網站版本v14.15.3 NVM版本v0.37.2 NVM-windows版本v1.1.7 IDE編譯器:VScodev1.52.1 作業系統:Windows10NT 認識 Node.js透過安裝到指定伺服器主機內,使用JavaScrpt進行語法撰寫。

常用於即時與推播等網路應用,像是WebServer、聊天機器人、資料存取服務等網路應用。

早期是採用C與Lua程式語言開發但並不順利,直到GoogleV8JavaScript引擎出現才成功,透過該引擎執行JavaScript代碼。

Node.js本身是單執行緒,因此採用Non-blockingI/O與非同步事件來規劃程式運作,這能讓大量使用者連線情況下不會因為等待IO等待回應而占用執行緒。

Node.js提供多個模組的API函式庫,包含一些非同步的IO檔案存取、Socket、HTTP等函式庫,所以Node.js自己就能創造Web伺服器,不需依賴IIS或Apache伺服器。

用戶端的JS全域物件為window物件,Node.js的JS全域物件為Global物件。

Node.js能在全域下使用console物件與setTimeout/setInterval函式,用法都跟一般的JS相同。

安裝Node.js作為伺服器應用平台,你需要提供一台主機作為伺服器運作。

本教學使用Windows為伺服器環境,並提供三種安裝Node.js方式教學: 官網下載前往官網提供穩定版(LTS)與最新版(Current)可選擇,選擇穩定版即可。

安裝方式大多簡單快速通過即可,有幾個過程項目需注意: 目前版本預設情況下會自動幫你安裝npm管理與設定PATH所需路徑。

所以不用擔心需要額外安裝或設定什麼 會提供一個Automaticyinstall的選項自動安裝工具請打勾,這能幫助你未來遇到一些npm模組可提供修改時呼叫Python與VScode來連動修改。

若未安裝時未來會出現錯誤訊息(ex:gypERR!findPython)。

若有打勾將會主程式安裝完畢後呼叫cmd進行此項目工具安裝。

安裝完畢後執行cmd或powershell,輸入node-v或npm-v能得到版本資訊代表安裝成功。

NVM工具全名為NodeVersionManager,為Caswell作者開發出來的強大管理工具,主要是由於工作上處理專案時需要不同的Node版本來執行(因有些npm模組有版本相容性問題)。

大多數的開發者都會選擇NVM來作為node安裝來源。

根據手冊說明安裝十分簡單,開啟cmd輸入以下指令(擇一)即可,但實際上僅適用於Mac/Linux應用 curl-o-https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh|bashwget-qO-https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.2/install.sh|bash 我們環境是windows無法使用,所以需要找支援windows的另一家NVM工具,這裡提供nvs來使用。

(另外常用的還有nvm-windows選擇)讓我們下載nvm-setup.zip使用。

安裝過程也十分簡單沒什麼選項,之後開啟cmd或powershell輸入nvs能列出版本選擇初次安裝。

之後再次輸入nvs能選擇使用node版本或另外安裝。

指令簡單明瞭。

C:\Users\Loki>nvsDownloading[###########################################################################################]100%Extracting[###########################################################################################]100%PATH+=$env:LOCALAPPDATA\nvs\node\14.15.3\x64C:\Users\Loki>node-vv14.15.3C:\Users\Loki>npm-v6.14.9 如此一來你就獲得node與npm的安裝,如果工作需要可多安裝幾個版本。

需要切換node版本時再透過指令nvs做畫面選擇即可。

注意每個node版本都是獨立的npm套件不共用。

USB免安裝因為工作需要不同電腦上執行,因此我習慣用USB作免安裝環境。

因此VScode、GIT、Nodejs都能使用免安裝。

同時依賴VSCode的同步功能自動把GIT跟Nodejs相關檔案都一起同步化。

這裡將會順便說明GIT如何加入到免安裝: VSCode的免安裝使用安裝不特別說明,本站舊文或網路上曾提到可自行參考。

另外根據官方Portable的資料保留方式,之後也會把Git與Node的檔案都放入到(VSCodefolder)\data\user-data內,這樣方便VSCode進行SettingSync時一同備份保留。

GIT的免安裝到GIT網站下載頁面,點選對應的windows作業系統在觸發下載(自動下載請直接取消)的頁面上找到64-bitGitforWindowsPortable。

下載解壓到USB位置的VSCode路徑(VSCodefolder)\data\user-data\User\Lokitools\Git。

Node的免安裝(兩種方式) 方法一:官方的Node到Nodejs網站下載頁面,點選對應的windows找到WindowsBinary(.zip)連結,下載解壓到USB位置的VSCode路徑(VSCodefolder)\data\user-data\User\lokiTools\node 方法二:NVS管理器(本篇使用方式為此)到NVS的release處下載頁面,點選SourceCode版本連結,下載解壓到USB位置的VSCode路徑(VSCodefolder)\data\user-data\User\lokiTools\nvs 移植舊npm模組如果已經有Node有相關npm模組,記得搬移進去到你的node\node_modules內,node根目錄的一些模組主程式也要(舉例hexo,hexo.cmd,hexo.ps1也對應到新位置根目錄)。

讓VScode終端機自動執行GIT與Node大致有三個重點要處理: 讓VSCode接管你的GIT執行程式。

讓VSCode終端機是使用windowscmd為執行環境。

讓node,npm,git的windowsPATH路徑於每次操作終端機之前寫入到該電腦的PATH環境內,使不同電腦上都能正常運作。

開始設計步驟如下: 目前所有已持有的檔案都在(VSCodefolder)\data\user-data\User\lokiTools\內,裡面有Git,node(或nvs)資料夾。

在該資料夾底下建立autoRunOnVSCode.bat批次檔案,讓VSCode在使用終端機時自動跑這個前置作業。

批次檔案主要處理宣告PATH(Git與node路徑)寫入到目前電腦的PATH環境內,以及顯示相關版本資訊。

這裡提供兩種版本,分別對應前面方法一(官方node)與方法二(nvs)的寫法,擇一即可。

autoRunOnVSCode.batbyNodejs@echooff::::::::::::::::::::::::::::::::::::::::::PutintoWindowsPATH::GITsetgitdir=%~dp0\Git\cmd::Method1:nodePATHsetnodedir=%~dp0\nodesetpath=%PATH%;%gitdir%;%nodedir%::::::::::::::::::::::::::::::::::::::::::PrintMessage::FigureoutversionsforGit,Node.Js,andNPM.ThisfirstonebreaksaparttheGitversiontomakeitlooknicer.for/f"tokens=3-6delims=."%%ain('git--version')do(setgitver1=%%a&setgitver2=%%b&setgitver3=%%c)echoGitVersion=v%gitver1%.%gitver2%.%gitver3%for/f"tokens=1"%%vin('node-v')dosetnodever=%%vechoNodeVersion=%nodever%for/f"tokens=1"%%nin('npm-v')dosetnpmver=%%nechoNPMVersion=v%npmver% autoRunOnVSCode.batbynvs@echooff::::::::::::::::::::::::::::::::::::::::::PutintoWindowsPATH::GITsetgitdir=%~dp0\Git\cmd::method2:nvs(nodeversionswitcher)PATHsetnvsdir=%~dp0\nvssetpath=%PATH%;%gitdir%;%nvsdir%call%~dp0\nvs\nvs.cmd::::::::::::::::::::::::::::::::::::::::::PrintMessageecho-------------------------for/f"tokens=1"%%nin('nvs-v')dosetnvsver=%%nechoNVSVersion=v%nvsver%echo-------------------------::FigureoutversionsforGit,Node.Js,andNPM.ThisfirstonebreaksaparttheGitversiontomakeitlooknicer.for/f"tokens=3-6delims=."%%ain('git--version')do(setgitver1=%%a&setgitver2=%%b&setgitver3=%%c)echoGitVersion=v%gitver1%.%gitver2%.%gitver3%for/f"tokens=1"%%vin('node-v')dosetnodever=%%vechoNodeVersion=%nodever%for/f"tokens=1"%%nin('npm-v')dosetnpmver=%%nechoNPMVersion=v%npmver% 最後是設定VSCode的settings.json,路徑設定走絕對路徑(舉例這裡USB固定為K槽),其三個重點: 設定git的執行擋在我們免安裝隨身碟之處 設定終端機採用windows內建的cmd命令字元 讓每次使用VSCode的終端機時會自動執行這個autoRunOnVSCode.bat批次檔settings.json//forcmdandgitpath"editor.renameOnType":true,"git.autofetch":true,"git.path":"K:\\VSCODE\\data\\user-data\\User\\lokiTools\\Git\\bin\\git.exe","terminal.integrated.shell.linux":"","terminal.integrated.shell.windows":"C:\\WINDOWS\\System32\\cmd.exe","terminal.integrated.shellArgs.windows":["/K","K:\\VSCODE\\data\\user-data\\User\\lokiTools\\autoRunOnVSCode.bat"], 目前為止已可成功使用免安裝隨身碟,只要保持USB磁碟機為固定代號就能使用。

入門操作Node操作原理是透過終端機指令node*.js去解讀JS語法。

如果有console時會直接由node回應給終端機輸出。

假設有個檔案為: helloworld.jsconsole.log("HelloWorld"); 透過node去執行這個檔案你會在終端機得到一個訊息。

L:\nodeTest>nodehelloworld.jsHelloWorldL:\nodeTest> 或者採用進入nodeREPL命令模式,在關鍵字node送出後,就能直接在終端機上輸入node指令: L:\nodeTest>nodeWelcometoNode.jsv14.15.3.Type".help"formoreinformation.>console.log("helloworld")helloworldundefined> 在REPL模式內妳可以使用以下快速鍵: ctrl+c:退出當前終端。

ctrl+c:單擊兩次-退出NodeREPL。

ctrl+d:退出節點REPL。

上下鍵:查看輸入的歷史命令 tab鍵:列出當前命令 .help:列出使用命令 .break:退出多行表達式 .clear:退出多行表達式 .savefilename:保存當前的NodeREPL模式到指定檔案 .loadfilename:加載當前節點REPL模式的檔案內容。

process物件內建的process物件能代表目前正執行代碼之流程運作,你可以控制當下process之事件、屬性、方法。

可使用的資源多可參考官方手冊做了解,這裡舉例兩個操作: exit中斷操作當代碼遇到process.exit()代表當前執行碼結束。

或使用event事件遇到exit行程結束時觸發指定動作。

console.log(1);process.on('exit',()=>{//當process遇到發生exit事件時做以下執行console.log('done');})console.log(2);process.exit();//直接exit結束console.log(3);//不會被執行到//print:12done nextTick指定下次執行由於Node.js採用事件迴圈方式來消化代碼,使得非同步與非阻擋式I/O能順利後續處理。

你也能指定將callback內容安排至下一次事件迴圈時第一個執行處。

console.log(1);process.nextTick(()=>{console.log("next");//此callback作業是下一次事件迴圈時觸發。

});console.log(2); 全域變數執行Node應用時,能透過__filename與__dirname全域變數來取得該應用程式所在之檔案名稱或目錄,對於需要找到相對路徑處理上有很大的幫助。

index.jsconsole.log(`dirname應用目錄${__dirname}filename檔案路徑${__filename}`); L:\nodeDemo>node.dirname應用目錄L:\nodeDemofilename檔案路徑L:\nodeDemo\index.js Moudles模組在設計Node應用時,由於是函式庫觀念,所有的可用函式都是使用模組系統觀念來運用,透過require以及exports方式提供檔案和模組之間的溝通。

大致上可以分為三種類型: ThirdPartyModules(第三方模組) LocalModules(自建模組) CoreModules(原生模組) ThirdPartyModules(第三方模組)第三方模組代表是別人寫好的自建模組(稍晚提到),直接將檔案放入本地端目錄下以相對路徑requare使用即可。

或者也可透過網路使用npm取得別人上傳的模組。

全名為NodePackageManager的npm是Node.js額外套件管理系統,安裝node主程式時就已安裝好。

透過npm能夠輕易安裝各家套件模組。

想知道有那些第三方模組可使用可以到網站npm|buildamazingthings找到,或者透過終端機指令npmsearch進行搜尋。

同樣的你也可以將自建模組放到npm上面去提供別人下載使用。

舉例npm搜尋Bootstrap套件::::::::::::::::搜尋可用的安裝模組L:\nodeTest>npmsearchbootstrapNAME|DESCRIPTION|AUTHOR|DATE|VERSION|KEYWORDSbootstrap|Themostpopular…|=xhmikosr=mdo…|2020-10-13|4.5.3|csssassmobile-firstresponsivefront-endframeworkwebless|LeanerCSS|=matthew-dean…|2020-12-18|4.0.0|compilelesscssnestingcssvariablecssgradientscssgradientscss3lesscompilerlesscsslessmixinslessless.jslesscssmixinsnestedbootstrap-vue|Withmorethan85…|=jackmu95=pi0…|2021-01-01|2.21.2|BootstrapBootstrapv4BootstrapforVueVueVue.jsVuev2SSRWebComponentsDirectivesIconsBootstrapIconsARIAAccessibilitya11yPoppe@ng-bootstrap/ng-bootstra|Angularpowered…|=pkozlowski_os…|2020-11-06|8.0.0|angularbootstrapcomponentsaccordionalertbuttonscarouselcollapsedatepickerdropdownmodalpaginationpopoverprogressbarratingtablep|||||react-bootstrap|Bootstrap4…|=monastic.panic…|2020-10-21|1.4.0|reactecosystem-reactreact-componentbootstrapngx-bootstrap|NativeAngular…|=valorkin|2020-11-06|6.2.0|angularbootstapngng2angular2twitter-bootstrapreactstrap|ReactBootstrap4…|=eddywashere…|2020-12-29|8.8.1|reactstrapbootstrapreactcomponentcomponentsreact-componentuibootstrap-slider|Sliderview…|=rovolutionary…|2020-06-04|11.0.2|sliderbootstraptwitterslideangular-ui-bootstrap|NativeAngularJS…|=icfantv…|2017-10-14|2.5.6|angularjsangularbootstrapuibootstrap-select|ThejQueryplugin…|=caseyjhol|2020-06-26|1.13.18|javascriptjqueryformbootstrapdropdownselectreplacementfont-awesome|Theiconicfontand…|=juliankrispel|2016-10-24|4.7.0|fontawesomefontawesomeiconfontbootstrap@coreui/vue|CoreUIVue…|=coreui|2020-12-17|3.2.7|coreuivuevue-componentvue-librarybootstrapframeworkresponsivelayoutcomponentcomponentsbootstrap-loader|BoostrapforWebpack|=alex.fedoseev…|2019-04-27|3.0.4|bootstraptwitter@fortawesome/fontawesome-|Theiconicfont,…|=devoto13…|2020-10-05|1.2.32|fontawesomefontawesomeiconsvgbootstrapsvg-core|||||@fortawesome/free-solid-s|Theiconicfont,…|=devoto13…|2020-10-05|5.15.1|fontawesomefontawesomeiconsvgbootstrapvg-icons|||||bootstrap-sass|bootstrap-sassisa…|=bootstrap-adm…|2019-02-13|3.4.1|bootstrapsasscsseyeglass-modulepwstrength-bootstrap|jQuerypluginfor…|=ablanco|2020-05-28|3.0.9|bootstrappasswordstrengthmeterjquery-pluginecosystem:jquerybootstrap-switch|Turncheckboxesand…|=lostcrew|2019-04-13|3.4.0|bootstrapswitchjavascriptjsbootstrap-input-spinner|ABootstrap4/…|=shaack|2020-10-25|1.16.8|Bootstrap4BootstrapjQueryWidgetHtmlInputUI@coreui/coreui|HTML,CSS,and…|=coreui|2020-11-23|3.4.0|bootstrapcssdashboardframeworkfront-endresponsivesassuikitwebapp 全域安裝vs區域安裝使用npm安裝第三方模組可分為全域或區域兩種安裝位置且會有不同的應用考量。

全域會影響整個node.js的所有專案應用。

區域即只會影響該專案目錄應用。

GlobalInstall全域安裝 語法為npminstall-g,安裝位置會在/user/local(或Node安裝目錄)的node_modules目錄下 安裝位置在哪可透過指令npmroot-g查詢(可由npmconfigset''來修改位置)::::::::::全域安裝路徑::::::L:\nodeTest>npmroot-gK:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64\node_modules:::::::::Node參數清單::::::L:\nodeTest>npmconfigls;cliconfigsmetrics-registry="https://registry.npmjs.org/"scope=""user-agent="npm/6.14.9node/v14.15.3win32x64";nodebinlocation=K:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64\node.exe;cwd=L:\nodeTest;HOME=C:\Users\Loki-Home;"npmconfigls-l"toshowalldefaults.:::::::::安裝目錄路徑:::::::L:\nodeTest>npmconfiggetprefixK:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64 一旦選擇全域方式安裝,該模組將被全域所能使用。

也就是可以直接在命令列上使用模組。

一個模組就能被所有專案的node所使用避免過多的版本差異,但對個別專案的版本依賴有困難。

舉例來說npm就是一個全域型的原件模組,可透過list(或ls)指令確認(透過-g為指定global全域): L:\nodeDemo>npmlist-gnpmK:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64`[email protected] LocalInstall區域安裝 語法為npminstall,安裝到在目前終端機位置的node_modules目錄下 如果需要使用該模組時必須使用require()才能載入使用 只有該專案目錄內才能找到該模組,其他目錄下無法找到。

因此可避免不同專案內的版本衝突(A案需ver1,B案需ver3) 舉例來說:我們先在目錄nodeTest下安裝express,nodeTest會多一個node_moudles資料夾且可查詢,接著到另一個專案目錄nodeDemo做查詢結果。

:::本地安裝模組expressL:\nodeTest>npminstallexpress:::這裡報錯誤是因為我們還沒使用正確步驟生成package.json導致,之後會重新說明正式做法npmWARNsaveErrorENOENT:nosuchfileordirectory,open'L:\nodeTest\package.json'npmnoticecreatedalockfileaspackage-lock.json.Youshouldcommitthisfile.npmWARNenoentENOENT:nosuchfileordirectory,open'L:\nodeTest\package.json'npmWARNnodeTestNodescriptionnpmWARNnodeTestNorepositoryfield.npmWARNnodeTestNoREADMEdatanpmWARNnodeTestNolicensefield.+express@4.17.1added50packagesfrom37contributorsandaudited50packagesin21.263sfound0vulnerabilities:::可找到模組L:\nodeTest>npmlistexpressL:\nodeTest`[email protected]:::其他目錄下找不到該模組L:\nodeTest>cd/nodeDemoL:\nodeDemo>npmlistexpressL:\nodeDemo`--(empty) npm其他指令你可以使用npm進行一些常用操作: npmsearch已知的模組名稱進行搜尋檢查可用安裝與版本資訊 npminstall|update|uninstall對已安裝過後的模組進行版本安裝、升級、移除。

npmhelp查找npm相關指令 npmcacheclear清除npm本地暫存 npmpublish|unpublish@將目前專案目錄發佈到npm套件管理系統給Nodejs社群使用,也能取消指定的某版本 ::::::::::::::::更新已安裝的模組L:\nodeTest>npmupdateexpress::全域模組的升級方式::L:\nodeTest>npmupdateexpress-g::::::::::::::::移除已安裝的模組L:\nodeTest>npmuninstallexpressnpmWARNsaveErrorENOENT:nosuchfileordirectory,open'L:\nodeTest\package.json'npmWARNenoentENOENT:nosuchfileordirectory,open'L:\nodeTest\package.json'npmWARNnodeTestNodescriptionnpmWARNnodeTestNorepositoryfield.npmWARNnodeTestNoREADMEdatanpmWARNnodeTestNolicensefield.removed1packageandaudited73packagesin1.39sfound0vulnerabilities LocalModules(自建模組)自建模組就是自己寫一個模組提供給自己應用。

模組單位為一個js檔案,跟node應用一樣都是js檔,但應用是透過指令nodeapp.js來執行,模組是在應用內宣告request('mod.js')來載入,應用與模組兩種用法不同。

自建模組也能打包成npm且分享至npm平台去。

手動的自建模組進行自建模組編寫時,最後需使用export方式傳出去。

屆時任何應用進行request載入時才能正常接洽使用回傳內容。

export本身是一個變數,它屬於整個module模型下的原生練物件。

因此只要將想回傳的東西指定給export(可以是字串或文字)即可。

產生resport回傳跟隨範例,設計一個模組在專案目錄下建立mod.js進行設計。

注意需要將資料回傳給應用時需指定exports來回應。

方法一:將exports當作物件,指定三種名稱函式mod.jsexports.en=function(userName){console.log('Hello!'+userName);};exports.tw=function(userName){console.log('你好!'+userName);};exports.hk=function(userName){console.log('雷侯!'+userName);}; 方法二:將完整的物件指定給exportmod.jsconsthello={en:function(userName){console.log('Hello!'+userName);},tw:function(userName){console.log('你好!'+userName);},hk:function(userName){console.log('雷侯!'+userName);}};module.exports=hello;/********************exports=hello;也能直接寫成這樣省略module字樣********************//****另一種簡化後的寫法module.exports={en:function(userName){console.log('Hello!'+userName);},tw:function(userName){console.log('你好!'+userName);},hk:function(userName){console.log('雷侯!'+userName);}};****/ 發出request取得結果接著建立test.js檔案編寫Node應用,注意檔案路徑上的差異。

我們透過request來請求載入結果存成變數,這個MyMod將是一個物件資料。

就能直接指定物件屬性來取得函式結果。

test.js/*自訂模組的requare時需指定相對路徑,而原生或npm包裝的模組不需要.js副檔名可省略,將會自動去尋找屬於js副檔名之檔名constmyMod=require('./mod.js');*/constmyMod=require('./mod');myMod.tw("Loki"); 執行應用程式透過終端機執行node應用執行test.js程式,得到以下結果。

L:\nodeDemo>nodetest.js你好!Loki npm的自建模組可使用npm元件方式新建立模組。

在npm話題內所有的模組都是package安裝包觀念,因此每一個模組內都會有package.json檔案提供這個安裝包的詳細資訊。

透過npminit方式初始化package(將要求你輸入一些基本資料)。

一個package可以跟別人的package形成依賴關係使用。

如果你安裝了PackageA時,可能這個A會自動幫你下載其他指定的PackageBCDE…利於本體A的正常運行。

所有的npm模組都有自己的package.json提供相關資訊參數,所以自己的node自訂模組也會有package.json。

modules是指一個功能程式之模組(不需package.json);package是指一個具備package.json的完整安裝包,內容會包含多個modules或其他相依關係的packages。

立相依關係的應用以下步驟為透過一個乾淨新專案為設計起點: 先建立一個test.js等待(被init被偵測) 透過init來建立package並手動設定package基本資料。

過程中你可以直接Enter用npm預判的預設值來自動填入。

L:\nodeDemo>npmlsL:\nodeDemo--(empty)L:\nodeDemo>npminitThisutilitywillwalkyouthroughcreatingapackage.jsonfile.Itonlycoversthemostcommonitems,andtriestoguesssensibledefaults.See`npmhelpinit`fordefinitivedocumentationonthesefieldsandexactlywhattheydo.Use`npminstall`afterwardstoinstallapackageandsaveitasadependencyinthepackage.jsonfile.Press^Catanytimetoquit.packagename:(nodedemo)version:(1.0.0)description:myfirstpackageentrypoint:(test.js)testcommand:gitrepository:keywords:author:LokiJianglicense:(ISC)AbouttowritetoL:\nodeDemo\package.json:{"name":"nodedemo","version":"1.0.0","description":"myfirstpackage","main":"test.js","scripts":{"test":"echo\"Error:notestspecified\"&&exit1"},"author":"LokiJiang","license":"ISC"}IsthisOK?(yes)yes 接著安裝npmexpress到這個本地安裝目錄內,並添加--save儲存在相依關係清單。

L:\nodeDemo>npminstallexpress--savenpmnoticecreatedalockfileaspackage-lock.json.Youshouldcommitthisfile.npmWARNnodedemo@1.0.0Norepositoryfield.+express@4.17.1added50packagesfrom37contributorsandaudited50packagesin24.244sfound0vulnerabilities 此時你可以回到根目錄的package.json內查看dependencies部分,會寫到這個模組需要依賴express套件。

未來如果別人從npm安裝你這個模組時,會自動安裝express使你的自訂模組正常運行。

你也可以試著從/node_moudles/express/package.json查看dependencies資訊,代表這個模組也依賴很多別人的套件。

也就是為何你只安裝express卻在/node_moudles/被強迫載入一堆你不認識的模組套件。

回話題到test.js設定express的調用: constexpress=require('express');constapp=express();app.get('/',(req,res)=>{res.send('HelloWorld!')})app.listen(3000,()=>{console.log(`Exampleapplisteningathttp://localhost:${port}`)}) 回到終端機執行你的test.js L:\nodeDemo>nodetest.jsExampleapplisteningathttp://localhost:3000 分享模組至npm你可以將自建的npm模組分享至npm開源平台(需註冊帳號),如需打造私用領域需額外pro付費。

要發布package至npm開源平台時有以下主要指令: npmwhoami若已登入帳號,能看到帳戶資訊 npmlogin登入帳號 npmpublish將目前路徑下的package上傳至npm,並根據package.json基本資料自動提供名稱與版本資訊。

如需版本更新需手動調整package.json的版本號並再次覆蓋的送出本指令。

npminstall將開源平台上的Package進行下載安裝 CoreModules(原生模組)由Node提供原生的內建模組,提供直接使用於應用APP的操作。

大致可分為以下常用重要的原生模組。

fs檔案系統fs檔案系統可用功能十分廣泛,除了能對檔案進行調整讀取也能對目錄進行控制。

使用前同樣需要透過require來載入該模組。

檔案資訊透過fs.stat()方式獲得檔案情報,需要從callback的參數來獲得。

不建議於進行檔案讀寫時額外用此方法確認之失敗手續,你應該直接用那些讀寫方法且若err則直接處理失敗手續。

index.jsconstfs=require('fs');fs.stat('text.txt',function(err,stats){//若檔案有誤,將產生err之錯誤物件//回傳皆為布林值console.log("檔案:"+stats.isFile());console.log("目錄:"+stats.isDirectory());console.log("區塊類型:"+stats.isBlockDevice());console.log("字元類型:"+stats.isCharacterDevice());console.log("符號連接:"+stats.isSymbolicLink());console.log("網頁Socket:"+stats.isSocket());}); 檔案讀取檔案I/O存取操作的相關類別、方法、事件。

讀取檔案的行為本身又可以分為同步fs.readFileSync(path[,options])與非同步fs.readFile(path[,options],callback): 非同步/**********file:test.txthello**************************/constfs=require('fs');fs.readFile('test.txt',function(err,data){if(err)throwerr;//如果失敗就離開並回傳至失敗事件console.log(data.toString());});console.log("world"); 非同步執行結果L:\nodeDemo>nodetest.jsworldhello 同步/**********file:test.txthello**************************/constfs=require('fs');constdata=fs.readFileSync('test.txt');console.log(data.toString());console.log("world"); 非同步執行結果L:\nodeDemo>nodetest.jshelloworld 寫入檔案寫入檔案方式為fs.writeFile(file,data[,options],callback),指定路徑若檔案不存在則新增,反之為覆蓋。

constfs=require('fs');fs.writeFile('hero.txt','Loki',(err)=>{//[,options]預設為UTF8可省略if(err)throwerr;console.log("done");}); 對已存在(不覆蓋)的檔案進行插入文字方式為fs.appendFile(path,data[,options],callback) constfs=require('fs');fs.appendFile('hero.txt','_Jiang',(err)=>{//[,options]預設為UTF8可省略if(err)throwerr;console.log("done");});/********hero.txt*Loki_Jiang**************/ 刪除檔案與更名刪除檔案的方式為fs.unlink(path,callback),修改檔案名稱的方式為fs.rename(oldPath,newPath,callback) //constfs=require('fs');//fsrequire('fs').unlink('hero.txt',()=>{....sameas↓require('fs').unlink('hero.txt',()=>{console.log('done');}); 開啟關閉的讀取修改這裡跟前面不太一樣的是先透過開啟檔案方式fs.open(path[,flags[,mode]],callback)將資源列入暫存再進行讀取寫入。

相對來說資源效率比前面的使用還好。

開啟檔案方式需使用標記flag來代表何種開啟方式,flag公式如下: ‘r’:打開檔案用於讀取。

如果檔案不存在則異常。

‘r+’:打開檔案用於讀取和寫入。

如果檔案不存在則異常。

‘rs+’:打開檔案用於讀取和寫入(同步模式)。

IO操作將繞過本地系統的檔案緩存。

‘w’:打開檔案用於寫入。

如果檔案不存在則創建,如果檔案存在則截斷。

‘wx’:類似於‘w’,但如果路徑存在則失敗。

‘w+’:打開檔案用於讀取和寫入。

如果檔案不存在則創建,如果檔案存在則截斷。

‘wx+’:類似於‘w+’,但如果路徑存在則失敗。

‘a’:打開檔案用於追加。

如果檔案不存在則創建。

‘ax’:類似於‘a’,但如果路徑已存在則失敗。

‘a+’:打開檔案用於讀取和追加。

如果檔案不存在則創建。

‘ax+’:類似於‘a+’,但如果路徑存在,則失敗。

‘as’:打開檔案用於追加(同步模式)。

如果檔案不存在則創建。

‘as+’:打開檔案用於讀取和追加(同步模式)。

如果檔案不存在則創建。

為了連續性作業示範,開啟檔案後我們將內容讀取動作fs.read(fd,buffer,offset,length,position,callback),並最後將檔案關閉fs.close(fd,callback)。

test.jsconstfs=require('fs');fs.open('test.txt','r',function(err,fd){if(err)returnconsole.error(err);fs.read(fd,function(err,bytesRead,buffer){//fd為此fs.open所產生的工作編號(number),利於指定上的操作行為。

if(err)throwerr;if(bytesRead>0){console.log(bytesRead+"字元被讀取");console.log(buffer.slice(0,bytesRead).toString());}fs.close(fd,function(err){if(err)throwerr;});});}); cmdL:\nodeDemo>nodetest.js5字元被讀取hello events事件events為提供事件監聽與處理的方法,在Node.js的一些物件(透過Emitter觸發器)來觸發指定事件(Listener)進行呼叫函式。

index.jsconstEventEmitter=require('events');constlokiEvent=newEventEmitter();//新事件lokiEvent.on('todo',function(){//該事件的todo發生時,做指定事情console.log("helloworld");})lokiEvent.emit('todo');//觸發lokiEvent事件的todo(觸發器名稱)//printhelloworld buffer緩衝區Buffer為處理二進位資料時的物件,要求作業系統之記憶體空間配額。

當使用fs檔案讀取時會用到存放處理,Buffer在全域下不需要使用require就能使用。

index.jsconstbf=Buffer.from('Loki','utf8');console.log(bf);//console.log(bf.toString());//Lokiconsole.log(bf.toString('hex'));//4c6f6b69console.log(bf.toString('base64'));//hello path路徑path主要是在進行fs檔案目錄之操作讀取寫入時,會遇到的字串轉換處理。

index.jsconstpath=require('path');/*normalize協助拔除`.`,`..`,`\\`,以及修正如windows作業系統的路徑表示符號*/console.log(path.normalize('C:////temp\\\\/\\/\\/foo/bar'));//printC:\temp\foo\bar/*join能將指定字串進行合併*/console.log(path.join(__dirname,'index.html'));//printL:\nodeDemo\index.html/*basename取出字串之檔名extname取出字串之附檔名dirname取出字串之目錄路徑*/console.log(__filename);console.log(path.basename(__filename));console.log(path.extname(__filename));console.log(path.dirname(__filename));//printL:\nodeDemo\index.js//printindex.js//print.js//printL:\nodeDemo 其他原生模組以下是部分常見模組,還有很多模組無法一一列出。

下一節將重要的網路應用相關模組(包含原生與第三方)額外獨立介紹。

os:控制作業系統或獲得相關資訊的方法。

url:提供解析URL之方法。

querystring:用來解析從用戶端傳回querystring字串之方法。

util:提供開發者使用的實用函式。

網路應用本章節目標將常用之網路應用開發所需模組與設計技巧。

net通訊服務TCP是HTTP通訊的基礎協定,作為Server與Client的封包傳送,用於一些底層上的資料確認通信使用。

這不是網頁媒體使用,而只是一種資訊封包傳送技術。

使用方式為透過net模組來進行架設TCP伺服器。

我們需要設計兩個應用程式分別為server.js與client.js,TCP伺服器保持監聽模式等待Client進行連線進一步取得Client資料,並回應資料還給Client。

server.jsconstnet=require('net');constserver=net.createServer(function(myConnect){//顯示連線資訊console.log(`正在與${myConnect.remoteAddress}:${myConnect.remotePort}建立連線`);//接收ClientData事件之處理myConnect.on('data',function(data){console.log(`來自${myConnect.remoteAddress}的client資料為'${data}'`);//寫入資料至Client端constserverData=`hi!Client`;myConnect.write(serverData);console.log(`發送給Client:${serverData}`)});//監聽ClientClose事件之處理myConnect.on('close',function(had_error){if(had_error)console.log('連線錯誤');elseconsole.log('Client連線已關閉,伺服器持續運作中。

..');});});//啟用Server服務consthost='192.168.1.105';constport=3999;server.listen(port,host,function(){console.log(`伺服器服務中。

...${host}:${port}`);}); client.jsconstnet=require('net');constclient=newnet.Socket();//建立Socket//連線至Serverconsthost='192.168.1.105';constport=3999;client.connect(port,host,function(){console.log(`連線至Server${host}:${port}`);//提供client資料傳送constclientData='hi!Server';client.write(clientData);console.log(`發送給Server:${clientData}`)});//監聽ServerData事件之處理client.on('data',function(data){console.log(`Server回應:${data}`);//請求關閉Server之連線console.log(`即將關閉伺服器連線。

..`);client.destroy();});//接收ServerClose事件之處理client.on('close',function(had_error){if(had_error)console.log('連線錯誤');elseconsole.log('Server連線已關閉');}); 接著透過不同node環境分別扮演Server與Clinet來測試連線。

http網頁服務HTTP架構於前者TCP之上層協定,能作為WebServer與瀏覽器之間的資料傳送。

我們能使用http模組來建立WebServer的相關類別、方法、事件(也是NodeJS的官方手冊初登場的新手教材教學),用於處理瀏覽器所發出的HTTP請求。

回應內容當瀏覽器對伺服器進行HTTP請求時獲得文字回應方式: 指定資料夾並建立js檔案,準備以下內容:webServerTXT.jsconsthttp=require('http');//宣告原生模組http//建立伺服器且提供網頁狀態、HEAD資訊、網頁內容constserver=http.createServer((request,response)=>{//response.statusCode=200;//response.setHeader('Content-Type','text/plain');//設定responseHEAD//response.write('HelloWorld')//設定網頁內容//response.end();//response送出//sameas↓response.writeHead(200,{'Content-Type':'text/plain'}); //response.setHeader()適合循序參數調整且最後將由writeHead()進行嘗試合併,writeHeader()內容優先為主response.end('HelloWorld');});//對伺服器進行開機並於完成作業後顯示文字至終端機consthostname='127.0.0.1',port=3000;//設定Web主機與通訊埠server.listen(port,hostname,()=>{console.log(`Serverrunningathttp://${hostname}:${port}/CloseServerpress'Ctrl+C'toexit`);}); 接著終端機輸入指令。

透過提示的指令開啟瀏覽器至指定URL網頁得到提示與伺服器運作之真實網頁。

cmdL:\nodeTest>nodewebServerTXT.jsServerrunningathttp://127.0.0.1:3000/CloseServerUseKey'Ctrl+C' 我們也可以直接回應一個網頁格式的內容,這裡我們寫得更簡速些,另注意HEAD資訊改為text/html。

webServerHTML.jsrequire('http').createServer((req,res)=>{res.writeHead(200,{'Content-Type':'text/html'});res.write(`TestHTML

HellorWorld

`);res.end();}).listen(3000,()=>{console.log('Serverrunningathttp://127.0.0.1:3000/');}); cmdL:\nodeDemo>nodewebServerHTMLServerrunningathttp://127.0.0.1:3000/ 也能傳送JSON資料給請求端,注意HEAD宣告改為application/json。

webServerJSON.jsconsthttp=require('http');constserver=http.createServer((request,response)=>{response.writeHead(200,{'Content-Type':'application/json'});//設定responseHEADresponse.write(JSON.stringify({dog:"wow",cat:"kiki"}));response.end();});server.listen(3000,()=>{console.log(`Serverrunningathttp://127.0.0.1:3000/`);}); 關於request用途,http.createServer所產生的callback之中,除了能指定response內容作為我們的目標網頁,request也能提供非常多有用的IncomingMessage來源資訊。

譬如我們可以取得請求方的資料如URL、HEAD、DATA test.jsconsthttp=require('http');//宣告原生模組httpconstserver=http.createServer((request,response)=>{response.writeHead(200,{'Content-Type':'text/html'});//設定responseHEADfor(constkeyinrequest)console.log(key);response.end();});server.listen(3000,"127.0.0.1",()=>{console.log(`Serverrunningathttp://127.0.0.1:3000/`);});//$nodetest.js,willreadallrequest'spototype 回應檔案前面來說都是透過JavaScript來會應指定的文字,我們可以搭配fs模組取得內容進行回應。

以下寫法為直接找到指定目錄,同時可以直接根據URL檔案名稱找到該目錄下的相同檔名,這裡多餘地利用require('path').basename(req.url)來過濾多餘路徑僅判斷檔名就好(不論URL的分類路徑)。

以下前置先建立目錄public與HTML檔案index.html,a.html,b.html,page404.html。

webServerHTML.jsconsthttp=require('http');constfs=require('fs');constserver=http.createServer(function(request,response){constpageDir=__dirname+'/public/';constfilePath=pageDir+(require('path').basename(request.url)||'index.html');constpage404=pageDir+'page404.html';fs.readFile(filePath,function(err,content){if(!err){response.writeHead(200,{'Content-Type':'text/html'});response.end(content);}else{console.log(err);fs.readFile(page404,function(err,content404){response.writeHead(404,{'Content-Type':'text/html'});response.end(content404);});}});});server.listen(3333,'127.0.0.1',function(){console.log(`Serverrunning!!tryhttp://127.0.0.1:3333/a.htmltryhttp://127.0.0.1:3333/b.htmltryhttp://127.0.0.1:3333/aaaaa/a.htmltryhttp://127.0.0.1:3333/c.htmlshow'404ERROR'`);}); 原生HTTP模組並不是很彈性,大部分的人都會推薦使用npm元件express作為網頁伺服器服務首選。

路由處理路由是指透過URL請求路徑來判斷執行的檔案讀取,呈現一種靜態檔案與動態網頁內容的變化。

舉例來說,可以根據請求的網址內容做不同的網址名稱(不需要副檔名)來找到指定的路徑檔案,以下前置與上節相同都有public目錄與數個檔案。

: webRouter.jsconsthttp=require('http');constfs=require('fs');http.createServer((request,response)=>{constpageDir=__dirname+'/public/';constresult=function(){//進行路由轉換:將動態URL算出靜態檔案位置switch(request.url){case'/apple':return{code:200,filePath:pageDir+'a.html'};case'/banana':return{code:200,filePath:pageDir+'b.html'};default:return{code:404,filePath:pageDir+'page404.html'};}}(); //直接執行獲得物件資料fs.readFile(result.filePath,function(err,content){response.writeHead(result.code,{'Content-Type':'text/html'});//設定responseHEADif(!err)response.end(content);elseresponse.end(pageDir+'page404.html');//如果檔案讀取失敗直接顯示404檔案});}).listen(3333,"127.0.0.1",()=>{console.log(`Serverrunning!!tryhttp://127.0.0.1:3333/appleshow'wordA'tryhttp://127.0.0.1:3333/bananashow'wordB'tryhttp://127.0.0.1:3333/catshow'404ERROR'`);}); 表單提交我們延續路由機制進行設計,提供三種路由可能分別是表單、結果、不存在。

表單部分進行設計一個URL路由指向到靜態檔案form並採用POST方式傳送,並提交後導向到結果部分,其路由指定之路徑名稱url。

如果是其他非指定的路由或非POST方式都導向到404頁面去。

先規劃form.html並放入指定目錄plubic內。

form.htmlDocument yourname:
password:

接著設計node應用,這裡多引用require('querystring')能幫助我們將表單資料(為二進位資料,需進行toString才能變成文字串資料)轉換成JSON格式。

webRouterFormconsthttp=require('http');constfs=require('fs');constqs=require('querystring');http.createServer((request,response)=>{constpageDir=__dirname+'/public/';constresult=function(){//進行路由轉換:將動態URL算出靜態檔案位置switch(request.url){case'/form':return{code:200,filePath:pageDir+'form.html'};case'/url':return{code:200,filePath:''//為了表單處理,這裡不提供靜態檔案路徑};default:return{code:404,filePath:pageDir+'page404.html'};}}(); //直接執行獲得物件資料if(result.filePath!=''){//代表為靜態檔案需求fs.readFile(result.filePath,function(err,content){response.writeHead(result.code,{'Content-Type':'text/html'});//設定responseHEADif(!err)response.end(content);elseresponse.end(pageDir+'page404.html');//如果檔案讀取失敗直接顯示404檔案});}else{//代表做表單處理if(request.method=='POST'){letbody=null;request.on('data',function(content){//data事件,將資料content進行處理body=qs.parse(content.toString());//content本身是二進位,因此需要先轉成字串。

接著透過qs.parse()能幫name1=value1&name2=value2轉換成{name1:value1,name:value2}});request.on('end',function(){//end事件,請求完畢後進行回應畫面之生成response.writeHead(result.code,{'Content-Type':'text/html'});response.write(`

TestHTML

Hi${body.name}!Nicetomeetyou.

YourPasswordis${body.pwd}

`);response.end();});}elseresponse.end(pageDir+'page404.html');//如果不是POST方式傳送則顯示404}}).listen(3333,"127.0.0.1",()=>{console.log(`Serverrunning!!tryhttp://127.0.0.1:3333/form/andSubmit`);}); Express.jsExpress是一個知名常用的網路應用程式開發框架,提供開發者更快有效建立Web應用程式且支援MVC架構。

提供應用程式與Web之間完整的路由與中介軟體。

初次安裝跟隨Express官方手冊教學,我們試著從npm方式創造一個應用程式。

於既定目錄下執行npminit初始化並填寫基本資訊,利用init來協助建立package.json,過程中的參數事項可自行評估填寫或略過。

將express相依安裝到專案目錄。

另外可添加--save參數進行安裝,這能幫我們自動安裝相依關係清單中的模組。

L:\nodeDemo>npminitThisutilitywillwalkyouthroughcreatingapackage.jsonfile.Itonlycoversthemostcommonitems,andtriestoguesssensibledefaults.See`npmhelpinit`fordefinitivedocumentationonthesefieldsandexactlywhattheydo.Use`npminstall`afterwardstoinstallapackageandsaveitasadependencyinthepackage.jsonfile.Press^Catanytimetoquit.packagename:(nodedemo)version:(1.0.0)description:myfirstexpressappentrypoint:(index.js)testcommand:gitrepository:keywords:studyauthor:LokiJianglicense:(ISC)AbouttowritetoL:\nodeDemo\package.json:{"name":"nodedemo","version":"1.0.0","description":"myfirstexpressapp","main":"index.js","scripts":{"test":"echo\"Error:notestspecified\"&&exit1"},"keywords":["study"],"author":"LokiJiang","license":"ISC"}IsthisOK?(yes)L:\nodeDemo>npminstallexpress--savenpmnoticecreatedalockfileaspackage-lock.json.Youshouldcommitthisfile.npmWARNnodedemo@1.0.0Norepositoryfield.+express@4.17.1added50packagesfrom37contributorsandaudited50packagesin24.638sfound0vulnerabilitiesL:\nodeDemo> 初始運作與掛載靜態目錄這裡試著用express產生一個可服務的NodeWeb運作,而express提供內建的中介軟體函式express.static來設定靜態路由,這能方便你掛載一些媒體檔案或網頁目錄。

規劃練習檔案路徑為/formDir/post.html,/formDir/delete.html,/public/index.html,內容隨意輸入能判別即可。

規劃以下index.js檔案,並於終端機輸入node.或node./即可執行嘗試並指定URL測試。

這裡不用檔名是因為index.js命名技巧關係。

index.jsconstexpress=require('express');constapp=express();/*設定靜態目錄多個目錄掛載可多筆設定亦可產生虛擬的目錄位置*/app.use(express.static('public'));//掛載靜態目錄public為路由根目錄app.use('/form',express.static('formDir'));//掛載靜態目錄formDir為路由另名目錄formconstserver=app.listen(3000,function(){console.log(`tryURLhttp://127.0.0.1:3000/(it'sinplubic/index.html)http://127.0.0.1:3000/form/post.html(it'sinformDir/post.html)http://127.0.0.1:3000/form/delete.html(it'sinformDir/delete.html)`);}); 何謂MVCMVC是一個設計上的物件導向模式之架構,並不是一個技術規範。

將系統架構分類出Model資料模型、View使用介面、Controller控制邏輯三大核心元件。

能很常的在一些WebApplications應用上被看到廣泛使用。

Web開發之類型 webApplication在Web開發領域內我們稱呼為Web應用,藉由使用者的HTTP請求內容,透過相關邏輯處理,在伺服器端生成使用者所需的資料畫面形成互動。

像是常逛的網站都是這類型Web開發,譬如購物網站、網路銀行、拍賣、會員商務網站等。

webServices在Web開發領域內還有另一種稱呼為Web服務類型,透過SOAP協定或RESTfulAPI方式進行資料交換,其實Web服務也是一種Web應用,只差在通常不是對直接對消費者的應用互動。

像是GoogleAPI、FickerAPI、OpenData等。

在開發Web應用時,傳統方式是從輸入、處理、輸出的步驟來完成互動設計。

而MVC則是反將這些步驟拆解成三種元件,每當使用者使用瀏覽器提出HTTP請求時,由Controller收到請求進行執行動作,也就是控制Model與View的變化,而Model元件會負責將資料準備好使得View能順利生成HTTP回應。

如圖所示: Model資料模型主要負責Web應用的資料存取與處理規則,也就是存取至資料庫、fs檔案、XML等相關資料。

View使用介面展示邏輯的物件,也就是建立使用者的瀏覽執行資訊,而HTTP回應的資訊通常是HTML網頁。

View元件會透過Mode元件來取得資料並將資料轉換成給使用者的畫面資訊。

Controller控制邏輯整個Web應用的中心處,負責跟view跟Model元件之間的協調與控制執行,例如在獲得使用者的HTTP請求處理就屬於Controller控制器範圍內,根據所需操作去控制Model來存取資料,然後送至View進行畫面所需的HTML網頁。

應用程式產生器Express提供一個需安裝的產生器能協助幫忙快速建立應用程式所需要的基本框架結構,透過該步驟協助能快速建立一個Web應用開發。

使用產生器來建立應用程式結構,只是多種用來建立Express應用程式結構的其中一種方式。

您有權使用這種結構,或是加以修改盡量符合您的需求。

安裝透過指令express-generator-g進行全域安裝(必要)。

ExpressGenerator與前面介紹的Express不同的是,Express可透過區域安裝方式再自行require載入使用,而ExpressGenerator是直接全域安裝並透過指令輸入express即可產生具備MVC架構的APP初始環境。

因此選擇ExpressGenerator來初始化應用即可,不用預先安裝Express到區域或全域上。

L:\nodeDemo>[email protected]:Legacyversionsofmkdirparenolongersupported.Pleaseupdatetomkdirp1.x.(NotethattheAPIsurfacehaschangedtousePromisesin1.x.)K:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64\express->K:\VSCODE\data\user-data\User\lokiTools\nvs\node\14.15.3\x64\node_modules\express-generator\bin\express-cli.js+express-generator@4.16.1added10packagesfrom13contributorsin2.255s 初始化應用由於現在已經有全域指令express能使用,直接在目前工作目錄上建立一個名為lokiApp的Express應用程式: L:\nodeDemo>expresslokiAppwarning:thedefaultviewenginewillnotbejadeinfuturereleaseswarning:use`--view=jade'or`--help'foradditionaloptionscreate:lokiApp\create:lokiApp\public\create:lokiApp\public\javascripts\create:lokiApp\public\images\create:lokiApp\public\stylesheets\create:lokiApp\public\stylesheets\style.csscreate:lokiApp\routes\create:lokiApp\routes\index.jscreate:lokiApp\routes\users.jscreate:lokiApp\views\create:lokiApp\views\error.jadecreate:lokiApp\views\index.jadecreate:lokiApp\views\layout.jadecreate:lokiApp\app.jscreate:lokiApp\package.jsoncreate:lokiApp\bin\create:lokiApp\bin\wwwchangedirectory:>cdlokiAppinstalldependencies:>npminstallruntheapp:>SETDEBUG=lokiapp:*&npmstart 到現在你已經獲得一個應用程式目錄同時該目錄持有完整的MVC架構。

接著根據提示我們需要轉到應用程式的目錄下,將所需要的相依模組安裝起來(將會自動生成node_moudle目錄與相關檔案)。

L:\nodeDemo>cdlokiAppL:\nodeDemo\lokiApp>[email protected]:Jadehasbeenrenamedtopug,pleaseinstallthelatestversionofpuginsteadofjadenpmWARNdeprecatedconstantinople@3.0.2:Pleaseupdatetoatleastconstantinople3.1.1npmWARNdeprecatedtransformers@2.1.0:Deprecated,usejstransformernpmnoticecreatedalockfileaspackage-lock.json.Youshouldcommitthisfile.added100packagesfrom139contributorsandaudited101packagesin75.152sfound4vulnerabilities(3low,1critical)run`npmauditfix`tofixthem,or`npmaudit`fordetails 最後,你可以試著執行Web服務起來。

L:\nodeDemo\lokiApp>SETDEBUG=lokiapp:*&npmstart>[email protected]:\nodeDemo\lokiApp>node./bin/wwwlokiapp:serverListeningonport3000+0msGET/200523.280ms-170GET/stylesheets/style.css2004.047ms-111GET/favicon.ico40412.350ms-1012 結構說明現有資料上上可以分為三個重要目錄: public:放置靜態HTML5、CSS、JS檔案。

路由已經設定好,任何放入此目錄下的檔案都能直接對應網址上使用。

譬如plubic/test.html 的URL位置為http://127.0.0.1:3000/test.html。

routes:路由模組 views:MVC的View目錄,由於是Jade樣板引擎所以副檔名皆為.jade,預設會提供error.jade,index.jade,layout.jade三個樣板檔案 以及兩個重要檔案: app.js:用於Express.js運作Web的核心檔案。

主要是將Expres相關環境連結搞定。

以下附上註解部分說明。

app.jsvarcreateError=require('http-errors');varexpress=require('express');//載入Expres模組varpath=require('path');varcookieParser=require('cookie-parser');varlogger=require('morgan');//載入路由目錄下的index.js與users.js,作為新增路由使用varindexRouter=require('./routes/index');varusersRouter=require('./routes/users');varapp=express();//透過app拿來控制Express進行set與use相關設定。

//viewenginesetup//設定VIEW樣板位置與引擎格式app.set('views',path.join(__dirname,'views'));app.set('viewengine','jade');//透過use來開啟網站功能,像是網站記錄、欄位資料分析等。

app.use(logger('dev'));app.use(express.json());app.use(express.urlencoded({extended:false}));app.use(cookieParser());app.use(express.static(path.join(__dirname,'public')));//指定靜態目錄位置為public//套用路由到Express網站的應用程式內app.use('/',indexRouter);app.use('/users',usersRouter);//catch404andforwardtoerrorhandler//處理404沒有找到的錯誤app.use(function(req,res,next){next(createError(404));});//errorhandler//進行相關錯誤的處理過程app.use(function(err,req,res,next){//setlocals,onlyprovidingerrorindevelopmentres.locals.message=err.message;res.locals.error=req.app.get('env')==='development'?err:{};//rendertheerrorpageres.status(err.status||500);res.render('error');});//最後使用export方式匯出整個Express物件的變數appmodule.exports=app; package.json:相關定義檔案,包含版本號、資訊相依模組內容等。

bin/www:建立Web服務的核心檔案,而預設埠3000也是在此設定。

bin/wwwvarport=normalizePort(process.env.PORT||'3000');//預設port號app.set('port',port); 手動添加路由與View網頁這裡我們將示範如何手動添加路由與Jade樣板引擎的MVC規劃的View頁面。

首先需要對app.js進行載入路由與新增路由。

app.jsvarindexRouter=require('./routes/index');varusersRouter=require('./routes/users');varcustomRouter=require('./routes/custom');//customRouterequire//...app.use('/',indexRouter);app.use('/users',usersRouter);app.use('/custom',customRouter);//customRouteadd 接著設計我們的routes/custom.js檔案(可以拿index.js來改)。

custom.jsvarexpress=require('express');varrouter=express.Router();/*GETcustompage.*//*透過router.get()的GET方法取得網頁而res.render()能產生response生成網頁-由於是Jade引擎所以第一個參數需指定樣板檔案名稱(也就是檔案為views/lokiJade)-第二個參數為該response之參數物件,這裡示範提供網頁標題*/router.get('/',function(req,res,next){res.render('lokiJade',{title:'MyCustomTest'});});//最後進行Export匯出module.exports=router; 再來到MVC結構的views目錄新增Jade樣板。

這裡我們要對應正確名稱lokiJade.jade extendslayoutblockcontenth1=titlepWelcometo#{title}div這是用JADE語法寫出來的樣板網頁唷 JADE可以分為兩大區域: extendlayout:能幫忙擴展同目錄下layout.jade檔案。

譬如layout.jade的內容就是常用的html宣告。

layout.jadedoctypehtmlhtmlheadtitle=titlelink(rel='stylesheet',href='/stylesheets/style.css')bodyblockcontent blockcontent:為區塊替換,也就是layout.jade與lokiJade.jade的blockcontent形成內容。

Jade語法簡介: =var符號為完整指定參數之內容,也就是可指定上一步驟的response參數物件。

如果是在某字串內替換則使用#{var}方式來替換。

HTML標籤指名稱沒有<>,如需指定attr屬性寫法為(key1=val1,key2=val2),例如a(style="color:red;font-size:2rem",href="/index")。

指定id方式為div#id,指定class方式為div.class即可,如此直白。

檢查畫面結果最後,我們可試著重新執行一次Ctrl+C,這次可嘗試不添加SETDEBUG直接輸入指令。

並測試網址畫面http://127.0.0.1:3000/custom是否成功。

cmdL:\nodeDemo\lokiApp>npmstart>[email protected]:\nodeDemo\lokiApp>node./bin/www 當用戶瀏覽該網址時,Node.js端的Console會看見相關GET動作,包含我們的路由目錄以及CSS樣式表。

GET/custom304184.458ms--GET/stylesheets/style.css3041.382ms-- 與MySQL連線Express扮演伺服器角色時,我們能跟各種資料庫進行連線整合。

舉例來說,在伺服器端安裝MySQL服務(這裡採WINDOWSXAMPP做簡單示範)並嘗試安裝模組進行連結。

事前準備SQL,將以下SQL建立準備完畢,將獲得資料庫名為node_sample並持有animal資料表: db.sqlSETSQL_MODE="NO_AUTO_VALUE_ON_ZERO";SETAUTOCOMMIT=0;STARTTRANSACTION;SETtime_zone="+00:00";CREATEDATABASEIFNOTEXISTS`node_sample`DEFAULTCHARACTERSETutf8mb4COLLATEutf8mb4_unicode_ci;USE`node_sample`;CREATETABLE`animal`(`id`smallint(5)UNSIGNEDNOTNULL,`name`textCOLLATEutf8mb4_unicode_ciNOTNULL,`weight`int(11)NOTNULL,`info`textCOLLATEutf8mb4_unicode_ciNOTNULL,`date`datetimeNOTNULL)ENGINE=InnoDBDEFAULTCHARSET=utf8mb4COLLATE=utf8mb4_unicode_ci;INSERTINTO`animal`(`id`,`name`,`weight`,`info`,`date`)VALUES(1,'藪貓',9,'食肉目貓科藪貓屬','2020-12-2008:21:08'),(2,'耳廓狐',17,'食肉目犬科狐屬','2020-12-1915:57:56'),(3,'河馬',120,'鯨偶蹄目河馬科河馬屬','2020-11-0708:52:06'),(4,'印度象',1258,'長鼻目象科象屬','2020-11-0708:52:06'),(5,'浣熊',30,'食肉目浣熊科浣熊屬','2020-11-0709:13:58'),(6,'斑馬',53,'奇蹄目馬科馬屬','2020-11-0708:52:06'),(7,'瞪羚',32,'鯨偶蹄目牛科瞪羚屬','2020-11-0708:52:06'),(8,'土狼',32,'食肉目鬣狗科土狼屬','2020-11-0708:52:06'),(9,'水獺',32,'食肉目鼬科小爪水獺屬','2020-11-0708:52:06'),(10,'美洲豹',999999,'食肉目貓科豹屬','2020-11-0708:52:06'),(11,'山貓',999999999,'食肉目貓科虎貓屬','2020-11-0708:52:06'),(12,'馬來貘',80,'奇蹄目貘科貘屬','2020-11-0709:13:33'),(13,'馬島獴',17,'食肉目食蟻狸科馬島獴屬','2020-11-0708:52:06'),(14,'花鹿',120,'鯨偶蹄目鹿科花鹿屬','2020-11-0708:52:06'),(15,'眼鏡王蛇',1258,'有鱗目眼鏡蛇科眼鏡王蛇屬','2020-11-0708:52:06'),(16,'食蟻獸',40,'披毛目食蟻獸科小食蟻獸屬','2020-11-0709:13:58'),(17,'孔雀',532,'雞形目雉科孔雀屬','2020-11-0710:54:58'),(18,'袋獾',32,'袋鼬目袋鼬科袋獾屬','2020-11-0710:55:05'),(19,'傘蜥蜴',555,'有鱗目飛蜥科傘蜥蜴屬','2020-11-0710:55:26'),(20,'朱䴉',32,'鵜形目䴉科朱䴉屬','2020-11-0708:52:06'),(21,'羊駝',999999,'鯨偶蹄目駱駝科小羊駝屬','2020-11-0708:52:06'),(22,'美洲紅䴉',55,'鵜形目䴉科美洲䴉屬','2020-11-0709:22:04'),(23,'美洲河狸',55,'嚙齒目河狸科河狸屬','2020-11-0709:24:31'),(24,'黑尾土撥鼠',999999999,'嚙齒目松鼠科草原犬鼠屬','2020-11-0708:52:06'),(25,'獅子',55,'食肉目貓科豹屬','2020-12-2009:38:19'),(26,'原牛',120,'鯨偶蹄目牛科牛屬','2020-12-2009:39:03'),(27,'阿拉伯大羚羊',2223,'鯨偶蹄目牛科長角羚屬','2020-12-2009:40:53'),(28,'日本黑熊',222,'食肉目熊科熊屬','2020-12-2009:49:00'),(29,'駝鹿',22,'鯨偶蹄目鹿科駝鹿屬','2020-12-2009:50:23');ALTERTABLE`animal`ADDPRIMARYKEY(`id`);ALTERTABLE`animal`MODIFY`id`smallint(5)UNSIGNEDNOTNULLAUTO_INCREMENT,AUTO_INCREMENT=30;COMMIT; 接著為了與MySQL連結我們需要安裝mysql模組,進行全域安裝後嘗試簡單使用test.js來進行測試。

test.jsconstmysql=require('mysql');constconnection=mysql.createConnection({host:'localhost',user:'root',password:'',database:'node_sample'});connection.connect();connection.query('SELECT*FROManimal',function(err,rows,fields){if(err){console.log("sqlcontentfail!!");throwerr};console.log('Thedatais:',rows);});connection.end(); 接著指定路徑到檔案位置,直接透過node來執行該js應用程式(不是Express應用)。

cmdL:\nodeDemo>nodetest.jsThedatais:[RowDataPacket{id:1,name:'藪貓',weight:9,info:'食肉目貓科藪貓屬',date:2020-12-20T00:21:08.000Z},RowDataPacket{id:2,name:'耳廓狐',weight:17,info:'食肉目犬科狐屬',date:2020-12-19T07:57:56.000Z}//...(略)] Express內使用SELECT沿用之前的lokiApp應用,我們將規劃一個路由作為顯示資料庫SELECT結果,並透過jade進行模板輸出。

同樣的在那之前mysql模組已全域安裝完成。

步驟修改處相同流程,先至app.js進行添加路由位置,未來只要網址輸入/animal則將指向到/routes/animal執行內容。

app.jsvaranimalRouter=require('./routes/animal');/////////////////////////////customSQLSELECT//...app.use('/animal',animalRouter);//customRouteadd 接著到routes目錄新增animal.js進行應用程式編寫,每當有人到達指定URL請求時進行以下作業: animal.jsvarexpress=require('express');varrouter=express.Router();varcontent=require('./dbContent');//將DB連結資訊以另外的地方編寫,再匯入回傳作為變數content之內容,此檔案位置為相同目錄下router.get('/',function(req,res){//執行SQL指令content.query('SELET*FROManimal',function(err,rows,fields){if(!err){//成功Fconsole.log(rows);//res.render('lokiJade',{title:'MyCustomTest'});res.render('aninalJade',{items:rows});//將資料以items變數方式傳送給JADE}elsethrowerr;//失敗});});//最後進行Export匯出module.exports=router; dbContent.jsconstmysql=require('mysql');module.export=mysql.createConnection({host:'localhost',user:'root',password:'',database:'node_sample'}); 由於response的render作業會去取得animalJade.jade樣板並持有變數items。

因此開始規劃網頁畫面結果: animalJade.jadeextendslayoutblockcontenttable(border='1',style='width:100%')theadtrthIDth名稱th重量th簡介th修改日期tbodyeachiteminitemstrtd=item.idtd=item.nametd=item.weighttd=item.infotd=item.date 此時嘗試網址http://127.0.0.1:3000/animal查看網頁結果。

參考文獻 指南|Node.js npmDocs Node.js中文网 Express-Node.jsWeb應用程式架構



請為這篇文章評分?