---
# System prepended metadata

title: Node.js
tags: [Node.js]

---

---
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 請求的參數 且直接就是對象