後端的濫觴

NODE.JS 及其應用

簡介

前端 vs 後端

  • 許多軟體設計的概念
    • 前端主司視覺效果
    • 後端主掌資料控制
  • 網頁設計又不太一樣
別稱 功能
前端 (front-end) client 端 網頁排版、呈現等
後端 (back-end) server 端 與資料庫溝通處理

靜態 vs 動態

  • 靜態網頁:只有前端
  • 動態網頁:前端 + 後端

並非一般所想有 javascript 特效就是「動態網頁」,而是網頁內容會隨資料庫改變,才稱為「動態網頁」

Node.js

  • V8 引擎驅動的伺服器端執行環境
  • V8
    • 2008 - present
    • C++ 寫成的 javascript 編譯器
    • 開源專案
    • chromium 瀏覽器
  • 官方網站
  • 本文介紹 6.3.x

執行 Node.JS

console.log('您好! Node.JS!');
node index.js

程式碼

index.js

執行指令

Node.js 特色

require 和 module

  • 把所有的程式碼、套件視為模組
  • 方便易讀、好管理
  • 比如:hello world 魔改
  • module.exports:匯出模組
  • require:載入模組
module.exports = 'Hello world!'
var str = require('data.js');
console.log(str);
data.js
index.js

更多模組

匯出的模組可以是所有東西

module.exports = function() {
  console.log('我要抓寶可夢');
}

函數

var __poke = function() {
  console.log('我要抓寶可夢');
}

var __PI = '我是拍'

module.exports = {
  poke: __poke,
  PI: __PI
}

物件

先前開發前端時,script 引入的順序會影響,require 方法可避免掉這種狀況

全域變數

  • __dirname:當前執行 script 所在資料夾
  • __filename當前執行 script 的檔名
  • process:程序相關操縱
    • process.env:環境變數
    • process.argv:參數

核心套件

參考:fspath

簡介

  • fs 套件
    • 檔案、資料夾處理
    • 和 linux 檔案操作有 87% 像
  • path 套件
    • 處理檔案路徑
    • 不同作業系統路徑格式不同

應用

檔案操作

var fs = require('fs')

fs.writeFile('pokemon.txt', 'Pokemon 超好丸', function (err) {
  // 如果寫檔失敗會丟錯誤
  if (err) throw err;
  console.log('我寫了檔案!');
})
fs.writeFile():寫檔案

應用

生成靜態網頁

  • 若一個網頁有很多部分是重複的
  • 利用程式來生成網頁,就能事半功倍!
  • 舉例
    • 一個檔案 data
    • 將 data 每一行生成一個網頁
    • 網頁的標題與內文就是該行內容
    • 所有生成網頁在 public 資料夾下
    • 檔案取名為 line<num>.html,<num>為第幾行
Hello world!
我是許胖
第三行
data

生成網頁

做法

  1. 將資料讀進來
  2. 處理資料
  3. 接上 HTML
  4. 生成網頁

生成網頁

有很多困難點

  • 寫不出來?
    • 不,這不是問題
  • 讀進檔案?
    • 哈,這個簡單 fs.readFile()
    • 注意檔案編碼!通常是 'utf8'
  • 處理資料:分行處理
    • 還記得字串的str.split()方法嗎?
    • 換行在 windows 系統下會是 '\r\n'、linux 系統為 '\n'
Hello world!
我是許胖
第三行

生成網頁

目前為止的戰況

var fs = require('fs')

fs.readFile('data', { encoding: 'utf8' } , function (err, data) {
  if (err) throw err;
  var lines = data.split('\r\n'); // 依照行去分割
})

剩下生成網頁

接上 HTML

lines.forEach(function (line, i) {
  var content = [
    '<!DOCTYPE html>\n<html><head><title>',
    line,
    '</title></head><body>',
    line,
    '</body></html>'
  ].join(''); // 網頁內容
})

生成網頁

檔案名稱取名

  • 不同的作業系統,檔案名稱可能為
    • linux系統:public/line<num>.html
    • Windows系統:public\line<num>.html
  • 使用 path.join() 避免上述狀況
var path = require('path')

var filename = path.join('public', 'line' + (i + 1) + '.html')

生成網頁

寫入檔案

  • 發現寫不進去!
  • 原因:沒有 public 這個資料夾
  • 直覺的解法:在寫入檔案前建立 public 資料夾
  • 看起來似乎可行 ...
  • 但執行第二次又炸了!
  • 原因:重複建立 public 資料夾

生成網頁

只能在第一次 mkdir ...

  • 解決方法:fs.access() 檢查 public 是否存在
  • 如果 fs.mkdir() 不好寫
var dirname  = 'public';
// 檢查 public 是否存在
fs.access(dirname, fs.constants.F_OK, function (err) {
  if (err) { // public 不存在
    fs.mkdirSync(dirname); // 建立資料夾
    console.log('建立 ' + dirname + ' 資料夾');
  } else {
    console.log(dirname + ' 資料夾已經存在!');
  }
})

生成網頁

把上面所有東西兜起來

  • 原始碼資料分開
  • 優點
    • 不需動到原始碼,修改 data 的資料,就可以產生不同網頁
    • 節省寫網頁開發時間
    • 靜態網頁可以丟上 github
  • 缺點
    • 生成需要時間

