Node.js 入门:从控制台调试到静态资源服务器搭建
本文详细介绍Node.js的基础开发流程,从利用console对象进行调试开始,深入遍历global、process等全局对象以理解Node.js运行环境。随后通过实例演示如何构建HTTP服务器,实现HTML页面输出,并进一步扩展以支持CSS和JavaScript等静态资源的正确解析与返回,帮助开发者掌握Node.js后端交互的核心逻辑。
Node.js 控制台调试与全局对象探索
安装过程此处省略。若安装成功,命令行中应能直接调用node命令。Node.js提供了便捷的调试体验,如同其他后端语言向控制台输出信息一样,Node.js沿用了类似Firefox的控制台对象及其方法。首先创建一个名为example.js的文件,内容如下,并在控制台执行该文件。
代码如下:
console.log("hello node.js")
for(var i in console){
console.log(i+" "+console[i])
}
node example.js。 请注意,在Node.js调试时切勿使用alert,这是浏览器特有的全局方法,在Node环境中调用会报错。
输出结果如下:
代码如下:
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上还有上千个插件。对于想尝试后端编程的前端开发者来说,这是极具诱惑力的。可能有人说,后端不是涉及数据库操作吗?这比起前端的DOM兼容问题,不值一提。还有什么文件夹与文件操作,你就当成是一种特殊的数组操作即可。因此你完全可以感到兴奋!
构建基础 HTTP 服务器
好了,我们来点实质的内容。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与路由拆分出来的。但最重要的事还有一样,如何处理这无穷的函数嵌套?本人觉得这与我的模块加载系统还没有什么两样,下次就从这里动手吧。
Java和JavaScript应用场景对比:企业级后端 vs Web前端与Node.js
2026-11-04 16:42:01
JavaScript模块化开发入门:CommonJS与ES Module的区别
2026-11-03 23:31:34
怎么创建并运行第一个js文件
2026-11-03 21:49:02
JavaScript运行环境怎么搭建?Node.js官方下载与安装教程
2026-11-03 20:03:04
NestJS 集成 TypeORM 或 Prisma 实现数据库持久化操作
2026-11-03 18:56:13
