--- tags: Node.js --- # Node.js ## Node.js-basis node.js 本身是一個 JS 的運行環境,類似瀏覽器一樣 可以解析和執行 JS 代碼。 在 node.js 中沒有 DOM、BOM 對象,他的 JS 是最基礎的 ECMAScript 再加上其他 API ### node.js 最基本的使用方式為: - A. 編寫一個 JS 文件 - B. 打開終端機 定位到此文件的目錄 輸入 "node 空格 + 文件名" * 這裏要注意 文件名稱如果是 node.js 則無法使用,且文件名稱最好也不要是中文。 ### node.js 文件操作: 瀏覽器中的 JS 是沒有文件操作能力的,但在 node.js 中 具有文件操作能力。 - 讀取文件方式為: - A. 使用 require 方法加載文件系統模塊 > `var fs = require('fs')` - B. 使用 `fs.readFile()` 方法 其參數1為文件地址 參數2為回調函數 * 回調函數中有兩個參數 其參數1為error 錯誤對象 參數2為data 文件內容 * 且data內容為二進制數據 必須在 JS 文件中把 data.toString() - 寫入文件方式為: - A. 使用 require 方法加載文件系統模塊 > `var fs = require('fs')` - B. 使用 `fs.writeFile()` 方法 其參數1為文件寫入地址+文件名稱 參數2為要寫入文件的內容 參數3為回調函數 * 回調函數中只有一個參數 為 error 錯誤對象 ### 創建服務器: 在 node.js 中提供了一個核心模塊叫 http 是專門用來創建編寫服務器的。 - 服務器使用方法為: - A. 使用 require 方法加載 http 核心模塊 `var http = require('http')` - B. 創建一個變量 server 為 `http.createServer()` - C. 使用 `server.on()` 方法 參數1為 request 參數2為回調函數 * 回調函數中有兩參數 參數1為 request 參數2為 response * 在回調函數中 需使用 response.write('XXX') 方法傳入響應內容 * 響應內容只能傳入二進制數字與字符串 其他一律報錯 * 傳完以後需寫上 response.end() 結束響應 否則瀏覽器會一直等待 * response.write('XXX') 也可以改成直接寫 response.end('XXX') 讓結束響應的同時寫入響應內容 * 這裏要注意 如果你響應的內容中有中文字 而你沒有設置響應頭 * 那你就會發現在瀏覽器頁面中響應的中文內容變成亂碼 * 解決方式為設置一個響應頭:`res.setHeader('Content-Type','text/html; charset=utf-8')` * 我們也可以在回調函數中獲取請求地址 request.url > 獲取結果為網址端口號後面的所有內容 - D. 使用 `server.listen()` 方法創建端口號 參數1為你指定的端口號 參數2為回調函數 * 你指定的端口號不能被其他人所使用 否則無法訪問 * 回調函數可以傳入一個 console.log 告知服務器已連結完成 ### Node.js 中的模塊系統 - 模塊又分為核心模塊與自定義模塊: - A.核心模塊為 fs 或 http - B.自定義的文件模塊為 js 文件 ### require vs exports - require 是用來加載並執行模塊的方法 - 通過 require 方法引入後 即可在 node 中一次使用多個文件 * 比如你想在打開文件1時也執行文件2的代碼 只需要在要執行的地方加上一句 require(文件2) 即可 * 且在 require 方法中可省略後綴名 因為後綴名默認就是 .js - exports 是用來導出文件中特定對象的方式 * 在 Node.js 中沒有全局作用域 只有模塊作用域 * 意思是每個文件之間的 變量 與 函數 不會互相影響 * 只能在文件本身內部做使用 就算被引入也無法調用別人定義的 變量 與 函數 - exports 本身默認是一個空對象 我們可以通過把 變量 或 函數 放進此對象裡來使用它們 * 比如我們有兩個文件 文件1想獲取文件2中的變量 `var name = 'ZC'` * 這時候我們可以在文件2的最後加上一行 `exports.name = name` * 然後到文件1 `var file2 = require(文件2)` 使用 `file2.name` 就能獲取到 name了 * 就類似我們使用核心模塊時的方式一樣 * 通過把模塊定義為一個變量 並使用變量.模塊方法名 就能調用到模塊中的函數了 ### http 結合 fs 打開不同類型網頁 - 利用讀取文件的方式 把文件內容寫入響應中,讓頁面根據網址不同而呈現不同網頁內容(需根據文件類型設置響應頭)。 * 響應頭 content-type 設置的參考連結: http://tool.oschina.net/commons ## Node.js-template ## Node.js REPL 在終端機中輸入 `node` 直接按 `enter` 則可進入測試環境。 此環境中不需 `require` 就能測試所有核心模塊的方法,離開測試狀態只需輸入兩次 `control+C` 即可。 ## Node.js 中的第三方模塊 ### 在 Node.js 中可以使用模板引擎 使用方式為: - 1. 下載/安裝 模板引擎 > `npm install art-template` - 2. 使用 require 方法加載 art-template - 3. 使用 `template.render()` 方法 參數1為創建的模板內容 參數2為對象 ### 使用 Node.js + art-template 做一個留言本服務 - 創建一個資料夾 下載 art-template 包 - 創建一個 app.js 當開啟此服務的文件 - 創建一個 views 資料夾放網頁頁面 - 寫一個首頁頁面 和 一個發留言頁面 還有一個404頁面 把這三個頁面放入 views 資料夾 * 此時先到 app.js 寫 http 服務 並且嘗試打開首頁頁面 * 就會發現當頁面遇到所有需發起請求的標籤時 瀏覽器會自動發起請求 * 若請求為本地目錄中的文件 我們就要處理這些靜態資源的問題 * 處理方式為 創建一個 public 資料夾 把所有需要被請求拿到的本地文件放進去 * 統一通過請求地址為 /public/ 進行請求 獲取文件,這樣可以方便我們不用挨個去判斷資源的路徑 - 創建一個 json 文件放所有留言反饋 並通過讀取文件+模板引擎渲染首頁內容 * 這裏要注意切換數據類型,讀取文件、寫入文件 與 `res.end()` 方法 傳入的內容必須為字符串。 - 回到發留言的頁面中去設定表單的提交 * 這裏使用 ajax 的 GET 請求 請求網址設一個 `/send` 像這樣 > `<form action="/send" method="get">` - 回到 app.js 文件寫 html 服務 讓網址為 `/send` 時使用 `url.parse()` 方法 獲取 GET 參數 * `url.parse()` 參數1為網址 參數2為布爾值 布爾值為 true 時會幫你自動把參數轉為對象 * 然後我們把 GET 請求傳入的參數設為一個對象 把對象轉為字符串寫入 json 文件 * 再通過重定向 > `res.statusCode = 302 ; res.setHeader('Location', '/')` 把頁面跳轉回首頁 即完成。 ## Node.js 模塊系統與 express 起手 ### 模塊系統 #### 關於 exports 的深入剖析 - 我們知道在每個模塊中有一個導出對象為 exports - 但其實 exports 是 module 對象中的其中一個成員 - 只是 Node.js 為了讓代碼看起來更為簡潔 統一設置了一個變量叫 exports 並讓 exprots 等於 module.exports - 使得我們可以通過給 exprots 添加對象的同時也給 module.exports 添加對象 - 可實際上 在文件中導出的並不是 exports 而是 module.exports - 所以我們如果要導出單個成員 沒辦法使用 exports = XXX 導出 * 這裏的 exports = XXX 只是給 exports 重新賦值 讓它不再等於 module.exports 而已 - 必須使用 module.exports = XXX 才能讓導出的對象直接變成 XXX 而不再是對象 - 這樣可以讓我們不需要再通過 require 到的成員訪問其中的對象(就是不需要再 require 點來點去 可以直接使用) #### 關於 require 的加載規則 - A. 所有文件默認會優先從緩存加載: * 假設你有 a b c 三文件 你在 a 中 require b 和 c * 在 b 中又 require c 那文件執行順序為 * `a > a 中的 b > b 中的 c > b > a > 獲取 c 的 exprots` * 當執行完 b 並且回到 a 的時候就不會再次執行 c 的代碼 但會獲取 c 導出的對象 * 因為在執行 b 時 c 就被執行過了 所以回到 a 時就不會再次執行裡面代碼了 - B. 第三方模塊加載過程為: * 到文件所屬目錄下找名為 node_modules 的資料夾 * 到此資料夾下找 require 的包名的資料夾 * 再到包名資料夾中找 package.json 文件 * 再到 json 文件中找 main 屬性對應的 js 文件 * 最後找到的那個 js 文件 即為最終引入這個第三方模塊所加載的對象 - 假設在尋找的過程中有找不到的 默認就會直接加載目錄下的 index.js 文件 - 如果連 index.js 文件都不存在 就會往目前所屬目錄的上一層目錄下重新按照此過程尋找 - 再沒有就再往上一層目錄下找 直到磁盤根目錄也沒有 就會直接報錯 - C. 由於第二點的加載規則會不斷往外找 所以建議: * 在每個項目中 直接於項目的根目錄中安裝第三方模塊 不要在每個子目錄中安裝 * 這樣才不會讓所有目錄下都充滿 node_modules 的資料夾 且造成重複安裝第三方模塊 ### 關於 npm 及 package 文件 #### npm - npm 全名為 node package manager - 是 Node.js 專門用來下載第三方包的 - 安裝 Node 就等同於安裝了 npm 了 - 常用命令為: + `npm init`: 創建 package.json 文件,若有加上`--yes` 就會跳過嚮導 快速生成此文件。 * `npm i -y` + `npm install`: 安裝所有 package.json 文件中 `dependencies` 屬性的項目。 * `npm i` + `npm install 包名`: 安裝包,若有加上 `--save` 就會同時更新 package.json 文件。 * `npm i XXX -s` + `npm uninstall 包名`: 刪除包,若有加上 `--save` 就會同時更新 package.json 文件。 * `npm un XXX -s` + `npm 命令 --help`: 查詢這個命令的使用幫助。 #### package.json - package.json 文件可通過在終端機中輸入 `npm init` 手動生成 - 裡面可填寫你的項目名稱、項目描述、啟動項目的 js 文件、項目依賴的第三方包等等 * 當你使用 `npm` 安裝第三方模塊時 加上 `--save` * 它就會自動產生 `dependencies` 屬性 保存你安裝的第三方模塊的包名及版本號 - 所以建議每個項目最好都有一個 package.json 文件 幫助我們保存項目依賴的訊息 * 這樣做的目的是 當你誤刪了 node_modules 資料夾時 * 可以通過在終端機中輸入 `npm install` 自動幫你安裝所有 `dependencies` 屬性中的項目 ### Express - Express 是一個第三方 Web 開發框架 - 因核心模塊 http 在開發方面有部分的不足 所以需要使用框架來加快我們開發項目 - 在 Express 中高度封裝了 node.js 中的 http 核心模塊 讓我們能更加專注在業務上而不是底層 - Express 使用方式為: + 建立項目資料夾 + 建立 package.json 文件 + 安裝 express + 創建入口文件 app.js + 打開 app.js 引入 express + 定義一個變量 `var app = express()` 這句代碼就等同於核心模塊中的 `http.createServer()` 方法 + 設定路徑 `app.get('/',function(req,res){res.send('hello express!')})` + app.get() 方法參數1 為 url 地址,參數2 為回調函數 + express 中不再使用 res.write() 、 res.end() 而是直接使用 res.send() + express 已幫我們處理好中文亂碼的問題 響應中文時就不需要再設置請求頭了 + express 也處理了沒有設置的 url 地址會直接返回 Cannot GET /XXX + 在 express 中要處理靜態資源的方式也很簡單 一樣創建一個 public 目錄 把需要的資源存放進去 + 然後通過 `app.use('/public/', express.static('./public/'))` 公開靜態資源 + 在 express 中 可以通過 `req.query` 獲取 get 請求的參數 且直接就是對象