• 鸟瞰Nodejs


    一,基础。

    1,Node的包管理器:npm; 安装node环境时会自动安装。
    本地模式获取一个包:npm install [package_name]
    此时包被安装到当前木的node_modules子目录下。
    全局模式获取一个包;npm install -g [package_name]
    全局模式安装的包不能直接通过require使用,但通过npm link命令可以在当前目录创建一个指向全局包的链接。比如,如果已经通过 npm install -g express 安装了 express, 这时在工程的目录下运行命令:  
    npm link express  
    ./node_modules/express -> /usr/local/lib/node_modules/express 
     
    2,第一个程序。
    创建helloworld.js文件,并写入:console.log("Hello World");
    打开cmd,进入hellodworld.js所在目录,运行:node hellowrold.js
     
    3,命令行工具node。
    查看帮助信息:node --help
    REPL模式:即时求值的环境,类似于浏览器的控制台,直接输入node。
     
    4,建立Http服务器。
    运行以下代码,然后浏览器打开 localhost:3000
    //app.js
    var http = require('http');
    http.createServer(function(req, res) {
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write('<h1>Node.js</h1>');
      res.end('<p>Hello World</p>');
    }).listen(3000);
    console.log("HTTP server is listening at port 3000."); 

    如果运行app.js后,再修改app.js并不会实时反映到浏览器端,必须退出重新运行。

    如果想实时看到修改后的效果,可以采用supervisor。
    使用方法:
        安装supervisor:  npm install -g supervisor
        使用supervisor运行app.js: supervisor app.js
     
    5,读取文件。
    5.1异步读取文件:
    //readfile.js
    var fs = require('fs');
    fs.readFile('file.txt', 'utf-8', function(err, data) {
      if (err) {
        console.error(err);
      } else {
        console.log(data);
      }
    });
    console.log('end.');

    可以看到,是通过回调函数实现异步。调用时只是将I/O请求发送给了操作系统,然后继续执行后面的代码,执行完成后进入事件循环监听事件。当fs接受到I/O请求完成的事件时,事件循环会主动调用回调函数。

     
    5.2同步读取文件:
    //readfilesync.js
    var fs = require('fs');
    var data = fs.readFileSync('file.txt', 'utf-8');
    console.log(data);
    console.log('end.'); 

    6,事件。

    //event.js
    var EventEmitter = require('events').EventEmitter;
    var event = new EventEmitter();
     
    event.on('some_event', function() {
      console.log('some_event occured.');
    });
     
    setTimeout(function() {
      event.emit('some_event');
    }, 1000); 

     

    7,node.js的事件循环机制。
    node.js程序本身就是一堆事件的回调函数。程序的入口是事件循环第一个事件的回调函数。事件的回调函数在执行的过程中,可能会有异步请求或直接emit事件,执行完毕后再返回事件循环,事件循环会检查事件队列中的未处理事件,直到程序结束。
     
    8,模块和包(Module 、Package)。
    8.1创建并使用模块。
    //module.js
    var name;
    exports.setName = function(thyName) {
      name = thyName;
    };
    exports.sayHello = function() {
      console.log('Hello ' + name);
    }; 

    //getmodule.js

    var myModule = require('./module');
    myModule.setName('BYVoid');
    myModule.sayHello(); 

     

    注意:模块是单例的,即无论多少次require,获得的都是同一个实例。
     
    8.2把一个对象封装到模块中
     
    //hello.js
    function Hello() {
      var name;
      this.setName = function(thyName) {
        name = thyName;
      };
      this.sayHello = function() {
        console.log('Hello ' + name);
      };
    };
    module.exports = Hello; 

    //gethello.js

    var Hello = require('./hello');
    hello = new Hello();
    hello.setName('BYVoid');
    hello.sayHello(); 

    原理:exports本身仅仅是一个普通的空对象,即{}。

     
    8.3包
    包就是C#中的类库。
    node中一个文件夹加上接口(index.js)再加上一个配置文件就是一个包。可以通过 npm init 命令根据提示一步一步创建一个标准的package.json配置。

    二、核心模块。

    1,全局对象
    node中的全局对象是global,所有的全局变量都是global的属性,包括console、process等
    全局变量process描述当前node的进程状态,process.nextTick(callback)的功能是为时间循环设置一项任务,node会在下次事件循环时调用callback.
    console.log() 
    console.error() 
    console.trace()
     
    2, util模块
    util.inherits(constructor, superConstructor) 实现对象间的原型继承,但构造函数内部创造的属性和函数都不会被继承
    var util = require('util');
    function Base() {
      this.name = 'base';
      this.base = 1991;
      this.sayHello = function() {
        console.log('Hello ' + this.name);
      };
    }
     
    Base.prototype.showName = function() {
      console.log(this.name);
    };
    function Sub() {
      this.name = 'sub';
    }
    util.inherits(Sub, Base);
    var objBase = new Base();
    objBase.showName();
    objBase.sayHello();
    console.log(objBase);
     
    var objSub = new Sub();
    objSub.showName();
    //objSub.sayHello();
    console.log(objSub); 

    util.inspect(object,[showHidden],[depth],[colors])是一个将任意对象转换 为字符串的方法, 通常用于调试和错误输出。

    util还提供了util.isArray()、 util.isRegExp()、 util.isDate()、util.isError() 四个类型测试工具,以及 util.format()、util.debug() 等工具
     
    3,events模块。
    监听和触发事件:
    var events = require('events');
    var emitter = new events.EventEmitter();
    emitter.on('someEvent', function(arg1, arg2) {
      console.log('listener1', arg1, arg2);
    });
    emitter.on('someEvent', function(arg1, arg2) {
      console.log('listener2', arg1, arg2);
    });
    emitter.emit('someEvent', 'byvoid', 1991); 

    EventEmitter提供的函数:

    EventEmitter.on(event, listener)
    EventEmitter.emit(event, [arg1], [arg2], [...])
    EventEmitter.once(event, listener)
    EventEmitter.removeListener(event, listener)
    EventEmitter.removeAllListeners([event]) 
     
    4,文件操作 - fs模块
    fs.readFile(filename,[encoding],[callback(err,data)])
    fs.readFileSync(filename, [encoding])
    fs.open(path, flags, [mode], [callback(err, fd)])
    fs.read(fd, buffer, offset, length, position, [callback(err, bytesRead,
    buffer)])
     
    5,http模块
     
    //app.js
    var http = require('http');
    http.createServer(function(req, res) {
      res.writeHead(200, {'Content-Type': 'text/html'});
      res.write('<h1>Node.js</h1>');
      res.end('<p>Hello World</p>');
    }).listen(3000);
    console.log("HTTP server is listening at port 3000.");

    http客户端:

    http.request(options, callback) 发起 HTTP 请求,以下是发送post请求的代码:
    var http = require('http');
    var querystring = require('querystring');
    var contents = querystring.stringify({
      name: 'byvoid',
      email: 'byvoid@byvoid.com',
      address: 'Zijing 2#, Tsinghua University',
    });
    var options = {
      host: 'www.byvoid.com',
      path: '/application/node/post.php',
      method: 'POST',
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded',
        'Content-Length' : contents.length
      }
    };
    var req = http.request(options, function(res) {
      res.setEncoding('utf8');
      res.on('data', function (data) {
        console.log(data);
      });
    });
    req.write(contents);
    req.end();

    http.get(options, callback)更加简便的方法用于处 理GET请求

    var http = require('http');
    http.get({host: 'www.byvoid.com'}, function(res) {
      res.setEncoding('utf8');
      res.on('data', function (data) {
        console.log(data);
      });
    });
    http.request 或 http.get创建的对象是http.ClientRequest,请求完成返回的对象是http.ClientRequest

    三、web开发

    1,使用Express框架。
    类似于asp.net ,express实现的功能:路由控制、模版解析支持、动态视图、缓存等功能。
    express默认支持的模版引擎:jade、ejs
     
    安装express: > npm install -g express
    安装express命令行工具: >npm install -g express-generator
    查看帮助信息: > express --help
    建立web基本结构(使用jade模版引擎):> express  myWeb
    建立web基本结构(使用ejs模版引擎): > express -e myWeb2
    进入刚建立的目录并初始化:>cd myWeb
                                                :> npm install
    启动:> npm start
    此时浏览http://localhost:3000 可以看到第一个界面了。
    关闭服务器 ctrl+c
     
    创建的代码中,app.js是工程的入口。
     
    2,路由控制。
    app.js中,引用路由:var routes = require('./routes/index');
    把Url“/”映射到路由:app.use('/', routes);
     
    routes/index.js中路由的写法:
        var express = require('express');
        var router = express.Router();
        /* GET home page. */
        router.get('/', function(req, res) {
          res.render('index', { title: 'Express' });
        });
        module.exports = router;
     
    路由匹配:
    /user/[username]  :
    app.get('/user/:username', function(req, res) {
      res.send('user: ' + req.params.username);
    }); 
     
    3,视图。
    布局、局部视图跟asp.net mvc一样。
    <ul><%- partial('listitem', items) %></ul> 
     
    视图助手(asp.net mvc中的helper 方法)
    var util = require('util');
    app.helpers({
      inspect: function(obj) {
        return util.inspect(obj, true);
      }
    });
    app.dynamicHelpers({
      headers: function(req, res) {
        return req.headers;
      }
    });
    app.get('/helper', function(req, res) {
      res.render('helper', {
        title: 'Helpers'
      });
    });
    <%=inspect(headers)%> 
     
    4,数据库访问(以MongoDB为例)。
    4.1 MongoDB。
    MongoDB以文档的形式存储数据,一下是一个文档的示例:
    { "_id" : ObjectId( "4f7fe8432b4a1077a7c551e8" ),
      "uid" : 2004,
      "username" : "byvoid",
      "net9" : { "nickname" : "BYVoid",
        "surname" : "Kuo",
        "givenname" : "Carbo",
        "fullname" : "Carbo Kuo",
        "emails" : [ "byvoid@byvoid.com", "byvoid.kcp@gmail.com" ],
        "website" : "http://www.byvoid.com",
        "address" : "Zijing 2#, Tsinghua University" }
     
    4.2 连接数据库。
     
    安装MongoDB.
     
    package.json中添加依赖代码:
    "dependencies": {
        "express": "~4.2.0",
        "static-favicon": "~1.0.0",
        "morgan": "~1.0.0",
        "cookie-parser": "~1.0.1",
        "body-parser": "~1.0.0",
        "debug": "~0.7.4",
        "jade": "~1.3.0",
        "mongodb":">=0.9.9"
      }
     
    运行:  > cd myWeb
                 > npm install
     
    工程目录中创建settings.js文件,代码如下:
    module.exports = {
      cookieSecret: 'microblogbyvoid',
      db: 'microblog',
      host: 'localhost',
    };
    注:db是数据库名称,host是数据库的地址,cookieSecret用于Cookie加密与数据库无关。
     
    创建models子目录,并在其中添加db.js文件,内容是:
    var settings = require('../settings');
    var Db = require('mongodb').Db;
    var Connection = require('mongodb').Connection;
    var Server = require('mongodb').Server;
    module.exports = new Db(settings.db, new Server(settings.host, Connection.DEFAULT_PORT, {})); 

    注:通过module.exports输出了创建的数据库链接。

     
    使用:
    var mongodb = require('./db');
     
    function User(user) {
      this.name = user.name;
      this.password = user.password;
    };
    module.exports = User;
     
    User.prototype.save = function save(callback) {
      // 存入 Mongodb 的文档
      var user = {
        name: this.name,
        password: this.password,
      };
      mongodb.open(function(err, db) {
        if (err) {
          return callback(err);
        }
        // 读取 users 集合
        db.collection('users', function(err, collection) {
          if (err) {
            mongodb.close();
            return callback(err);
          }
          // 为 name 属性添加索引
          collection.ensureIndex('name', {unique: true});
          // 写入 user 文档
          collection.insert(user, {safe: true}, function(err, user) {
            mongodb.close();
            callback(err, user);
          });
        });
      });
    };
     
    User.get = function get(username, callback) {
      mongodb.open(function(err, db) {
        if (err) {
          return callback(err);
        }
        // 读取 users 集合
        db.collection('users', function(err, collection) {
          if (err) {
            mongodb.close();
            return callback(err);
          }
          // 查找 name 属性为 username 的文档
          collection.findOne({name: username}, function(err, doc) {
            mongodb.close();
            if (doc) {
              // 封装文档为 User 对象
              var user = new User(doc);
              callback(err, user);
            } else {
              callback(err, null);
            }
          });
        });
      });
    }; 

     四,其他。

    1,模块加载机制。
    核心模块已经被编译成二进制代码,可以直接require获取,如 require('fs');
    require文件模块的时候,如果指明了路径则按指定的加载,如果没有指定,则去node_modules目录加载。
    即使多次require同一个模块, 加载到的仍是同一个对象,因为node根据实际文件名把加载过的文件模块缓存了。
     
    2,日志功能。
    启用日志功能,需要以产品模式运行express : > NODE-ENV=production node app.js
    记录访问日志和错误日志:
    var fs = require('fs');
    var accessLogfile = fs.createWriteStream('access.log', {flags: 'a'});
    var errorLogfile = fs.createWriteStream('error.log', {flags: 'a'});
    至于错误日志,需要单独实现错误响应,修改如下:
    app.configure('production', function(){
    app.error(function (err, req, res, next) {
    var meta = '[' + new Date() + '] ' + req.url + ' ';
    errorLogfile.write(meta + err.stack + ' ');
    next();
    });
    });
     
    3,javascript的作用域。
    JavaScript的作用域是由函数来决定的,if、for语句中的花括号不是独立的作用域。
    if (true) {
      var somevar = 'value';
    }
    console.log(somevar); // 输出 value 
     
    在一个函数中定义的变量只对这个函数内部可见。
     
    4,call 和 apply 的用法。
    var someuser = {
      name: 'byvoid',
      display: function(words) {
        console.log(this.name + ' says ' + words);
      }
    };
     
    var foo = {
      name: 'foobar'
    };
     
    someuser.display.call(foo, 'hello'); // 输出 foobar says hello 
     
    5,bind的用法。
    var someuser = {
      name: 'byvoid',
      func: function() {
        console.log(this.name);
      }
    };
     
    var foo = {
      name: 'foobar'
    };
     
    foo.func = someuser.func;
    foo.func(); // 输出 foobar
     
    foo.func1 = someuser.func.bind(someuser);
    foo.func1(); // 输出 byvoid
     
    func = someuser.func.bind(foo);
    func(); // 输出 foobar
     
    func2 = func;
    func2(); // 输出 foobar 
     
    bind绑定参数列表:
    var person = {
      name: 'byvoid',
      says: function(act, obj) {
        console.log(this.name + ' ' + act + ' ' + obj);
      }
    };
     
    person.says('loves', 'diovyb'); // 输出 byvoid loves diovyb
     
    byvoidLoves = person.says.bind(person, 'loves');
    byvoidLoves('you'); // 输出 byvoid loves you 

    6, === 和 == 的区别。

    ==包含隐式转换。
     
    7,原型、原型链。
    prototype 、 _proto_
    Object 、Function
    Object.prototype 是所有对象的祖先,Function.prototype 是所有函数的原型,包括构造函数。
     
    参考:《Node.js从入门到精通》
  • 相关阅读:
    redis应用场景
    使用Nginx+Lua+Redis构建灰度发布环境
    Comparison method violates its general contract
    mysql+redis
    缓存技术PK:选择Memcached还是Redis?
    缓存技术PK
    菜鸟教程之工具使用(九)——Git如何进行分支的merge操作
    菜鸟教程之工具使用(八)——EGit禁止自动转换回车换行符
    菜鸟教程之工具使用(七)——从GIt上导出Maven项目
    菜鸟教程之工具使用(六)——让Maven项目直接在eclipse内部的Tomcat中运行
  • 原文地址:https://www.cnblogs.com/FuzhePan/p/3780984.html
Copyright © 2020-2023  润新知