├── README ├── _static └── cover.png ├── cover.tex ├── images ├── arrow.eps ├── global.eps ├── nodejs.eps ├── professional.eps ├── tree.eps └── zh-tw │ ├── node_basic_file_byte.png │ ├── node_basic_file_read.png │ ├── node_basic_rout_test.png │ ├── node_basic_rout_url.png │ ├── node_basic_rout_url_default.png │ ├── node_basic_rout_url_error.png │ ├── node_basic_rout_url_index.png │ ├── node_basic_rout_url_test.png │ ├── node_install_cmd.jpg │ ├── node_install_linux_node_test.jpg │ ├── node_install_node_test.jpg │ ├── node_npm_linux_install.jpg │ ├── node_npm_linux_test.jpg │ ├── node_npm_registry.png │ ├── node_npm_search.png │ └── node_npm_test.png ├── index.rst ├── readme.rst ├── src ├── hello.js ├── node_basic_file_http_static.js ├── node_basic_file_simple.js ├── node_basic_http_get.js ├── node_basic_http_hello_world.js ├── node_basic_http_rout.js ├── node_basic_http_rout_done.js └── static │ ├── index.html │ ├── static.html │ └── test.html ├── watchr.rb └── zh-tw ├── convetion.rst ├── node_basic.rst ├── node_install.rst ├── node_introduce.rst ├── node_javascript.rst ├── node_npm.rst └── outline.rst /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/README -------------------------------------------------------------------------------- /_static/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/_static/cover.png -------------------------------------------------------------------------------- /cover.tex: -------------------------------------------------------------------------------- 1 | \documentclass[b5paper,12pt]{article} 2 | \usepackage[margin=0in]{geometry} 3 | 4 | \usepackage[cm-default]{fontspec} 5 | %% \usepackage{xunicode} 6 | \usepackage{xcolor} 7 | \usepackage{fontspec} 8 | \usepackage[slantfont,boldfont,CJKaddspaces,CJKchecksingle,CJKnumber,fallback]{xeCJK} 9 | \XeTeXlinebreaklocale "zh" 10 | \XeTeXlinebreakskip = 0pt plus 1pt 11 | \defaultfontfeatures{Mapping=tex-text} 12 | \setCJKmainfont[BoldFont={Adobe Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 13 | \setCJKsansfont[BoldFont={Adobe Fan Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 14 | \setCJKmonofont{Adobe Ming Std} 15 | \setCJKromanfont[BoldFont={Adobe Heiti Std},ItalicFont={Adobe Fangsong Std},BoldItalicFont={Adobe Kaiti Std}]{Adobe Song Std} 16 | \setmainfont{Adobe Garamond Pro} 17 | \setsansfont{Myriad Pro} 18 | \setmonofont{Courier Std} 19 | \setromanfont{Adobe Garamond Pro} 20 | 21 | \usepackage{graphicx} 22 | \usepackage{pstricks} 23 | \definecolor{pink}{rgb}{1,0.7,0.7} 24 | \definecolor{main}{rgb}{0.55,0.78,0.29} 25 | \let\psgrid\relax 26 | 27 | \begin{document} 28 | \thispagestyle{empty} 29 | 30 | \noindent 31 | \begin{pspicture}(\linewidth,\pdfpageheight) 32 | \psgrid 33 | 34 | \psline[linewidth=3mm,linecolor=main](0,0.99\pdfpageheight)(\linewidth,0.99\pdfpageheight) 35 | 36 | \rput(0.5\linewidth,0.96\pdfpageheight){\textsf{\textbf{\fontsize{14pt}{14pt}\selectfont Node.js Taiwan 社群強力推薦}}} 37 | 38 | \rput(\linewidth,\pdfpageheight) 39 | {\pspolygon*(-3.6,0)(-1.4,0)(0,-1.4)(0,-3.6)} 40 | \rput(\linewidth,\pdfpageheight) 41 | {\rput{-45}(-1,-1){\Large\textbf{\white 實戰}}} 42 | \rput(\linewidth,\pdfpageheight) 43 | {\rput{-45}(-1.5,-1.5){\Large\textbf{\white 程式設計}}} 44 | 45 | \psframe[linecolor=main,fillcolor=main,fillstyle=solid](0,13.5)(\linewidth,17.5) 46 | \psline[linewidth=0.3in,linecolor=main](0,12.7)(\linewidth,12.7) 47 | \rput[l](1.5,17.5){\textsl{\fontsize{36pt}{36pt}\selectfont The}} 48 | \rput[l](3.6,15.4){\fontsize{96pt}{96pt}\selectfont Node.js} 49 | \rput[l](3.5,15.5){\fontsize{96pt}{96pt}\selectfont\white Node.js} 50 | \rput[l](12,13.6){\textsl{\fontsize{36pt}{36pt}\selectfont Little Book}} 51 | 52 | \rput[l](0.75\linewidth,11){\textsf{\textbf{\fontsize{36pt}{36pt}\selectfont 中文版}}} 53 | 54 | \rput[b](0.75\linewidth,19) 55 | {\includegraphics[scale=1]{images/nodejs}} 56 | 57 | \rput[b](0.18\linewidth,6.5) 58 | {\includegraphics[scale=2.5]{images/professional}} 59 | 60 | \rput[rb](0.95\linewidth,5){ 61 | \minipage{\linewidth}\raggedleft\huge{\textsf{\textbf{\fontsize{14pt}{28pt}\selectfont 62 | 適用最新版 Node.js 0.6.7\\ 63 | 本書由 Node.js Taiwan 社群程式設計師編寫\\ 64 | 最快樂的 Node.js 入門學習指南 65 | }}}\endminipage} 66 | 67 | \rput[b](3.6,1) 68 | {\includegraphics[scale=0.4]{images/tree}} 69 | \rput[b](3.4,0.3){\textsf{\textbf{\fontsize{28pt}{28pt}\selectfont ContPub Press}}} 70 | 71 | \rput[b](0.85\linewidth,0.2){\textsl{\fontsize{22pt}{22pt}\selectfont Node.js Taiwan}} 72 | 73 | \end{pspicture} 74 | 75 | \end{document} 76 | -------------------------------------------------------------------------------- /images/arrow.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/arrow.eps -------------------------------------------------------------------------------- /images/global.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/global.eps -------------------------------------------------------------------------------- /images/nodejs.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/nodejs.eps -------------------------------------------------------------------------------- /images/professional.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/professional.eps -------------------------------------------------------------------------------- /images/tree.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/tree.eps -------------------------------------------------------------------------------- /images/zh-tw/node_basic_file_byte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_file_byte.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_file_read.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_file_read.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_test.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_url.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_url_default.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_url_error.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_url_index.png -------------------------------------------------------------------------------- /images/zh-tw/node_basic_rout_url_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_basic_rout_url_test.png -------------------------------------------------------------------------------- /images/zh-tw/node_install_cmd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_install_cmd.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_install_linux_node_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_install_linux_node_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_install_node_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_install_node_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_linux_install.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_npm_linux_install.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_linux_test.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_npm_linux_test.jpg -------------------------------------------------------------------------------- /images/zh-tw/node_npm_registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_npm_registry.png -------------------------------------------------------------------------------- /images/zh-tw/node_npm_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_npm_search.png -------------------------------------------------------------------------------- /images/zh-tw/node_npm_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nodejs-tw/nodejs-little-book/afce2303a1fb466532ffab7431a6fb07c65eed20/images/zh-tw/node_npm_test.png -------------------------------------------------------------------------------- /index.rst: -------------------------------------------------------------------------------- 1 | .. Settings 2 | @title: The Little Node.js Book 3 | @authors: clonn, lyhcode 4 | @basename: the-little-nodejs-book-zh-tw 5 | @language: zh_TW 6 | @latex_parindent: 2em 7 | @latex_paper_size: b5 8 | @latex_font_size: 11pt 9 | @latex_logo: images/nodejs.eps 10 | @latex_contentsname: 目錄 11 | @pdf_cover: cover.tex 12 | @epub_cover: _static/cover.png 13 | @epub_theme: epub_simple 14 | @mobi_theme: mobi_simple 15 | 16 | .. raw:: latex 17 | 18 | \renewcommand{\chaptername}{} 19 | %% \renewcommand{\chaptermark}[1]{\markboth{~第~\thechapter~章~~~#1~}{}} 20 | %% \renewcommand{\sectionmark}[1]{\markright{~\thesection~~#1~}{}} 21 | \renewcommand\bibname{參考資料} 22 | 23 | ####################### 24 | The Little Node.js Book 25 | ####################### 26 | 27 | .. toctree:: 28 | :maxdepth: 3 29 | 30 | readme 31 | zh-tw/node_introduce 32 | zh-tw/node_javascript 33 | zh-tw/node_install 34 | zh-tw/node_basic 35 | zh-tw/node_npm 36 | -------------------------------------------------------------------------------- /readme.rst: -------------------------------------------------------------------------------- 1 | .. raw:: latex 2 | 3 | \setcounter{secnumdepth}{-1} 4 | 5 | ******** 6 | 關於本書 7 | ******** 8 | 9 | 本書為 The Little Node.js Book 正體中文版。 10 | 11 | 授權 12 | ==== 13 | 14 | The Little Node.js Book 採用創用CC姓名標示-非商業性授權。\ 15 | **您不必為本書付費。** 16 | 17 | The Little Node.js Book book is licensed under the 18 | Attribution-NonCommercial 3.0 Unported license. **You should not have 19 | paid for this book.** 20 | 21 | 基本上您可以複製、散佈、修改或播放本書,\ 22 | 但請務必尊重原作者的著作權,\ 23 | 勿將本書用於商業用途。 24 | 25 | 您可以在以下網址取得授權條款全文。 26 | 27 | http://creativecommons.org/licenses/by-nc/3.0/legalcode 28 | 29 | 關於作者 30 | ======== 31 | 32 | * Caesar Chi (clonn) 33 | * Fillano Feng (fillano) 34 | * Kyle Lin (lyhcode) 35 | 36 | 致謝 37 | ==== 38 | 39 | 最新版本 40 | ======== 41 | 42 | 本書最新的原始碼(中文版)網址如下: 43 | 44 | http://github.com/nodejs-tw/the-little-nodejs-book 45 | 46 | **** 47 | 前言 48 | **** 49 | 50 | nodeJS 是採用javascript 開發的後端程式平台,由0.4.x至目前v0.6.0版本核心上已經有許多變更,當然目的只有一個就是讓開發變得更簡單,速度能夠更快,這是所有人奮鬥的目標。而nodeJS 華文維基平台,主力由nodeJS.tw 一群喜好javascript 開發者共同主筆,內文以中文為主,希望能夠降低開發者學習門檻,藉由我們拋磚引玉,讓更多華人開發者共同學習、討論。 51 | 52 | 53 | .. raw:: latex 54 | 55 | \setcounter{secnumdepth}{1} 56 | 57 | -------------------------------------------------------------------------------- /src/hello.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | http.createServer(function (req, res) { 3 | res.writeHead(200, {'Content-Type': 'text/plain'}); 4 | res.end('Hello World\n'); 5 | }).listen(1337, "127.0.0.1"); 6 | console.log('Server running at http://127.0.0.1:1337/'); 7 | -------------------------------------------------------------------------------- /src/node_basic_file_http_static.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | fs = require("fs"), 6 | folderPath = "static", 7 | url = require('url'), 8 | path, 9 | filePath, 10 | encode = "utf8"; 11 | 12 | server = http.createServer(function (req, res) { 13 | path = url.parse(req.url); 14 | filePath = folderPath + path.pathname; 15 | 16 | fs.readFile(filePath, encode, function(err, file) { 17 | if (err) { 18 | res.writeHead(404, {'Content-Type': 'text/plain'}); 19 | res.end(); 20 | return; 21 | } 22 | 23 | res.writeHead(200, {'Content-Type': 'text/application'}); 24 | res.write(file); 25 | res.end(); 26 | }); 27 | }); 28 | 29 | server.listen(port, ip); 30 | 31 | console.log("Server running at http://" + ip + ":" + port); 32 | 33 | -------------------------------------------------------------------------------- /src/node_basic_file_simple.js: -------------------------------------------------------------------------------- 1 | var fs = require("fs"), 2 | filename = "static/index.html", 3 | encode = "utf8"; 4 | 5 | fs.readFile(filename, encode, function(err, file) { 6 | console.log(file); 7 | }); 8 | 9 | -------------------------------------------------------------------------------- /src/node_basic_http_get.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | qs = require('querystring'), 6 | url = require('url'); 7 | 8 | server = http.createServer(function (req, res) { 9 | var path = url.parse(req.url), 10 | parameter = qs.parse(path.query); 11 | 12 | console.dir(parameter); 13 | 14 | res.writeHead(200, {'Content-Type': 'text/plain'}); 15 | res.write('Browser test GET parameter\n'); 16 | res.end(); 17 | }); 18 | 19 | server.listen(port, ip); 20 | 21 | console.log("Server running at http://" + ip + ":" + port); 22 | -------------------------------------------------------------------------------- /src/node_basic_http_hello_world.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'); 5 | 6 | server = http.createServer(function (req, res) { 7 | res.writeHead(200, {'Content-Type': 'text/plain'}); 8 | res.end('Hello World\n'); 9 | }); 10 | 11 | server.listen(port, ip); 12 | 13 | console.log("Server running at http://" + ip + ":" + port); 14 | -------------------------------------------------------------------------------- /src/node_basic_http_rout.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | url = require('url'); 6 | 7 | server = http.createServer(function (req, res) { 8 | console.log(req.url); 9 | res.writeHead(200, {'Content-Type': 'text/plain'}); 10 | res.end('hello world\n'); 11 | }); 12 | 13 | server.listen(port, ip); 14 | 15 | console.log("Server running at http://" + ip + ":" + port); 16 | -------------------------------------------------------------------------------- /src/node_basic_http_rout_done.js: -------------------------------------------------------------------------------- 1 | var server, 2 | ip = "127.0.0.1", 3 | port = 1337, 4 | http = require('http'), 5 | url = require('url'), 6 | path; 7 | 8 | server = http.createServer(function (req, res) { 9 | path = url.parse(req.url); 10 | 11 | res.writeHead(200, {'Content-Type': 'text/plain'}); 12 | 13 | switch (path.pathname) { 14 | case "/index": 15 | res.end('I am index.\n'); 16 | break; 17 | case "/test": 18 | res.end('this is test page.\n'); 19 | break; 20 | default: 21 | res.end('default page.\n'); 22 | break; 23 | } 24 | }); 25 | 26 | server.listen(port, ip); 27 | 28 | console.log("Server running at http://" + ip + ":" + port); 29 | -------------------------------------------------------------------------------- /src/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js index html file 6 | 7 | 8 |

node.js index html file

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/static/static.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js static html file 6 | 7 | 8 |

node.js static html file

9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/static/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | node.js test html file 6 | 7 | 8 |

node.js test html file

9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /watchr.rb: -------------------------------------------------------------------------------- 1 | watch( '(.*)\.rst' ) {|md| system("sphinx-cook -f pdf .") } 2 | -------------------------------------------------------------------------------- /zh-tw/convetion.rst: -------------------------------------------------------------------------------- 1 | ********** 2 | 書籍寫作規範 3 | ********** 4 | 5 | 專有名詞 6 | ======= 7 | 8 | 技術類專有名詞依循官方常見用法。 9 | 10 | * Node.js 11 | * npm 12 | * JavaScript 13 | 14 | 標點符號 15 | ======= 16 | 17 | 以中文全形標點符號為主。 18 | 19 | * ,、。!「」() 20 | 21 | 遇英文段落(整句)採用一般(半形)標點符號。 22 | 23 | 中文與英文單字之間以一個空白字元隔開,標點符號與英文單字之間不需要空隔。 24 | 25 | 這是為了讓排版顯示的自動斷詞斷句可以正確運作,以及增加中英文混雜段落的閱讀舒適。 26 | 27 | * Node.js 是一種適合用於 Server-side 的開發框架(Framework),相當 Nice! 28 | 29 | 特殊排版說明 30 | ========== 31 | 32 | 中文同一段落為了方便編輯,可以將句子斷行;\ 33 | 但行尾必須加上半形「\」倒斜線符號。\ 34 | 因為在 HTML、EPUB 或 MOBI 格式中,\ 35 | 換行字元會被當做空白字元處理,導致增加過多影響版面美觀的空隔。 36 | 37 | 38 | 程式碼 39 | ===== 40 | 41 | 只有一行、不需要 Highlight,或是一般的 command line 操作指令說明,\ 42 | 使用 reStructuredText 標準 Code 寫法。 43 | 44 | :: 45 | 46 | This is line One 47 | This is line Two 48 | 49 | 片段程式碼(snippets),使用 inline code block,並指定 language type。 50 | 51 | 程式碼與 ``.. code-block`` 之間一定要隔一個空行,且一定要有程式碼(否則文件無法編譯)。 52 | 53 | .. code-block:: javascript 54 | 55 | if (something) { 56 | alert('test'); 57 | } 58 | 59 | .. literalinclude:: src/hello.js 60 | :language: javascript 61 | -------------------------------------------------------------------------------- /zh-tw/node_basic.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Node.js 基礎 3 | ************ 4 | 5 | 前篇文章已經由介紹、安裝至設定都有完整介紹,nodeJS 內部除了javascript 常用的函式(function)、物件(object)之外,也有許多不同的自訂物件,nodeJS 預設建立這些物件為核心物件,是為了要讓開發流程更為,這些資料在官方文件已經具有許多具體說明。接下來將會介紹在開發nodeJS 程式時常見的物件特性與使用方法。 6 | 7 | node.js http 伺服器建立 8 | ======================= 9 | 10 | 在`node.js官方網站 `裡面有舉一個最簡單的HTTP 伺服器建立,一開始初步就是建立一個伺服器平台,讓node.js 可以與瀏覽器互相行為。每種語言一開始的程式建立都是以 Hello world 開始,最初也從Hello world 帶各位進入node.js 的世界。 11 | 12 | 輸入以下程式碼,儲存檔案為 node_basic_http_hello_world.js 13 | 14 | .. literalinclude:: ../src/node_basic_http_hello_world.js 15 | :language: javascript 16 | 17 | 程式碼解講,一開始需要有幾個基本的變數。 18 | * ip: 機器本身的ip 位置,因為使用本地端,因此設定為127.0.0.1 19 | * port: 需要開通的阜號,通常設定為http port 80,因範例不希望與基本port 相衝,隨意設定為1337 20 | 21 | 在node.js 的程式中,有許多預設的模組可以使用,因此需要使用require 方法將模組引入,在這邊我們需要使用http這個模組,因此將http載入。Http 模組裡面內建有許多方法可以使用,這邊採用createServer 創建一個基本的http 伺服器,再將http 伺服器給予一個server 變數。裡面的回呼函式(call back function)可以載入http 伺服器的資料與回應方法(request, response)。在程式裡面就可以看到我們直接回應給瀏覽器端所需的 Header,回應內容。 22 | 23 | .. code-block:: javascript 24 | 25 | res.writeHead(200, {'Content-Type': 'text/plain'}); 26 | res.end('Hello World\n'); 27 | 28 | Http 伺服器需要設定port, ip,在最後需要設定Http 監聽,需要使用到 listen 事件,監聽所有Http 伺服器行為。 29 | 30 | .. code-block:: javascript 31 | 32 | http.listen(port, ip); 33 | 34 | 所有事情都完成之後,需要確認伺服器正確執行因此使用console,在javascript 裡就有這個原生物件,console所印出的資料都會顯示於node.js 伺服器頁面,這邊印出的資料並不會傳送到使用者頁面上,之後許多除壞(debug) 都會用到console 物件。 35 | 36 | .. code-block:: javascript 37 | 38 | console.log("Server running at http://" + ip + ":" + port); 39 | 40 | node.js http 路徑建立 41 | ===================== 42 | 43 | 前面已經介紹如何建立一個簡單的http 伺服器,接下來本章節將會介紹如何處理伺服器路徑(rout) 問題。 44 | 在http的協定下所有從瀏覽器發出的要求(request) 都需要經過處理,路徑上的建立也是如此。 45 | 46 | 路徑就是指伺服器 ip 位置,或者是網域名稱之後,對於伺服器給予的要求。修改剛才的hello world 檔案,修改如下。 47 | 48 | .. code-block:: javascript 49 | 50 | server = http.createServer(function (req, res) { 51 | console.log(req.url); 52 | res.writeHead(200, {'Content-Type': 'text/plain'}); 53 | res.end('hello world\n'); 54 | }); 55 | 56 | 重新啟動node.js 程式後,在瀏覽器端測試一下路徑行為,結果如下圖, 57 | 58 | .. image:: ../images/zh-tw/node_basic_rout_test.png 59 | :scale: 100% 60 | :align: center 61 | 62 | 當在瀏覽器輸入http://127.0.0.1:1337/test ,在伺服器端會收到兩個要求,一個是我們輸入的/test 要求,另外一個則是 /favicon.ico。 63 | /test 的路徑要求,http 伺服器本身需要經過程式設定才有辦法回應給瀏覽器端所需要的回應,在伺服器中所有的路徑要求都是需要被解析才有辦法取得資料。 64 | 從上面解說可以了解到在node.js 當中所有的路徑都需要經過設定,未經過設定的路由會讓瀏覽器無法取得任何資料導致錯誤頁面的發生,底下將會解說如何設定路由,同時避免發生錯誤情形。 65 | 先前node.js 程式需要增加一些修改,才能讓使用者透過瀏覽器,在不同路徑時有不同的結果。根據剛才的程式做如下的修改, 66 | 67 | .. literalinclude:: ../src/node_basic_http_rout_done.js 68 | :language: javascript 69 | 70 | 程式做了片段的修改,首先載入url 模組,另外增加一個path 變數。 71 | url 模組就跟如同他的命名一般,專門處理url 字串處理,裡面提供了許多方法來解決路徑上的問題。 72 | 因為從瀏覽器發出的要求路徑可能會帶有多種需求,或者GET 參數組合等。因此我們需要將路徑單純化,取用路徑部分的資料即可,例如使用者可能會送出 http://127.0.0.1:1337/test?send=1 ,如果直接信任**req.url** 就會收到結果為 /test?send=1 ,所以需要透過url 模組的方法將路徑資料過濾。 73 | 74 | 在這邊使用url.parse 的方法,裡面帶入網址格式資料,會回傳路徑資料。為了後需方便使用,將回傳的資料設定到path 變數當中。在回傳的路徑資料,裡面包含資訊,如下圖, 75 | 76 | .. image:: ../images/zh-tw/node_basic_rout_url.png 77 | :scale: 100% 78 | :align: center 79 | 80 | 這邊只需要使用單純的路徑要求,直接取用path.pathname ,就可以達到我們的目的。 81 | 82 | 最後要做路徑的判別,在不同的路徑可以指定不同的輸出,在範例中有三個可能結果,第一個從瀏覽器輸入/index 就會顯示 index 結果, /test 就會呈現出 test 頁面,最後如果都不符合預期的輸入會直接顯示 default 的頁面,最後的預防可以讓瀏覽器不會出現非預期結果,讓程式的可靠性提昇,底下為測試結果。 83 | 84 | .. image:: ../images/zh-tw/node_basic_rout_url_index.png 85 | :scale: 100% 86 | :align: center 87 | 88 | .. image:: ../images/zh-tw/node_basic_rout_url_test.png 89 | :scale: 100% 90 | :align: center 91 | 92 | .. image:: ../images/zh-tw/node_basic_rout_url_default.png 93 | :scale: 100% 94 | :align: center 95 | 96 | .. image:: ../images/zh-tw/node_basic_rout_url_error.png 97 | :scale: 100% 98 | :align: center 99 | 100 | node.js 檔案讀取 101 | ================ 102 | 103 | 前面已經介紹如何使用路由(rount)做出不同的回應,實際應用只有在瀏覽器只有輸出幾個文字資料總是不夠的,在本章節中將介紹如何使用檔案讀取,輸出檔案資料,讓使用者在前端瀏覽器也可以讀取到完整的html, css, javascript 檔案輸出。 104 | 105 | 檔案管理最重要的部分就是`File system ` 這個模組,此模組可以針對檔案做管理、監控、讀取等行為,裡面有許多預設的方法,底下是檔案輸出的基本範例,底下會有兩個檔案,第一個是靜態html 檔案, 106 | 107 | .. literalinclude:: ../src/static/index.html 108 | :language: javascript 109 | 110 | 另一個為node.js 程式, 111 | 112 | .. literalinclude:: ../src/node_basic_file_simple.js 113 | :language: javascript 114 | 115 | 116 | 一開始直接載入**file system 模組**,載入名稱為 **fs** 。讀取檔案主要使用的方法為readFile ,裡面以三個參數 **路徑(file path)** , **編碼方式(encoding)** , **回應函式(callback)** ,路徑必須要設定為靜態html 所在位置,才能指定到正確的檔案。靜態檔案的編碼方式也必須正確,這邊使用靜態檔案的編碼為 **utf8** ,如果編碼設定錯誤,node.js 讀取出來檔案結果會使用 byte raw 格式輸出,如果 **錯誤編碼格式,會導致輸出資料為 byte raw** 117 | 118 | .. image:: ../images/zh-tw/node_basic_file_byte.png 119 | :scale: 100% 120 | :align: center 121 | 122 | **回應函式** 中裡面會使用兩個變數,error 為錯誤資訊,如果讀取的檔案不存在,或者發生錯誤,error 數值會是 true ,如果成功讀取資料 error 將會是 false 。 content 則是檔案內容,資料讀取後將會把資料全數丟到content 這個變數當中。 123 | 124 | 最後程式的輸出結果畫面如下, 125 | 126 | .. image:: ../images/zh-tw/node_basic_file_read.png 127 | :scale: 100% 128 | :align: center 129 | 130 | 131 | node.js http 靜態檔案輸出 132 | ======================== 133 | 134 | 前面已經了解如何讀取本地端檔案,接下來將配合http 伺服器路由,讓每個路由都能夠輸出相對應的靜態 html 檔案。 135 | 首先新增加幾個靜態html 檔案, 136 | 137 | .. literalinclude:: ../src/static/index.html 138 | :language: javascript 139 | 140 | 141 | .. literalinclude:: ../src/static/test.html 142 | :language: javascript 143 | 144 | 145 | .. literalinclude:: ../src/static/static.html 146 | :language: javascript 147 | 148 | 準備一個包含基本路由功能的http 伺服器 149 | 150 | .. code-block:: javascript 151 | 152 | var server, 153 | ip = "127.0.0.1", 154 | port = 1337, 155 | http = require('http'), 156 | url = require('url'); 157 | 158 | server = http.createServer(function (req, res) { 159 | var path = url.parse(req.url); 160 | }); 161 | 162 | server.listen(port, ip); 163 | 164 | console.log("Server running at http://" + ip + ":" + port); 165 | 166 | 加入 **file system** 模組, 使用 **readFile** 的功能,將這一段程式放置於createServer 的回應函式中。 167 | 168 | .. code-block:: javascript 169 | 170 | fs.readFile(filePath, encode, function(err, file) { 171 | }); 172 | 173 | readFile 的回應函式裡面加入頁面輸出,讓瀏覽器可以正確讀到檔案,在這邊我們設定讀取的檔案為 html 靜態檔案,所以 Content-type 設定為 **text/html** 。讀取到檔案的內容,將會正確輸出成 html 靜態檔案。 174 | 175 | .. code-block:: javascript 176 | 177 | fs.readFile(filePath, encode, function(err, file) { 178 | res.writeHead(200, {'Content-Type': 'text/html'}); 179 | res.write(file); 180 | res.end(); 181 | }); 182 | 183 | 到這邊為止基本的程式內容都已經完成,剩下一些細節的調整。首先路徑上必須做調整,目前的靜態檔案全部都放置於 **static 資料夾** 底下,設定一個變數來記住資料夾位置。 184 | 185 | 接著將瀏覽器發出要求路徑與資料夾組合,讀取正確html 靜態檔案。使用者有可能會輸入錯誤路徑,所以在讀取檔案的時候要加入錯誤處理,同時回應 **404** 伺服器無法正確回應的 http header 格式。 186 | 187 | 加入這些細節的修改,一個基本的http 靜態 html 輸出伺服器就完成了,完整程式碼如下, 188 | 189 | .. literalinclude:: ../src/node_basic_file_http_static.js 190 | :language: javascript 191 | 192 | 193 | node.js http GET 資料擷取 194 | ======================== 195 | 196 | http 伺服器中,除了路由之外另一個最常使用的方法就是擷取GET 資料。本單元將會介紹如何透過基本http 伺服器擷取瀏覽器傳來的要求,擷取GET 資料。 197 | 198 | 在http 協定中,GET 參數都是藉由URL 從瀏覽器發出要求送至伺服器端,基本的傳送網址格式可能如下, 199 | 200 | :: 201 | 202 | http://127.0.0.1/test?send=1&test=2 203 | 204 | 上面這段網址,裡面的GET 參數就是 send 而這個變數的數值就為 1,如果想要在http 伺服器取得GET 資料,需要在瀏覽器給予的要求(request)做處理, 205 | 206 | 首先需要載入 **query string** 這個模組,這個模組主要是用來將字串資料過濾後,轉換成 **javascript 物件**。 207 | 208 | 209 | .. code-block:: javascript 210 | 211 | qs = require('querystring'); 212 | 213 | 接著在第一階段,利用url 模組過濾瀏覽器發出的URL 資料後,將回應的物件裡面的 query 這個變數,是一個字串值,資料過濾後如下, 214 | 215 | .. 216 | 217 | send=1&test=2 218 | 219 | 透過 query string ,使用parse 這個方法將資料轉換成javascript 物件,就表示 GET 的資料已經被伺服器端正式擷取下來, 220 | 221 | .. code-block:: javascript 222 | 223 | path = url.parse(req.url); 224 | parameter = qs.parse(path.query); 225 | 226 | 整個node.js http GET 參數完整擷取程式碼如下, 227 | 228 | .. literalinclude:: ../src/node_basic_http_get.js 229 | :language: javascript 230 | 231 | 程式運作之後,由瀏覽器輸入要求網址之後,node.js 伺服器端回應資料為, 232 | 233 | :: 234 | 235 | Server running at http://127.0.0.1:1337 236 | { send: '1', test: '1' } 237 | 238 | 239 | 240 | 本章結語 241 | ======= 242 | 243 | 前面所解說的部份,一大部分主要是處理 http 伺服器基本問題,雖然在某些部分有牽扯到http 伺服器基本運作原理,主要還是希望可以藉由這些基本範例練習node.js ,練習回應函式與語法串接的特點,習慣編寫javascript 風格程式。當然直接這樣開發node.js 是非常辛苦的,接下來在模組實戰開發的部份將會介紹特定的模組,一步一步帶領各位從無到有進行node.js 應用程式開發。 244 | 245 | -------------------------------------------------------------------------------- /zh-tw/node_install.rst: -------------------------------------------------------------------------------- 1 | ****************** 2 | Node.js 安裝與設定 3 | ****************** 4 | 5 | 本篇將講解如何在各個不同OS建立NodeJS 環境,目前NodeJS 0.4.8版本環境架設方式需依賴Linux指令才可編譯完成,當然在不同作業系統中也已經有NodeJS package,可以直接使用指令快速架設。以下各不同作業系統解說如何安裝NodeJS。 6 | 7 | Linux base 8 | ========== 9 | 10 | 11 | .. topic:: Ubuntu Linux 使用 apt-get 安裝 Node.js 12 | 13 | :: 14 | 15 | sudo apt-get install python-software-properties 16 | sudo add-apt-repository ppa:chris-lea/node.js-devel 17 | sudo apt-get update 18 | sudo apt-get nodejs 19 | 20 | 21 | Linux 很適合作為 NodeJS 的伺服器作業系統及開發環境。安裝前,請先確認以下套件已正確安裝。 22 | 23 | * curl (wget) 用來下載檔案的工具 24 | * git 先進的版本控制工具 25 | * g++ GNU C++ 軟體編譯工具 26 | * make GNU 軟體專案建置工具 27 | 28 | 安裝指令如下,如設有權限問題,請在指令前面加上sudo 29 | 30 | .. code-block:: javascript 31 | 32 | git clone https://github.com/joyent/node.git 33 | cd node 34 | git checkout v0.6.7 35 | ./configure 36 | make 37 | sudo make install 38 | 39 | 接著測試nodeJS 是否正常執行 40 | 41 | .. code-block:: javascript 42 | 43 | node --version 44 | 45 | 出現版本訊息即表示安裝成功。 46 | 47 | .. image:: ../images/zh-tw/node_install_linux_node_test.jpg 48 | :scale: 100% 49 | :align: center 50 | 51 | Windows 52 | ======= 53 | 54 | nodeJS 在v0.6.0版本之後開始正式支援windows native,直接使用node.exe 就可以執行程式,支援性完全與linux 相同,更棒的部份就是不需經過編譯,經過下載之後,簡單設定完成,立即開發node 程式。 55 | 56 | `下載node.js 安裝檔案 ` 57 | 58 | 如此完成windows native node.exe 安裝,接著可以進入command line 執行測試。在command line 輸入"node",會出現node.js 版本訊息畫面,表示安裝完成。 59 | 60 | .. image:: ../images/zh-tw/node_install_cmd.jpg 61 | :scale: 100% 62 | :align: center 63 | 64 | .. image:: ../images/zh-tw/node_install_node_test.jpg 65 | :scale: 100% 66 | :align: center 67 | -------------------------------------------------------------------------------- /zh-tw/node_introduce.rst: -------------------------------------------------------------------------------- 1 | ************ 2 | Node.js 簡介 3 | ************ 4 | 5 | Node.js 是一個高效能、易擴充的網站應用程式開發框架 (Web Application Framework) 。它誕生的原因,是為了讓開發者能夠更容易開發高延展性的網路服務,不需要經過太多複雜的調校、效能調整及程式修改,就能滿足網路服務在不同發展階段對效能的要求。 6 | 7 | Ryan Dahl 是 NodeJS 的催生者,目前任職於 Joyent (主機託管服務公司) 。他開發 NodeJS 的目的,就是希望能解決 Apache 在連線數量過高時,緩衝區 (buffer) 和系統資源會很快被耗盡的問題,希望能建立一個新的開發框架以解決這個問題。因此嘗試使用效能十分優秀的 V8 JavaScript Engine ,讓網站開發人員熟悉的 JavaScript 程式語言,也能應用於後端服務程式的開發,並且具有出色的執行效能。 8 | 9 | JavaScript 是功能強大的物件導向程式語言,但是在 JavaScript 的官方規格中,主要是定義網頁(以瀏覽器為基礎)應用程式需要的應用程式介面 (API) ,對 JavaScript 程式的應用範圍有所侷限。為使 JavaScript 能夠在更多用途發展, CommonJS 規範一組標準函式庫 (standard library) ,使 JavaScript 的應用範圍能夠和 Ruby 、 Python 及 Java 等語言同樣豐富。撰寫 NodeJS 的 JavaScript 程式碼,符合 CommonJS 規範,可以使用 CommonJS API 為基礎開發程式,並且在不同的 CommonJS 兼容 (compliant) JavaScript 執行環境中,程式碼具有可攜性。 10 | 11 | 瀏覽器的 JavaScript 與實現 CommonJS 規範的 NodeJS 有何不同呢?瀏覽器的 JavaScript 提供 XMLHttpRequest ,讓程式可以和網頁伺服器建立資料傳輸連線,但這通常只能適用於網站開發的需求,因為我們只能用 XMLHttpRequest 與網頁伺服器通訊,卻無法利用它建立其他類型如 Telnet / FTP / NTP 的伺服器通訊。如果我們想開發網路服務程式,例如 SMTP 電子郵件伺服器,就必須使用 Sockets 建立 TCP (某些服務則用 UDP) 監聽及連線,其他程式語言如 PHP 、 Java 、 Python 、 Perl 及 Ruby 等,在標準開發環境中皆有提供 Sockets API ,而瀏覽器的 JavaScript 基於安全及貼近網站設計需求的考量下,並未將 Sockets 列入標準函式庫之中。 CommonJS 的規範填補這種基礎函式庫功能的空缺,遵循 CommonJS 規範的 NodeJS 可以直接使用 Sockets API 建立各種網路服務程式。 12 | 13 | JavaScript 語言本身支援 Lambda 的特性,因此一個匿名函式 (anonymous function) 可以被儲存成一個變數,並當作參數傳遞給另一個函式。 14 | 15 | var proc1 = function(op, x) { 16 | return op(x); 17 | } 18 | 19 | var op1 = function(x) { return x+1; } 20 | var op2 = function(x) { return x*x; } 21 | 22 | proc1(op1, 3); // result is 4 23 | proc1(op2, 5); // result is 25 24 | 25 | 另一個 JavaScript 開發者必須掌握的語言特性稱為 Closure 。 26 | 27 | NodeJS 符合 CommonJS 的規範,使得 Callback 方式易於實現,也能夠讓更多同好基於 JavaScript 開發符合 NodeJS 的外掛模組 (Module)。 28 | 29 | 回想以前要寫一個能夠同時容納上百人的上線的網路服務,需要花費多大的苦工,可能10人多就需要經過一次程式調整,而NodeJS就是為了解決這個困境, NodeJS 因此誕生,它是一種利用 V8 Javascript 編譯器,所開發的產品,利用V8 編譯器的高效能,與Javascript 的程式開發特性所產生的網路程式。 30 | 31 | 開發人員所編寫出來的Javascript 腳本程式,怎麼可能會比其他語言寫出來的網路程式還要快上許多呢?以前的網路程式原理是將使用者每次的連線(connection)都開啟一個執行緒(thread),當連線爆增的時候將會快速耗盡系統效能,並且容易產生阻塞(block)的發生。 32 | 33 | NodeJS對於資源的調校有所不同,當程式接收到一筆連線(connection),會通知作業系統透過epoll, kqueue, /dev/poll,或select將連線保留,並且放入heap中配置,先讓連線進入休眠(Sleep)狀態,當系統通知時才會觸發連線的callback。這種處理連線方式只會佔用掉記憶體,並不會使用到CPU資源。另外因為採用Javascript 語言的特性,每個request都會有一個callback,如此可以避免發生Block的狀況發生。 34 | 35 | 基於Callback特性,目前NodeJS大多應用於Comet(long pulling) Request Server,或者是高連線數量的網路服務上,目前也有許多公司將NodeJS設為內部核心網路服務之一。在NodeJS也提供了外掛管理(Node package management),讓愛好NodeJS輕易開發更多有趣的服務、外掛,並且提供到npm讓全世界使用者快速安裝使用。 36 | 37 | 本書最後執行測試版本為node.js v0.6.8,相關API 文件可查詢`http://nodejs.org ` 38 | 本書所有範例均可於linux, windows 上執行,如遇到任何問題歡迎至`http://nodejs.tw `,詢問對於node.js 相關問題。 39 | -------------------------------------------------------------------------------- /zh-tw/node_javascript.rst: -------------------------------------------------------------------------------- 1 | ******************** 2 | JavaScript 與 NodeJS 3 | ******************** 4 | 5 | 其實使用 JavaScript 在網頁端與伺服器端的差距並不大, 6 | 但是為了使 NodeJS 可以發揮他最強大的能力, 7 | 有一些知識還是必要的, 8 | 所以還是針對這些主題介紹一下。 9 | 其中 Event Loop、Scope 以及 Callback 其實是比較需要了解的基本知識, 10 | cps、currying、flow control是更進階的技巧與應用。 11 | 12 | Event Loop 13 | ========== 14 | 15 | 可能很多人在寫Javascript時,並不知道他是怎麼被執行的。這個時候可以參考一下jQuery作者John Resig一篇好文章,介紹事件及timer怎麼在瀏覽器中執行:How JavaScript Timers Work。通常在網頁中,所有的Javascript執行完畢後(這部份全部都在global scope跑,除非執行函數),接下來就是如John Resig解釋的這樣,所有的事件處理函數,以及timer執行的函數,會排在一個queue結構中,利用一個無窮迴圈,不斷從queue中取出函數來執行。這個就是event loop。 16 | 17 | (除了John Resig的那篇文章,Nicholas C. Zakas的 "Professional Javascript for Web Developer 2nd edition" 有一個試閱本:http://yuiblog.com/assets/pdf/zakas-projs-2ed-ch18.pdf,598頁剛好也有簡短的說明) 18 | 19 | 所以在Javascript中,雖然有非同步,但是他並不是使用執行緒。所有的事件或是非同步執行的函數,都是在同一個執行緒中,利用event loop的方式在執行。至於一些比較慢的動作例如I/O、網頁render, reflow等,實際動作會在其他執行緒跑,等到有結果時才利用事件來觸發處理函數來處理。這樣的模型有幾個好處: 20 | 沒有執行緒的額外成本,所以反應速度很快 21 | 不會有任何程式同時用到同一個變數,不必考慮lock,也不會產生dead lock 22 | 所以程式撰寫很簡單 23 | 但是也有一些潛在問題: 24 | 任一個函數執行時間較長,都會讓其他函數更慢執行(因為一個跑完才會跑另一個) 25 | 在多核心硬體普遍的現在,無法用單一的應用程式instance發揮所有的硬體能力 26 | 用NodeJS撰寫伺服器程式,碰到的也是一樣的狀況。要讓系統發揮event loop的效能,就要盡量利用事件的方式來組織程式架構。另外,對於一些有可能較為耗時的操作,可以考慮使用 process.nextTick 函數來讓他以非同步的方式執行,避免在同一個函數中執行太久,擋住所有函數的執行。 27 | 28 | 如果想要測試event loop怎樣在「瀏覽器」中運行,可以在函數中呼叫alert(),這樣會讓所有Javascript的執行停下來,尤其會干擾所有使用timer的函數執行。有一個簡單的例子,這是一個會依照設定的時間間隔嚴格執行動作的動畫,如果時間過了就會跳過要執行的動作。點按圖片以後,人物會快速旋轉,但是在旋轉執行完畢前按下「delay」按鈕,讓alert訊息等久一點,接下來的動畫就完全不會出現了。 29 | 30 | Scope 與 Closure 31 | ================ 32 | 33 | 要快速理解 JavaScript 的 Scope(變數作用範圍)原理,只要記住他是Lexical Scope就差不多了。簡單地說,變數作用範圍是依照程式定義時(或者叫做程式文本?)的上下文決定,而不是執行時的上下文決定。 34 | 35 | 為了維護程式執行時所依賴的變數,即使執行時程式運行在原本的scope之外,他的變數作用範圍仍然維持不變。這時程式依賴的自由變數(定義時不是local的,而是在上一層scope定義的變數)一樣可以使用,就好像被關閉起來,所以叫做Closure。用程式看比較好懂: 36 | 37 | .. code-block:: js 38 | 39 | function outter(arg1) { 40 | //arg1及free_variable1對inner函數來說,都是自由變數 41 | var free_variable1 = 3; 42 | return function inner(arg2) { 43 | var local_variable1 =2;//arg2及local_variable1對inner函數來說,都是本地變數 44 | return arg1 + arg2 + free_variable + local_variable1; 45 | }; 46 | } 47 | 48 | var a = outter(1);//變數a 就是outter函數執行後返回的inner函數 49 | var b = a(4);//執行inner函數,執行時上下文已經在outter函數之外,但是仍然能正常執行,而且可以使用定義在outter函數裡面的arg1及free_variable1變數 50 | console.log(b);//結果10 51 | 52 | 在Javascript中,scope最主要的單位是函數(另外有global及eval),所以有可能製造出closure的狀況,通常在形式上都是有巢狀的函數定義,而且內側的函數使用到定義在外側函數裡面的變數。 53 | 54 | Closure有可能會造成記憶體洩漏,主要是因為被參考的變數無法被垃圾收集機制處理,造成佔用的資源無法釋放,所以使用上必須考慮清楚,不要造成意外的記憶體洩漏。(在上面的例子中,如果a一直未執行,使用到的記憶體就不會被釋放) 55 | 56 | 跟透過函數的參數把變數傳給函數比較起來,Javascript Engine會比較難對Closure進行最佳化。如果有效能上的考量,這一點也需要注意。 57 | 58 | Callback 59 | ======== 60 | 61 | 要介紹 Callback 之前, 62 | 要先提到 JavaScript 的特色。 63 | 64 | JavaScript 是一種函數式語言(functional language),所有Javascript語言內的函數,都是高階函數(higher order function,這是數學名詞,計算機用語好像是first class function,意指函數使用沒有任何限制,與其他物件一樣)。也就是說,函數可以作為函數的參數傳給函數,也可以當作函數的返回值。這個特性,讓Javascript的函數,使用上非常有彈性,而且功能強大。 65 | 66 | callback在形式上,其實就是把函數傳給函數,然後在適當的時機呼叫傳入的函數。Javascript使用的事件系統,通常就是使用這種形式。NodeJS中,有一個物件叫做EventEmitter,這是NodeJS事件處理的核心物件,所有會使用事件處理的函數,都會「繼承」這個物件。(這裡說的繼承,實作上應該像是mixin)他的使用很簡單: 67 | 可以使用 物件.on(事件名稱, callback函數) 或是 物件.addListener(事件名稱, callback函數) 把你想要處理事件的函數傳入 68 | 在 物件 中,可以使用 物件.emit(事件名稱, 參數...) 呼叫傳入的callback函數 69 | 這是Observer Pattern的簡單實作,而且跟在網頁中使用DOM的addEventListener使用上很類似,也很容易上手。不過NodeJS是大量使用非同步方式執行的應用,所以程式邏輯幾乎都是寫在callback函數中,當邏輯比較複雜時,大量的callback會讓程式看起來很複雜,也比較難單元測試。舉例來說: 70 | 71 | var p_client = new Db('integration_tests_20', new Server("127.0.0.1", 27017, {}), {'pk':CustomPKFactory}); 72 | p_client.open(function(err, p_client) { 73 | p_client.dropDatabase(function(err, done) { 74 | p_client.createCollection('test_custom_key', function(err, collection) { 75 | collection.insert({'a':1}, function(err, docs) { 76 | collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { 77 | cursor.toArray(function(err, items) { 78 | test.assertEquals(1, items.length); 79 | p_client.close(); 80 | }); 81 | }); 82 | }); 83 | }); 84 | }); 85 | }); 86 | 87 | 這是在網路上看到的一段操作mongodb的程式碼,為了循序操作,所以必須在一個callback裡面呼叫下一個動作要使用的函數,這個函數裡面還是會使用callback,最後就形成一個非常深的巢狀。 88 | 89 | 這樣的程式碼,會比較難進行單元測試。有一個簡單的解決方式,是盡量不要使用匿名函數來當作callback或是event handler。透過這樣的方式,就可以對各個handler做單元測試了。例如: 90 | 91 | var http = require('http'); 92 | var tools = { 93 | cookieParser: function(request, response) { 94 | if(request.headers['Cookie']) { 95 | //do parsing 96 | } 97 | } 98 | }; 99 | var server = http.createServer(function(request, response) { 100 | this.emit('init', request, response); 101 | //... 102 | }); 103 | server.on('init', tools.cookieParser); 104 | server.listen(8080, '127.0.0.1'); 105 | 106 | 更進一步,可以把tools改成外部module,例如叫做tools.js: 107 | 108 | module.exports = { 109 | cookieParser: function(request, response) { 110 | if(request.headers['Cookie']) { 111 | //do parsing 112 | } 113 | } 114 | }; 115 | 116 | 然後把程式改成: 117 | 118 | var http = require('http'); 119 | 120 | var server = http.createServer(function(request, response) { 121 | this.emit('init', request, response); 122 | //... 123 | }); 124 | server.on('init', require('./tools').cookieParser); 125 | server.listen(8080, '127.0.0.1'); 126 | 127 | 這樣就可以單元測試cookieParser了。例如使用nodeunit時,可以這樣寫: 128 | 129 | var testCase = require('nodeunit').testCase; 130 | module.exports = testCase({ 131 | "setUp": function(cb) { 132 | this.request = { 133 | headers: { 134 | Cookie: 'name1:val1; name2:val2' 135 | } 136 | }; 137 | this.response = {}; 138 | this.result = {name1:'val1',name2:'val2'}; 139 | cb(); 140 | }, 141 | "tearDown": function(cb) { 142 | cb(); 143 | }, 144 | "normal_case": function(test) { 145 | test.expect(1); 146 | var obj = require('./tools').cookieParser(this.request, this.response); 147 | test.deepEqual(obj, this.result); 148 | test.done(); 149 | } 150 | }); 151 | 152 | 善於利用模組,可以讓程式更好維護與測試。 153 | 154 | CPS(Continuation-Passing Style) 155 | ================================ 156 | 157 | cps是callback使用上的特例,形式上就是在函數最後呼叫callback,這樣就好像把函數執行後把結果交給callback繼續運行,所以稱作continuation-passing style。利用cps,可以在非同步執行的情況下,透過傳給callback的這個cps callback來獲知callback執行完畢,或是取得執行結果。例如: 158 | 159 | 160 | 161 | 162 | 163 | 164 | 179 | 180 | 進一步的應用,也可以參考2-6 流程控制。 181 | 182 | 183 | 函數返回函數與Currying 184 | ==================== 185 | 186 | 前面的cps範例裡面,使用了函數返回函數,這是為了把cps callback傳遞給onreadystatechange事件處理函數的方法。(因為這個事件處理函數並沒有設計好會傳送/接收這樣的參數)實際會執行的事件處理函數其實是內層返回的那個函數,之外包覆的這個函數,主要是為了利用Closure,把next傳給內層的事件處理函數。這個方法更常使用的地方,是為了解決一些scope問題。例如: 187 | 188 | 201 | 202 | 最後得出的結果會是100,而不是想像中的45,這是因為等到setTimeout指定的函數執行時,變數i已經變成10而離開迴圈了。要解決這個問題,就需要透過Closure來保存變數i: 203 | 204 | 220 | 221 | 函數返回函數的另外一個用途,是可以暫緩函數執行。例如: 222 | 223 | function add(m, n) { 224 | return m+n; 225 | } 226 | var a = add(20, 10); 227 | console.log(a); 228 | 229 | add這個函數,必須同時輸入兩個參數,才有辦法執行。如果我希望這個函數可以先給它一個參數,等一些處理過後再給一個參數,然後得到結果,就必須用函數返回函數的方式做修改: 230 | 231 | function add(m) { 232 | return function(n) { 233 | return m+n; 234 | }; 235 | } 236 | var wait_another_arg = add(20);//先給一個參數 237 | var a = function(arr) { 238 | var ret=0; 239 | for(var i=0;i 349 | function Wait(fns, done) { 350 | var count = 0; 351 | var results = []; 352 | this.getCallback = function(index) { 353 | count++; 354 | return (function(waitback) { 355 | return function() { 356 | var i=0,args=[]; 357 | for(;i 405 | 406 | 執行結果: 407 | 408 | done a 409 | done b 410 | done a 411 | done c 412 | done all. result: 3500 413 | 414 | 上面只是一些小實驗,更成熟的作品是Tim Caswell的step:https://github.com/creationix/step 415 | 416 | 如果希望真正使用同步的方式寫非同步,則需要使用Promise.js這一類的library來轉換非同步函數,不過他結構比較複雜XD(見仁見智,不過有些人認為Promise有點過頭了):http://blogs.msdn.com/b/rbuckton/archive/2011/08/15/promise-js-2-0-promise-framework-for-javascript.aspx 417 | 418 | 如果想不透過其他Library做轉換,又能直接用同步方式執行非同步函數,大概就要使用一些需要額外compile原始程式碼的方法了。例如Bruno Jouhier的streamline.js:https://github.com/Sage/streamlinejs 419 | 420 | 421 | 循序執行 422 | ------- 423 | 424 | 循序執行可以協助把非常深的巢狀callback結構攤平,例如用這樣的簡單模組來做(serial.js): 425 | 426 | module.exports = function(funs) { 427 | var c = 0; 428 | if(!isArrayOfFunctions(funs)) { 429 | throw('Argument type was not matched. Should be array of functions.'); 430 | } 431 | return function() { 432 | var args = Array.prototype.slice.call(arguments, 0); 433 | if(!(c>=funs.length)) { 434 | c++; 435 | return funs[c-1].apply(this, args); 436 | } 437 | }; 438 | } 439 | 440 | function isArrayOfFunctions(f) { 441 | if(typeof f !== 'object') return false; 442 | if(!f.length) return false; 443 | if(!f.concat) return false; 444 | if(!f.splice) return false; 445 | var i = 0; 446 | for(; i`_ 72 | 這個 repository,\ 73 | 即可使用 apt-get 完成 npm 安裝。 74 | 75 | .. topic:: Ubuntu Linux 使用 apt-get 安裝 npm 76 | 77 | :: 78 | 79 | sudo apt-get install python-software-properties 80 | sudo add-apt-repository ppa:gias-kay-lee/npm 81 | sudo apt-get update 82 | sudo apt-get npm 83 | 84 | npm 官方提供的安裝程式 ``install.sh``\ ,\ 85 | 可以適用於大多數的 Linux 系統。\ 86 | 使用這個安裝程式,請先確認: 87 | 88 | 1. 系統已安裝 curl 工具(請使用 ``curl --version`` 查看版本訊息) 89 | 2. 已安裝 Node.js 並且 PATH 正確設置 90 | 3. Node.js 的版本必須大於 0.4.x 91 | 92 | 以下為 npm 提供的安裝指令: 93 | 94 | :: 95 | 96 | curl http://npmjs.org/install.sh | sh 97 | 98 | 安裝成功會看到如下訊息: 99 | 100 | .. topic:: install.sh 安裝成功的訊息 101 | 102 | :: 103 | 104 | npm@1.0.105 /home/USERNAME/local/node/lib/node_modules/npm 105 | It worked 106 | 107 | 安裝於 Mac OS X 108 | --------------- 109 | 110 | 建議採用與 Node.js 相同的方式,進行 npm 的安裝。\ 111 | 例如使用 MacPorts 安裝 Node.js,\ 112 | 就同樣使用 MacPorts 安裝 npm,\ 113 | 這樣對日後的維護才會更方便容易。 114 | 115 | 使用 MacPorts 安裝 npm 是本書比較建議的方式,\ 116 | 它可以讓 npm 的安裝、移除及更新工作自動化,\ 117 | 將會幫助開發者節省寶貴時間。 118 | 119 | .. topic:: 安裝 MacPorts 的提示 120 | 121 | 在 MacPorts 網站,可以取得 OS X 系統版本對應的安裝程式(例如 10.6 或 10.7)。 122 | 123 | http://www.macports.org/ 124 | 125 | 安裝過程會詢問系統管理者密碼,使用預設的選項完成安裝即可。\ 126 | 安裝 MacPorts 之後,在終端機執行 ``port -v`` 將會看到 MacPorts 的版本訊息。 127 | 128 | 安裝 npm 之前,先更新 MacPorts 的套件清單,以確保安裝的 npm 是最新版本。 129 | 130 | :: 131 | 132 | sudo port -d selfupdate 133 | 134 | 接著安裝 npm。 135 | 136 | :: 137 | 138 | sudo port install npm 139 | 140 | 若讀者的 Node.js 並非使用 MacPorts 安裝,\ 141 | 則不建議使用 MacPorts 安裝 npm,\ 142 | 因為 MacPorts 會自動檢查並安裝相依套件,\ 143 | 而 npm 相依 nodejs,\ 144 | 所以 MacPorts 也會一併將 nodejs 套件安裝,\ 145 | 造成先前讀者使用其它方式安裝的 nodejs 被覆蓋。 146 | 147 | 讀者可以先使用 MacPorts 安裝 curl(\ ``sudo port install curl``\ ),\ 148 | 再參考 Linux 的 install.sh 安裝方式,\ 149 | 即可使用 npm 官方提供的安裝程式。 150 | 151 | NPM 安裝後測試 152 | ------------- 153 | 154 | npm 是指令列工具(command-line tool),\ 155 | 使用時請先打開系統的文字終端機工具。 156 | 157 | 測試 npm 安裝與設定是否正確,請輸入指令如下: 158 | 159 | :: 160 | 161 | npm -v 162 | 163 | 或是: 164 | 165 | :: 166 | 167 | npm --version 168 | 169 | 如果 npm 已經正確安裝設定,就會顯示版本訊息: 170 | 171 | .. topic:: 執行結果(範例) 172 | 173 | :: 174 | 175 | 1.1.0-2 176 | 177 | 使用 NPM 安裝套件 178 | ================ 179 | 180 | npm 目前擁有超過 6000 種套件(packages),\ 181 | 可以在 `npm registry `_ 使用關鍵字搜尋套件。 182 | 183 | http://search.npmjs.org/ 184 | 185 | 舉例來說,在關鍵字欄位輸入「coffee-script」,\ 186 | 下方的清單就會自動列出包含 coffee-script 關鍵字的套件。 187 | 188 | .. image:: ../images/zh-tw/node_npm_registry.png 189 | 190 | 接著我們回到終端機模式的操作,\ 191 | ``npm`` 的指令工具本身就可以完成套件搜尋的任務。 192 | 193 | 例如,以下的指令同樣可以找出 coffee-script 相關套件。 194 | 195 | :: 196 | 197 | npm search coffee-script 198 | 199 | 以下是搜尋結果的參考畫面: 200 | 201 | .. image:: ../images/zh-tw/node_npm_search.png 202 | 203 | 找到需要的套件後(例如 express),即可使用以下指令安裝: 204 | 205 | :: 206 | 207 | npm install coffee-script 208 | 209 | 值得注意的一點是,使用 ``npm install`` 會將指定的套件,\ 210 | 安裝在工作目錄(Working Directory)的 ``node_modules`` 資料夾下。 211 | 212 | 以 Windows 為例,如果執行 ``npm install`` 的目錄位於: 213 | 214 | ``C:\project1`` 215 | 216 | 那麼 npm 將會自動建立一個 node_modules 的子目錄(如果不存在)。 217 | 218 | ``C:\project1\node_modules`` 219 | 220 | 並且將下載的套件,放置於這個子目錄,例如: 221 | 222 | ``C:\project1\node_modules\coffee-script`` 223 | 224 | 這個設計讓專案可以個別管理相依的套件,\ 225 | 並且可以在專案佈署或發行時,\ 226 | 將這些套件(位於 node_modules)一併打包,\ 227 | 方便其它專案的使用者不必再重新下載套件。 228 | 229 | 這個 ``npm install`` 的預設安裝模式為 **local**\ (本地),\ 230 | 只會變更當前專案的資料夾,\ 231 | 不會影響系統。 232 | 233 | 另一種安裝模式稱為 **global**\ (全域),\ 234 | 這種模式會將套件安裝到系統資料夾,\ 235 | 也就是 npm 安裝路徑的 ``node_modules`` 資料夾,\ 236 | 例如: 237 | 238 | ``C:\Program Files\nodejs\node_modules`` 239 | 240 | 是否要使用全域安裝,\ 241 | 可以依照套件是否提供\ **新指令**\ 來判斷,\ 242 | 舉例來說,\ 243 | express 套件提供 ``express`` 這個指令,\ 244 | 而 coffee-script 則提供 ``coffee`` 指令。 245 | 246 | 在 local 安裝模式中,這些指令的程式檔案,\ 247 | 會被安裝到 ``node_modules`` 的 ``.bin`` 這個隱藏資料夾下。\ 248 | 除非將 .bin 的路徑加入 PATH 環境變數,\ 249 | 否則要執行這些指令將會相當不便。 250 | 251 | 為了方便指令的執行,\ 252 | 我們可以在 ``npm install`` 加上 ``-g`` 或 ``--global`` 參數,\ 253 | 啟用 global 安裝模式。例如: 254 | 255 | :: 256 | 257 | npm install -g coffee-script 258 | npm install -g express 259 | 260 | 使用 global 安裝模式,\ 261 | 需要注意執行權限的問題,\ 262 | 若權限不足,可能會出現類似以下的錯誤訊息: 263 | 264 | :: 265 | 266 | npm ERR! Error: EACCES, permission denied '...' 267 | npm ERR! 268 | npm ERR! Please try running this command again as root/Administrator. 269 | 270 | 要獲得足夠得執行權限,請參考以下說明: 271 | 272 | * Windows 7 或 2008 以上,在「命令提示字元」的捷徑按右鍵,\ 273 | 選擇「以系統管理員身分執行」,\ 274 | 執行 npm 指令時就會具有 Administrator 身分。 275 | * Mac OS X 或 Linux 系統,可以使用 ``sudo`` 指令,例如:\ 276 | 277 | ``sudo npm install -g express`` 278 | * Linux 系統可以使用 root 權限登入,或是以「\ ``sudo su -``\ 」切換成 root 身分。\ 279 | (使用 root 權限操作系統相當危險,因此並不建議使用這種方式。) 280 | 281 | 若加上 ``-g`` 參數,使用 ``npm install -g coffee-script`` 完成安裝後,\ 282 | 就可以在終端機執行 ``coffee`` 指令。例如: 283 | 284 | :: 285 | 286 | coffee -v 287 | 288 | .. topic:: 執行結果(範例) 289 | 290 | :: 291 | 292 | CoffeeScript version 1.2.0 293 | 294 | 套件的更新及維護 295 | ============== 296 | 297 | 除了前一節說明的 search 及 install 用法,\ 298 | npm 還提供其他許多指令(commands)。 299 | 300 | 使用 ``npm help`` 可以查詢可用的指令。 301 | 302 | :: 303 | 304 | npm help 305 | 306 | .. topic:: 執行結果(部分) 307 | 308 | :: 309 | 310 | where is one of: 311 | adduser, apihelp, author, bin, bugs, c, cache, completion, 312 | config, deprecate, docs, edit, explore, faq, find, get, 313 | help, help-search, home, i, info, init, install, la, link, 314 | list, ll, ln, login, ls, outdated, owner, pack, prefix, 315 | prune, publish, r, rb, rebuild, remove, restart, rm, root, 316 | run-script, s, se, search, set, show, star, start, stop, 317 | submodule, tag, test, un, uninstall, unlink, unpublish, 318 | unstar, up, update, version, view, whoami 319 | 320 | 使用 ``npm help command`` 可以查詢指令的詳細用法。例如: 321 | 322 | :: 323 | 324 | npm help list 325 | 326 | 接下來,本節要介紹開發過程常用的 npm 指令。 327 | 328 | 使用 ``list`` 可以列出已安裝套件: 329 | 330 | :: 331 | 332 | npm list 333 | 334 | .. topic:: 執行結果(範例) 335 | 336 | :: 337 | 338 | ├── coffee-script@1.2.0 339 | └─┬ express@2.5.6 340 | ├─┬ connect@1.8.5 341 | │ └── formidable@1.0.8 342 | ├── mime@1.2.4 343 | ├── mkdirp@0.0.7 344 | └── qs@0.4.1 345 | 346 | 檢視某個套件的詳細資訊,例如: 347 | 348 | :: 349 | 350 | npm show express 351 | 352 | 升級所有套件(如果該套件已發佈更新版本): 353 | 354 | :: 355 | 356 | npm update 357 | 358 | 升級指定的套件: 359 | 360 | :: 361 | 362 | npm update express 363 | 364 | 移除指定的套件: 365 | 366 | :: 367 | 368 | npm uninstall express 369 | 370 | 使用 package.json 371 | ================= 372 | 373 | 對於正式的 Node.js 專案,\ 374 | 可以建立一個命名為 ``package.json`` 的設定檔(純文字格式),\ 375 | 檔案內容參考範例如下: 376 | 377 | .. topic:: package.json(範例) 378 | 379 | :: 380 | 381 | { 382 | "name": "application-name" 383 | , "version": "0.0.1" 384 | , "private": true 385 | , "dependencies": { 386 | "express": "2.5.5" 387 | , "coffee-script": "latest" 388 | , "mongoose": ">= 2.5.3" 389 | } 390 | } 391 | 392 | 其中 ``name`` 與 ``version`` 依照專案的需求設置。 393 | 394 | 需要注意的是 ``dependencies`` 的設定,\ 395 | 它用於指定專案相依的套件名稱及版本: 396 | 397 | * ``"express": "2.5.5"`` 398 | 399 | //代表此專案相依版本 2.5.5 的 express 套件 400 | * ``"coffee-script": "latest"`` 401 | 402 | //使用最新版的 coffee-script 套件(每次更新都會檢查新版) 403 | * ``"mongoose": ">= 2.5.3"`` 404 | 405 | //使用版本大於 2.5.3 的 mongoose 套件 406 | 407 | 假設某個套件的新版可能造成專案無法正常運作,\ 408 | 就必須指定套件的版本,\ 409 | 避免專案的程式碼來不及更新以相容新版套件。\ 410 | 通常在開發初期的專案,\ 411 | 需要盡可能維持新套件的相容性(以取得套件的更新或修正),\ 412 | 可以用「\ ``>=``\ 」設定最低相容的版本,\ 413 | 或是使用「\ ``latest``\ 」設定永遠保持最新套件。 414 | -------------------------------------------------------------------------------- /zh-tw/outline.rst: -------------------------------------------------------------------------------- 1 | ****************** 2 | Node.js 安裝與設定 3 | ****************** 4 | 5 | 安裝 6 | ==== 7 | 8 | 設定 9 | ==== 10 | 11 | 12 | ************ 13 | Node.js 基礎 14 | ************ 15 | 16 | 17 | ****************** 18 | 套件管理工具(NPM) 19 | ****************** 20 | 21 | :: 22 | 23 | npm install xxxx 24 | 25 | ************ 26 | 網站開發框架 27 | ************ 28 | 29 | View 30 | 31 | Node.js Layout & Routing 32 | 33 | Template 34 | 35 | 36 | ********** 37 | Socket.IO 38 | ********** 39 | 40 | long pulling 41 | 42 | 即時反饋模組 43 | 44 | 即時網站架構 45 | 46 | websocket 訊息即時傳遞 47 | 48 | 49 | ********* 50 | 存取資料庫 51 | ********* 52 | 53 | Database 54 | 55 | *************** 56 | 實戰一、心智圖 57 | *************** 58 | 59 | ************* 60 | 實戰二、Todo 61 | ************* 62 | 63 | --------------------------------------------------------------------------------