zoukankan      html  css  js  c++  java
  • 原生nodejs 学习笔记1

    网上许多nodejs教程或书藉都是教你调用第三方模块来编写nodejs应用的,虽然这是非常便捷的,但是封装太厚,你基本一点东西还是没有学到。人家的模块,人家想怎么改就行,可以下一版本就改了接口,你的应用就完蛋了。比如说google,他就爱干这种事情。因此我们还得老老实实学习底层API吧。

    本节首先教大家跑起一个页面吧。

    我在以前就写一篇相关的, node.js 一个简单的页面输出,大家可以先预习一下。

    一般来说,大家都是从这样一个例子入门

    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);
    

    上面脚本写在一个app.js上面,然后打开控制台,定位于它所在目录,输入node app,再打开某一浏览器,输入http://localhost:8888/就看出效果。

    分析上面脚本:

    1. 引入依赖,这是前端AMD规范未流行前, 非常神奇的东西。不过一个项目这么大,肯定要分成N个目录N个文件,各人负责一部分,减少合并冲突的风险
    2. require("http")吐出的http对象是一个原生对象,类似于前端的alert, setTimeout,非常常用。而这个http对象更是后端的中流砥柱,WEB应用离不开它
    3. http.createServer方法要接收一个函数,俗称回调。以后你会看到越来越多回调,nodejs的世界就是回调的世界,nodejs的发展史就是跟回调的抗争史
    4. 这个回调传给你两个重要对象,请求对象与响应对象。response要往前端输出东西,我们需要设置响应头(response.writeHead),告诉浏览器接着下来要怎么处理我们的内容。因为有的东西可能会当成script脚本,有的当成图片,有的当成CSS文件,有的要当成附件下载……response.write就是用来输出内容。输出结束了要调用end方法,这其实是方便response执行end回调。
    5. http.createServer 其实是返回一个Server实例,它有一个listen方法,它是用于监听某一端口。

    ok,这样就完了。我们深入一点吧(这里比较难,可以跳过,直接看下一个★★★)。

    我们打开这里,查看http模块的源码,它大抵调用了

    _http_incoming
    _http_outgoing
    _http_server
    _http_client
    

    这四个重要的内部模块,它们是我们在nodejs环境中访问不到。http还引用其他内部模块,但现在可以不理会它们。_http_incoming,_http_outgoing是提供两个输入输出对象,流是以后我们重点学习的东西。在本模块中,除去哪些已经废弃的方法,主要是这三个方法:get, request , createServer

    我将http源码删减一下,大家就明白什么回事了:

    
    //------------------Server------------------
    var server = require('_http_server');
    exports.ServerResponse = server.ServerResponse;
    var Server = exports.Server = server.Server;
    
    exports.createServer = function(requestListener) {
      return new Server(requestListener);
    };
    
    //------------------Client------------------
    var client = require('_http_client');
    var ClientRequest = exports.ClientRequest = client.ClientRequest;
    
    exports.request = function(options, cb) {
      return new ClientRequest(options, cb);
    };
    
    exports.get = function(options, cb) { //get是request方法的包装
      var req = exports.request(options, cb);
      req.end();
      return req;
    };
    

    那么requestListener是怎么传进去的呢?我们到_http_server内部模块去,发现ServerResponse只是OutgoingMessage的子类,Server实例是通过request事件来绑定我们的createServer回调,同时它也通过connection事件绑定一个connectionListener的方法,connectionListener巨长,里面会emit request事件。

        if (!util.isUndefined(req.headers.expect) &&
            (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
            continueExpression.test(req.headers['expect'])) {
          res._expect_continue = true;
          if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
            self.emit('checkContinue', req, res);
          } else {
            res.writeContinue();
            self.emit('request', req, res);
          }
        } else {
          self.emit('request', req, res);
        }
    

    res是ServerResponse的实例var res = new ServerResponse(req);,req是parserOnIncoming方法传进来的,而parserOnIncoming则是 parser.onIncoming 的一个方法. 追踪到_http_common内部模块,发现以下几句:

    
       parser.onIncoming(parser.incoming, info.shouldKeepAlive)//毫无疑问,parser.incoming就是我们的req对象
    
       parser.incoming = new IncomingMessage(parser.socket);
    

    好了,一切真相大白,req是IncomingMessage的实例, res是ServerResponse亦即OutgoingMessage的实例。这里不得不吐槽,nodejs的源码太混乱了!

    ★★★我们又切换为简单模式。明白以上那段话,我们就知道我们为什么需要重点学习“http.ServerResponse”与“http.IncomingMessage”,如果大家看过express的源码,就会发现其request模块与response模块就在这上面进行扩展的。

    //https://github.com/strongloop/express/blob/master/lib/request.js
    var req = exports = module.exports = {
      __proto__: http.IncomingMessage.prototype
    };
    //https://github.com/strongloop/express/blob/master/lib/response.js
    var res = module.exports = {
      __proto__: http.ServerResponse.prototype
    };
    

    在好久之前,我们是通过fs模块的 fs.readFile 来读取服务器上的某个文件返回给前端:

    
    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...");
    }
    

    前面已经说过,现在已经是流的时代了,response是一个可写流(对应可读流),我们创建一个可读流就行了。

    var http = require("http");
    var fs = require("fs")
    http.createServer(function (request, response) {
        var readable = fs.createReadStream("./index.html")
        response.writeHead(200, {"Content-Type": "text/html"});
        readable.pipe(response);
    
    }).listen(8888);
    

    如果发生错误想跳转到404页面

    var http = require("http");
    var fs = require("fs")
    http.createServer(function (request, response) {
        var readable = fs.createReadStream("./index.html")
        response.writeHead(200, {"Content-Type": "text/html"});
        readable.pipe(response);
        readable.on("error", function () {
            var readable = fs.createReadStream("./404.html")
            readable.pipe(response);
        })
    }).listen(8888);
    
  • 相关阅读:
    SqlHelper
    C#中gridView常用属性和技巧介绍
    oracle中的存储过程例子
    log4j.properties配置详解
    Ant之build.xml
    jQuery源码
    jQuery实现分页
    mysql中log
    SQL只获取字段中的中文字符
    子Repeater获取父级Repeater绑定项的值
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/4344301.html
Copyright © 2011-2022 走看看