node.js 一個簡單的頁面輸出

來源:互聯網
上載者:User

最近決定重拾node.js,用它來做一個合并JS檔案的東西。由於忘得差不多了,先看能不能輸出一個頁面來再說。以下是我的一些筆記,省得以後又忘淨光……

安裝過程就不說了。如果成功是能使用node的命令。node.js調試是非常方便的。每種後台語言都有一個向那個黑黢黢的控制台團輸出語用的命令。node.js沿用FF那套東西,也就是console對象與其方法。我們首先建一個example.js檔案,內容如下,然後在控制台開啟它。

console.log("hello node.js")for(var i in console){console.log(i+"  "+console[i])}
node example.js。

你千萬不要在node.js使用alert進行調試,那是瀏覽器帶的全域方法,不報錯才怪。

輸出結果如下:

var log = function () {  process.stdout.write(format.apply(this, arguments) + '\n');}var info = function () {  process.stdout.write(format.apply(this, arguments) + '\n');}var warn = function () {  writeError(format.apply(this, arguments) + '\n');}var error = function () {  writeError(format.apply(this, arguments) + '\n');}var dir = function (object) {  var util = require('util');  process.stdout.write(util.inspect(object) + '\n');}var time = function (label) {  times[label] = Date.now();}var timeEnd = function (label) {  var duration = Date.now() - times[label];  exports.log('undefined: NaNms', label, duration);}var trace = function (label) {  // TODO probably can to do this better with V8's debug object once that is  // exposed.  var err = new Error;  err.name = 'Trace';  err.message = label || '';  Error.captureStackTrace(err, arguments.callee);  console.error(err.stack);}var assert = function (expression) {  if (!expression) {    var arr = Array.prototype.slice.call(arguments, 1);    require('assert').ok(false, format.apply(this, arr));  }}

通過這些函數,我們大概瞭解到node.js在全域範圍添加了些什麼,如require, process。但也不能武斷說是,因為它們可能是某個範圍的私人對象。不過,瞭解這些全域對象,並從這些對象上出發去瞭解其他對象,非常有助於我們瞭解node.js的生態結構。在前端,每當瀏覽器升級,我就遍曆一下window對象以及其個元素節點就得知它又增加了什麼方法與屬性,然後再查文檔。那些更新日誌不可能把全部細節都告訴你的,必須自己動手遍曆一下,這樣你就比別人知道得更多。好了,我們去找node.js的全域對象。

node.js的文檔告訴我們,有如下幾個全域對象:

global,  process,  require,__filename,__dirname, module

但我們為什麼能直接使用console.log呢?經驗告訴我們,console肯定是某全域對象的成員,正如我們可以alert, 也可以window.alert。好了,我們選遍曆一下global這個名字取得非常霸氣的對象

for(var i in global){console.log("var " + i+" = "+global[i])}

結果如下:

var global = [object global]var process = [object EventEmitter]var GLOBAL = [object global]var root = [object global]var Buffer = function Buffer(subject, encoding, offset) {//太長了,省略}var setTimeout = function () {      var t = NativeModule.require('timers');      return t.setTimeout.apply(this, arguments);    }var setInterval = function () {      var t = NativeModule.require('timers');      return t.setInterval.apply(this, arguments);    }var clearTimeout = function () {      var t = NativeModule.require('timers');      return t.clearTimeout.apply(this, arguments);    }var clearInterval = function () {      var t = NativeModule.require('timers');      return t.clearInterval.apply(this, arguments);    }var console = [object Object]

發現global與瀏覽器的window一樣,都有個指向自身的同名成員。window === window.window, global === global.global。但node.js早期設計得不好,又一搞了個多餘的GLOBAL成員。

console.log(global === global.global)//trueconsole.log(global === global.GLOBAL)//true

我們再遍曆module對象:

for(var i in module){console.log("var " + i + " = "+module[i])}

結果如下:

var id = .var exports = [object Object]var parent = nullvar filename = /home/cheng19840218/node/example.jsvar loaded = falsevar exited = falsevar children = var paths = /home/cheng19840218/node/node_modules,/home/cheng19840218/node_modules,/home/node_modules,/node_modulesvar load = function (filename) {//太長了,省略}var _compile = function (content, filename) {//太長了,省略}

原來那個著名的exports是在此提供的,__filename大概也是filename的引用。只要遍曆一下,你就發現許多有趣的東西。但別以為一下秘密就暴光在你眼皮下,還有許多不可遍曆屬性。比如上面我遍曆global對象,只有尞尞可數幾個成員,我們可以使用ecma262v5新增的方法去考察一下:

console.log(Object.getOwnPropertyNames(global))

結果如下:

[ 'clearInterval',  'TypeError',  'decodeURI',  'Buffer',  'parseFloat',  'Number',  'URIError',  'encodeURIComponent',  'RangeError',  'ReferenceError',  'RegExp',  'Array',  'isNaN',  'setTimeout',  'console',  'Date',  'Infinity',  'Boolean',  'Error',  'root',  'NaN',  'String',  'Function',  'Math',  'undefined',  'encodeURI',  'escape',  'unescape',  'process',  'decodeURIComponent',  'EvalError',  'clearTimeout',  'GLOBAL',  'setInterval',  'SyntaxError',  'Object',  'eval',  'global',  'parseInt',  'JSON',  'isFinite' ]

許多人學node.js就立即看其文檔,殊不知node.js本身所依賴的V8引擎就擁有許多要學的東西,這其中包括ecma262v5帶來的新方法新對象,還有效仿firefox的一些文法:

__defineGetter____defineSetter____lookupGetter____lookupSetter__setget__proto__

不過以"__"開頭的東西我是不建議用的,像set與get現在最新的瀏覽器都支援,如IE9,可以在其開發人員工具下試試下面的指令碼:

var a = {  get latest () {    if (this.log.length > 0) {      return this.log[this.log.length - 1];    }    else {      return null;    }  },  log: []}a.log[0] = "a";a.log[1] = "b";console.log(a.latest)

在node.js基本上沒有相容問題(如果你不是從早期的node.js玩起來),而且原生對象又加了這麼多擴充,再加上node.js內建的庫,每個模組都提供了花樣繁多的API,如果還嫌不夠,github上還有上千個外掛程式。對於想向嘗試一下後端編程的JSer來說,這是極具誘惑力的。可能有人說,後端不是涉及資料庫操作嗎?這與比前端的DOM相容比起來,不值一提。還有什麼檔案夾與檔案操作 ,你就當成是一種特殊的數組操作就是。因此你完全可以憤憤不平!

好了,我們來點實質的內容吧。node.js本來就是一個http伺服器,它是要與前端互動的,因此少不了兩個對象:請求(request)與響應(response)。請求與響應顯然一種非同步東西,因為我們 不知道前端什麼時候發請求過來,響應也不能立即給前端,還要做日誌,讀寫資料庫等操作呢。因此對於javascript來說,這用回呼函數來實現最好。那麼由誰來接受這個回調呢?一個伺服器對象!

var http = require("http");http.createServer(function(request, response) {  response.writeHead(200, {"Content-Type": "text/plain"});  response.write("Hello node.js");  response.end();}).listen(8888);

node.js有個特殊的require,用於同步載入其他模組的對象,這與其他語言的require, import差不多。能同步就是好,不像前端那樣一層套一層。然後利用一個函數去執行個體化一個伺服器對象,然後監聽8888連接埠。這是node.js官網最初的例子,大家都寫爛了。但這樣的程式在現實中一無是處,我們在地址欄上輸入URL,你起碼要返回一個完整頁面給我吧!

對此,我們首先要進行模組化。模組化是以檔案為單位的,把example.js更名為server.js,然後再把裡面的內容改為一個模組。對於一個node.js的檔案,其實它裡面的內容是在一個封閉的環境中執行。要想共用給其他模組使用,就必須綁定在exports對象上。

var http = require("http");exports.start = function(){http.createServer(function(request, response) {  console.log("Request received...");  response.writeHead(200, {"Content-Type": "text/plain"});  response.write("Hello node.js");  response.end();}).listen(8888);console.log("server start...");}

然後我們再建一個index.js作為入口(index.js與server.js放在同一目錄下)。

var server = require("./server");server.start();

然後建一個index.html頁面。

<!doctype html><html>    <head>        <title>index</title>        <meta content="IE=8" http-equiv="X-UA-Compatible"/>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">           </head>    <body>        <h2>這是首頁</h2>    </body></html>

現在我們就在要請求過來時,把此頁的內容讀出來,返給使用者。這時我們就要用到fs模組的方法了。

            var http = require("http");            var fs = require('fs');            exports.start = function(){                http.createServer(function(request, response) {                    fs.readFile('./index.html', 'utf-8',function (err, data) {//讀取內容                        if (err) throw err;                        response.writeHead(200, {"Content-Type": "text/html"});//注意這裡                        response.write(data);                        response.end();                    });                }).listen(8888);                console.log("server start...");            }

好了,這時我們重啟再次輸入地址,就看到一個完整的頁面了。

但一個頁面除了HTML結構層外,還有javascript與css。那麼,我們在目前的目錄建一個檔案夾javascripts, 裡面建index.js,內容如下:

window.onload = function(){   var p = document.createElement("p");   p.innerHTML = "這是動態添加的"   document.body.appendChild(p);}

再建一個styles目錄,裡面建index.css,內容如下:

html,body{   background: #3671A5;   height: 100%}

然後在index.html引入這兩個檔案:

<!doctype html><html>    <head>        <title>index</title>        <meta content="IE=8" http-equiv="X-UA-Compatible"/>        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">        <link type="text/css" rel="stylesheet" href="styles/index.css"/>        <script src="/javascripts/index.js"></script>     </head>    <body>        <h2>這是首頁</h2>     </body></html>

重新開啟,發現沒有改變,google,說要處理js與css檔案的請求。沒有辦法,取得request.url屬性,再判定尾碼名,為它進行檔案讀取與設定首部。

var http = require("http");var fs = require('fs');var url = require('url');exports.start = function(){    http.createServer(function(request, response) {        var pathname = url.parse(request.url).pathname;        var ext = pathname.match(/(\.[^.]+|)$/)[0];//取得尾碼名        switch(ext){            case ".css":            case ".js":                fs.readFile("."+request.url, 'utf-8',function (err, data) {//讀取內容                    if (err) throw err;                    response.writeHead(200, {                        "Content-Type": {                             ".css":"text/css",                             ".js":"application/javascript",                      }[ext]                    });                    response.write(data);                    response.end();                });                break;            default:                fs.readFile('./index.html', 'utf-8',function (err, data) {//讀取內容                    if (err) throw err;                    response.writeHead(200, {                        "Content-Type": "text/html"                    });                    response.write(data);                    response.end();                });        }    }).listen(8888);    console.log("server start...");}

至此,本文的目的達到了。三個node.js檔案,一個普通的js檔案,一個css檔案,一個html檔案。下一個目的就是多頁了,一個網站是由多個目的構成的。它包含如下內容,能處理ajax請求,上傳檔案,Session與Cookie支援,日誌,MIME識別,路由派發,緩衝系統......要做的事多得嚇人,因此有人一上來就架構,與學JS那樣,連API還沒有摸熟就用jQuery了,那學個毛!回顧一下我們上面的server.js中間的部分,其實就要把MIME與路由拆分出來的。但最重要的事還有一樣,如何處理這無窮的函數嵌套?本人覺得這與我的模組載入系統還沒有什麼兩樣,下次就從這裡動手吧。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.