Node.js 初探

Qbane

NTUOSC 2016/3/17

CC BY-NC-SA 4.0

JavaScript 統治世界

前端的解答:React.js (我是只有略懂)

後端的解答:Node.js (今天的主角)

知道什麼是node之前

你應該對JavaScript的特性已略知一二

否則node對你來說應該像是怪物

下面寫道

最近在用 Node.js 寫一個會自動告訴你今天有什麼最新動漫。

好了,最後那根本是地獄,我發現 Node.js 的大問題就是那個 ASync,

例如你有了一個迴圈,Node.js 會先跑迴圈下面的程式,而不是迴圈執行完後才跑下面的程式。

Example

for(var i = 0; i < 100; i++) {
  $.get('http://example.com/anime?id=' + i, function(resp) {
    console.log('#' + i + ' is checked: ', resp);
  });
}

console.log('Done!');

// WTF

函數飛來飛去

function NTUOSC() { /* constructor... */ }
function peace() { /* just a plain function */ }
(function(x,y) { return x + y; })(2, 3) // 5
(x=>x*x) // ES6 "Arrow function"

Asynchronous

var completedCount = 0;
for(var i = 0; i < 100; i++) {
  $.get('http://example.com/anime?id=' + i, function(resp) {
    completedCount++;
    console.log('#' + i + ' is checked: ', resp);
    if (completedCount == 100) {
      console.log('Done!');
    }
  });
}

// :)

常常不如你想像的那樣

卻是 JavaScript 的特性之一

Event-driven

Viktor.poke = function(times) {
  if (times > 100) {
    throw new Error('Such weird!');
  }

  for (var i = 0; i < times; i++) {
    this.beingPoked();
  }
  return this.happiness;
}

Don't ever call me, let me call you <3

Viktor.poke = function(times, callback) {
  if (times > 100) {
    return callback('Such weird!', null);
  }
  
  var completed = 0;
  this.on('poke', function(err) {
    completed++;
    if (completed == times)
      callback(null, this.happiness);
  });
  
  for (var i = 0; i < times; i++)
    this.emit('poke', callback);
};

async後意外無法用try-catch捕捉
所以需要傳callback進去

callback第一個參數常常是err
null表示操作成功

事件用emit發送,用on接收
callback function在裡面傳來傳去

所以這樣到底有什麼好處

除了讓腦袋打結以外

Async / Event-Driven is good!

做為一個伺服器

常見的情形是有很多請求需要並行處理

不能因為一個人塞住整個服務

Async 讓單執行緒的程式同時做不同的事

Event-Driven 保持 (disk/network) I/O 利用有效率

JavaScript 的語言特性促成了 Node.js 的誕生

Non-blocking

可是我腦袋還是會打結怎麼辦

除了多寫讓自己熟練以外
有套件可以輔助解決這個問題

(套件名:async)

Getting Started

安裝 Node.js

Windows 下看到的畫面可能不一樣,不過應該還是可以用

就跟用 Python 一樣

有個命令列模式 (REPL)

可以直接在裏面執行JavaScript

就跟用 pip 一樣

Node.js 的套件管理器叫作 npm

可說是 JS 界最大的開源套件庫

> npm -v
3.3.9

// it depends
> mkdir node-test  # 測試用資料夾
> cd node-test     # 切進資料夾
> npm init         # 在目錄中展開一個node套件
...

Hello World

Hello World

Hello, world!
hello.js
$ node hello.js
$ node
> .load hello.js


> .exit

OR

console.log('Hello, world!');
Hello, world!
> 

←這樣會留在REPL內

↑離開

Entry Point

一個檔案就是一個套件

一個資料夾也可以是一個套件

(咦)

node 的套件以 index.js / index.json 為進入點

node .
# 這樣等價於
node index.js
# 也可能是
node index.json
# 也可能找不到而產生錯誤

require

CommonJS 規範

(還記得上星期講react的時候說到另一種套件引用機制是什麼嗎)

require('fs')     // node原生的套件
require('async')  // 相依的套件

require('./index.js')   // 相對目錄引用
require('./data.json')  // 引 JSON 檔也是可以的

相對路徑一定要用 點(.) 點點(..) 開始

module.exports

當這個 JavaScript 檔(模組) 要匯出一些內容的時候使用

匯出內容不拘

// Jandi.js
function ddosSecretFunc(ip, payloadSize) {
  // such scary, much secure...
}

module.exports = {
  id: 'JANDI',
  organization: 'H***NTU',
  birthday: NaN,
  ping:       function(ip) { /* ... */ },
  traceroute: function(ip) { /* ... */ },
  ddos:       ddosSecretFunc
};

Example

// index.js

// WRONG way to include
// var jandi = require('Jandi');

// correct way
var jandi = require('./Jandi');

console.log('This is the bot ' + jandi.id + ' speaking!');
// This is the bot JANDI speaking!

setTimeout(function() {
  jandi.ddos('127.0.0.1', 0x7fffffff);
}, 2000);

Tutorial on Built-in Modules

都還蠻低階 (low-level) 的

檔案讀寫

var fs = require('fs');

// 非同步版本
// 未指定編碼則回傳二進位內容
// Node 的 buffer 有點像 C 的 char[] 包裝過
fs.readFile('123.txt', function(err, result) {
  console.log(result);  // <Buffer 31 32 33 ...>
  // Node.js 裡的 Buffer 有點像 C 的 char[]
});

fs.readFile('123.txt', 'utf-8', function(err, result) {
  console.log(result);  // 123...
});

// 也有同步的版本
var data = fs.readFileSync('123.txt', 'utf-8');
console.log(data);  // 123...

行程操作

var process = require('process');  // 不一定需要

// 像 C 一樣可以拿一些底層的參數
// console.log('argc', process.argc);  <== wrong
console.log('argc', process.argv.length);
console.log('argv', process.argv);

// 或是直接操作標準輸出、標準錯誤輸出
process.stdout.write('Hello stdout!');
process.stderr.write('Oh stderr!');

// 標準輸入有點複雜,而且感覺蠻少用的...

建立伺服器

var http = require('http');

http
  .createServer(function(req, resp) {
    console.log('connected!');
    resp.writeHead(200, {'Content-Type': 'text/plain'});
    resp.write("Hello World");
    resp.end();
  })
  .listen(5566, '127.0.0.1');

這樣土炮網站想必非常瑣碎

所以之後會介紹一個網頁應用程式的 framework

NPM

Node Package Manager

Nearly passed Midnight

Nuclear Planning Manual

Natural Pushing Machine

Nobody Publish Monsters

如何安裝套件

  1. 先到 GitHub 或 NPM官網 找合適的套件
  2. npm install 套件名稱
  3. 等很久很久(npm實在有夠慢)
  4. 完成!
  5. 如果沒有完成,請用sudo再試一次XD

套件一般都會存在 node_modules/ 下

npm install <package>

Global  /  Local

-g

將套件安裝在系統全域目錄而不是此目錄下

--dev
--save

For building  /  developing

安裝指定的套件 或 安裝所有相依的套件

(不接參數的話)

將套件標示為開發用而不是執行用

將相依性紀錄到套件描述檔

npm uninstall <package>

Global  /  Local

-g

解除安裝系統全域目錄的而不是此目錄下的

--save

解除安裝指定的套件 或 解除安裝所有相依的套件

(沒接參數應該是不會有這個操作啦)

將相依性從套件描述檔拿掉

接下來會介紹的幾個套件

async
request
express
static
...

 

2016/3/18 AM5

Thanks for your listening!

套件教學

Made with Slides.com