node.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) //true
console.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 = null
var filename = /home/cheng19840218/node/example.js
var loaded = false
var exited = false
var children = 
var paths = /home/cheng19840218/node/node_modules,/home/cheng19840218/node_modules,/home/node_modules,/node_modules
var 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__
set
get
__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..." );                                                                                                
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值