• node.js 一个简单的页面输出


    最近决定重拾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)//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...");
    }
    

    至此,本文的目的达到了。三个node.js文件,一个普通的js文件,一个css文件,一个html文件。下一个目的就是多页了,一个网站是由多个目的构成的。它包含如下内容,能处理ajax请求,上传文件,Session与Cookie支持,日志,MIME识别,路由派发,缓存系统......要做的事多得吓人,因此有人一上来就框架,与学JS那样,连API还没有摸熟就用jQuery了,那学个毛!回顾一下我们上面的server.js中间的部分,其实就要把MIME与路由拆分出来的。但最重要的事还有一样,如何处理这无穷的函数嵌套?本人觉得这与我的模块加载系统还没有什么两样,下次就从这里动手吧。

  • 相关阅读:
    &nbsp|&quot|&amp|&lt|&gt等html字符转义
    OpenSSL: 消息摘要算法
    Linux下tcp协议socket的recv函数返回时机分析(粘包)
    ipv6
    Electron 调用系统工具记事本、计算器等
    MySQL 导出函数与存储过程
    远程连接Ubuntu桌面配置
    当Activity继承AppCompatActivity如何实现隐藏标题栏与状态栏
    spring boot 1.5.2 操作mongodb3.4.0
    VScode-Go can't load package: package .: no buildable Go source files in
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2255083.html
Copyright © 2020-2023  润新知