後端的濫觴
NODE.JS 及其應用
簡介
前端 vs 後端
- 許多軟體設計的概念
- 前端主司視覺效果
- 後端主掌資料控制
- 網頁設計又不太一樣
別稱 | 功能 | |
---|---|---|
前端 (front-end) | client 端 | 網頁排版、呈現等 |
後端 (back-end) | server 端 | 與資料庫溝通處理 |
靜態 vs 動態
- 靜態網頁:只有前端
- 動態網頁:前端 + 後端
並非一般所想有 javascript 特效就是「動態網頁」,而是網頁內容會隨資料庫改變,才稱為「動態網頁」
Node.js
執行 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:程序相關操縱
核心套件
簡介
- 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
生成網頁
做法
- 將資料讀進來
- 處理資料
- 接上 HTML
- 生成網頁
生成網頁
有很多困難點
- 寫不出來?
- 不,這不是問題
- 讀進檔案?
-
哈,這個簡單 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 資料夾
-
fs.mkdir()
-
- 看起來似乎可行 ...
- 但執行第二次又炸了!
-
原因:重複建立 public 資料夾
生成網頁
只能在第一次 mkdir ...
-
解決方法:fs.access() 檢查 public 是否存在
-
fs.constants.F_OK:常數,可檢查檔案存在與否
-
-
如果 fs.mkdir() 不好寫
-
可以改用同步的版本 fs.mkdirSync()
-
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
- 缺點
- 生成需要時間
核心套件
參考:http、url、Querystring
複習一下 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' 事件
-
執行函式 function (req, res) {}
-
req:請求內容,http.IncomingMessage物件
-
res:伺服器回應內容,http.ServerResponse物件
-
-
函式就是在觸發 '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 伺服器
var http = require('http')
http
// 建立 http 伺服器
.createServer(function (req, res) {
console.log('執行!');
res.write('Hello world!')
res.end()
})
// localhost:5566
.listen(5566)
應用
讀取網頁
- 送 GET 請求給 UVa
-
http.request()
- 送請求
- 預設為 GET
-
回傳值為http.ClientRequest 物件
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
- 常見屬性
初始化專案 & 安裝套件
- 還在用手打?
-
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