核心套件

參考:httpurlQuerystring

複習一下 http ~

HTTP

瀏覽器

伺服器

請求 request

  • GET
  • POST
  • PUT
  • DELETE
  • ... 以此類推

回應 response

HTTP 格式

Source:wikipedia

用 GET 的方式送請求給 wikipedia 首頁

GET /wiki/Main_Page http/1.1
HOST: en.wikipedia.org

狀態碼

應用

建立 http 伺服器

  • http.createServer()
    • 回傳值是 http.Server 物件
    • 在 http 模組中,伺服器就是以此物件作用
  • 對伺服器送出請求,觸發 'request' 事件
  • 函式就是在觸發 'request' 時,根據 req 來決定送回 res 的內容
var http = require('http')

http
// 建立 http 伺服器
.createServer(function (req, res) {
  console.log('執行!');
  res.write('Hello world!')
  res.end()
})
// localhost:5566
.listen(5566)

應用

建立 http 伺服器

  • res.write():寫入回應內容
  • res.end():代表回應結尾,每次伺服器回應一定要呼叫
  • server.listen():指定綁定的埠(port)、網域等
    • 在此例中,網頁的 URL 為 localhost:5566
var http = require('http')

http
// 建立 http 伺服器
.createServer(function (req, res) {
  console.log('執行!');
  res.write('Hello world!')
  res.end()
})
// localhost:5566
.listen(5566)

應用

讀取網頁

var https = require('https')

https
// 送出請求
.request('https://uva.onlinejudge.org', function (res) {
  console.log(res.statusCode);
  console.log(res.method);
  // 網頁編碼 & 資料
  res.setEncoding('utf8');
  res.on('data', function (data) {
    console.log(data)
  })
  // 結束讀取回應
  res.on('end', function () {
    console.log('結束');
  })
})
// 錯誤控制
.on('error', function (err) {
  console.log('失敗! ' + err.message);
})
// 記得要加 end()
.end()

練習

var http = require('http')

http
// 建立 http 伺服器
.createServer(function (req, res) {
  res.write('Url: ' + req.url + ', Method: ' + req.method + '\n');
  res.write('Body: ' + req.body);
  res.end()
})
// localhost:5566
.listen(5566)

NPM

node.js 套件管理器

NPM

  • 成千上萬的套件在 npmjs.org

  • 很難找出誰好誰不好

  • 只能多看多摸索

  • 後面會介紹一些好用的套件

package.json

  • 在專案中應用 npm 的套件都要建立 package.json 檔案
    • 一個 JSON 為格式的設定檔
    • 紀錄專案資訊
  • npm 專案範例:hexo-tag-owl
  • 常見屬性
    • name:專案名稱
    • version:版本號,發布成 npm 套件才有用
    • description:專案描述
    • keywords:關鍵字,發布成 npm 套件才有用
    • author:作者
    • license:以什麼授權條款發布 (比較wiki)
    • dependencies:專案依賴套件

初始化專案 & 安裝套件

  • 還在用手打?
    • npm init
  • 安裝套件
    • npm install --save <套件名>
    • 全域套件:npm install -g <套件名>
  • npm update --save <套件名>
    • 更新套件
  • npm uninstall --save <套件名>
    • 移除套件

其他功能

  • npm set:可以在未來 npm init 中預設一些值
  • 常見的預設值
    • npm set init-author-name '<名字>'
    • npm set init-author-email '<email>'
    • npm set init-license '<授權種類>'
  • npm ls 或 npm ls <套件名>
    • 查看專案的套件

scripts

  • package.json 可以設定 scripts 屬性
  • 方便執行一些重複指令
  • 範例程式
    • 建立一個伺服器
    • 每次 request 傳回生成網頁
  • npm run build 生成網頁
  • npm start
    • npm run start 的縮寫,start 和 test 可縮寫
    • 多行指令可用 & 連接
"scripts": {
  "build": "node index.js",
  "start": "node index.js & node server.js"
}

發布套件

  • 也就是丟到 npm 給大家用程式碼
  • 一般來說,自己的專案不需要發布
  • 步驟
    • 在 npm 註冊:npm adduser
    • 登入 npm:npm login
    • 發布套件:npm publish
  • 範例:hexo-tag-owl

爬資料好幫手

簡介

  • request:簡化 http request
  • cheerio:Node.JS 版本的 jQuery

Title Text

CLI 開發利器

其他套件

Lodash

MVC 架構

最簡單的 MVC 程式

// Model
var models = {
  hello: 'Hello world!'
}
// View
var views = {
  show: function(str) {
    console.log(str)
  }
}
// Controller
var controllers = {
  run: function() {
    views.show(models.hello);
  }
}

controllers.run()
console.log('Hello world!')

改造前

改造後

太星爆了

後端的濫觴:Node.js 及其應用

By Hsu Heng Yu

後端的濫觴:Node.js 及其應用

2016 板中資訊社暑期課程 (8) CC-BY-NC-SA 4.0 by 許恆與 (a.k.a. m80126colin)

  • 125