Node.js读书笔记

七天学会Node.js   http://nqdeng.github.io/7-days-nodejs/#1.5.6
Node.js入门       http://www.nodebeginner.org/index-zh-cn.html
Node.js资源汇总   https://cnodejs.org/topic/54128625351649283bcc5b30
1.Node.js   --  是一个可以让JavaScript运行在服务器端的开发平台,充分考虑了实时响应、超大规模数据要求下架构的可扩展性,
                采用了单线程、异步式I/O、事件驱动的程序设计模型,带来了巨大的性能提升,减少了多线程程序设计的复杂性,提高了开发效率。
2.Node.js能做什么?
            Node.js为网络而生,主要用于以下领域的开发
            1.具有复杂逻辑的网站
            2.基于社交网络的大规模Web应用
            3.Web Socket服务器
            4.TCP/UDP套接字应用程序
            5.命令行工具和交互式终端程序
            6.待遇图形用户界面的本地应用程序
       说明:
            Node.js内建了HTTP服务器支持,即可以轻而易举地实现一个网站和服务器的组合

3.异步式I/O与事件驱动架构设计代替多线程,提升性能     --- Node.js最大的特点
      对于高并发解决方案:
                 传统的架构:多线程模型,为每个业务逻辑提供一个系统线程,通过系统线程切换来弥补同步式I/O调用时的时间开销
                 Node.js:   采用单线程模型,对于所有的I/O都采用异步式的请求方式,避免了频繁的上下文切换,
                             Node.js在执行过程中会维护一个事件队列,程序在执行时进入事件循环等待下一个事件到来,每个异步式I/O请求完成后会被推送到事件队列,等待程序进程进行处理
                             Node.js的异步机制是基于事件的,所有的磁盘IO,、网络通信、数据库查询都以非阻塞方式请求,返回的结果由事件循环来处理

4.安装Node.js
             1.Windows下直接下载安装包即可,会自动安装node.js和npm
             2.Linux下 需要分别安装node.js和npm包管理器
                       sudo apt-get install node
                       sudo apt-get install npm
5.运行Node.js程序
             1.node命令进入perl命令模式,在里面执行程序
             2.创建xx.js,进入到该目录,运行命令  node xx.js 运行程序
             3.node -e "console.log('hello node.js')" 通过 -e参数,直接运行参数后的程序段
6.建立HTTP服务器
             1.创建app.js文件
             2.编写代码
               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");
             3.运行程序
               进入app.js目录,输入命令 :  node app.js
       说明:
             1.该程序调用了Node.js提供的http模块,对所有Http请求答复同样的内容并监听3000端口
             2.浏览器中访问  http://127.0.0.1:3000
             3.程序一直等待,直到Ctrl+C结束程序运行
             4.listen函数中创建了事件监听器,使得node.js进程不会退出事件循环

7.使用supervisor方便代码调试
                     Node.js运行时,修改代码只有重启服务后才能生效,使用supervisor可以让你在修改代码后,不手动重启(supervisor帮你在代码变动时自动重启)即可看到变化
                     1.安装:
                          npm install  -g supervisor
                     2.使用supervisor命令启动app.js
                          supervisor app.js
    Node的小基友supervisor  
     每次修改代码后会自动重启。懒程序员就指望这种省事省力的工具活着了:)
     安装:npm install -g supervisor
     执行:supervisor app.js

8.几组常见概念:
            同步:发出一个请求调用时,在没有得到结果之前,该调用就不返回        ---  即:请求一定要得到回应才返回
            异步:当异步过程调用发出后,调用者不会立即得到结果,实际处理这个调用的部件是在调用发出后,通过状态、通知来通知调用者,或通过回调函数处理这个调用  --- 即:请求发出后,继续执行其他任务,服务器处理完后,通过回调将结果发送给调用者
            阻塞:  阻塞调用    ---  调用结果返回之前,当前线程会被挂起,函数只有在得到结果之后才回返回
            非阻塞:非阻塞调用  ---  调用结果返回之前,函数不会阻塞当前线程,而会立刻返回

      说明:
            同步与异步  --- 关注的时消息通信机制

               同步   ---  就是在发出一个 调用 时,在没有得到结果前,该 调用 就不返回,但一旦调用返回,就得到返回值了(执行完毕了)  ---  调用者 主动等待这个 调用的结果
               异步   ---  调用  发出后,这个调用就直接返回,没有返回结果
                      ---  当一个异步过程调用发出后,调用者不会立刻得到结果,而是在 调用 发出后,被调用者 通过状态、通知来通知调用者,或通过回调函数处理这个调用
               eg:
                  典型的异步编程模型比如Node.js

                  举个通俗的例子:
                  你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,书店老板会说,你稍等,”我查一下",然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。
                  而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过“回电”这种方式来回调。

            阻塞与非阻塞  --- 关注的是程序在等待调用结果(消息、返回值)时的状态

               阻塞调用    ---  指调用之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回
               非阻塞调用  ---  指在不能立刻得到结果之前,该调用不会阻塞当前线程
               eg:
                 你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
                 在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

9.异步式非阻塞I/O与事件式编程
                Node.js最大的特点就是异步式IO与事件紧密结合的编程模式,控制流很大程度上要靠事件和回调函数来组织,一个逻辑要拆分为若干个单元
          阻塞与线程:
            1.阻塞:  线程在执行中如果遇到磁盘读写或网络通信等IO操作,通常要耗费很长时间,这是操作系统会剥夺这个线程的CPU控制权,使其暂停执行,同时将资源让给其他的工作线程,这种线程调度方式称为阻塞。
                      当I/O操作完毕时,操作系统架构这个线程的阻塞状态解除,恢复其对CPU的控制权,令其继续执行,这么IO模式称为同步式I/O或阻塞式IO
            2.异步式IO或非阻塞式IO: 当线程遇到IO操作时,不会以阻塞方式等待IO操作的完成或数据的返回,而只是将IO请求发送给操作系统,继续执行下一条语句,
                                     当操作系统完成IO操作时,以事件形式通知执行IO操作的线程,线程会在特定时候处理这个事件,为了处理异步IO,线程必须有事件循环,不断地检查有没有未处理的事件,依次予以处理

10.回调函数
          eg:以异步方式读取文件为例  --- readfile.js
              var fs = require('fs');
              fs.readFile('file.txt','utf-8',function(err,data){
              if(err){
              console.error(err);
              }else{
              console.log(data);
              }
              });
          说明:这里使用了匿名回调函数,也可将匿名函数单独剥离出来
              function readFileCallBack(err, data) {
              if (err) {
              console.error(err);
              } else {
              console.log(data);
              }
              }
              var fs = require('fs');
              fs.readFile('file.txt', 'utf-8', readFileCallBack);
              console.log('end.');

11.事件
        Node.js所有的异步IO操作在完成时都会发送一个事件到事件队列,事件由EventEmitter对象提供。
        eg:
           上例中的fs.readFile和http.createServer的回调函数都是通过EventEmitter来实现的

          EventEmitter的用法:
                              运行代码后,1秒后控制台输出了 some_event occured。
                原理:
                     event对象注册了事件some_event的一个监听器,然后我们通过setTimeout在1000ms后向event对象发送事件some_event,此时会调用some_event的监听器

          var EventEmitter = require('events').EventEmitter;
          var event = new EventEmitter();

          event.on('some_event',function(){
              console.log('some_event occured');
          });

          setTimeout(function(){
          event.emit('some_event1');
          },1000);

12.Node.js的事件循环机制
          Node.js程序由事件循环开始,到事件循环结束,所有的逻辑都是事件的回调函数,所以Node.js始终在事件循环中,程序入口就是事件循环第一个事件的回调函数。
          事件的回调函数在执行的过程中,可能会发出IO请求或直接发射(emit)事件,执行完毕后在返回事件循环,事件循环会检查事件队列中有没有未处理的事件,直到程序结束。
          Node.js的事件循环对开发中不可见,有libev库实现,libev支持多种类型的事件,在Node.js中均被EventEmitter封装,libev事件循环的每一次迭代,在Node.js中就是一次Tick

13.模块和包
           模块(Module)和包(Package)是Node.js最重要的支柱,开发大规模程序通常需要把各个功能拆分、封装,然后组合起来,模块正是为了实现这种方式而诞生的
           Node.js提供了require函数来调用其他模块,而且模块都是基于文件,机制简单
     模块
         1.模块时Node.js程序的基本组成部分,文件和模块一一对应,一个Node.js文件就是一个模块,这个文件可能是JavaScript代码、JSON或编译过的C/C++扩展
         eg:
            var http = require('http')
            其中http是Node.js的一个核心模块,其内部由C++实现,外部用JavaScript封装,通过require函数获取这个模块,然后使用其中的对象
     创建及加载模块
         1.创建模块   ---         Node.js中一个文件就是一个模块
                      Node.js提供了exports和require两个函数对象
                      exports   ---  模块公开的接口
                      require   ---  用于从外部获取一个模块的接口,即获取模块的exports对象

            eg:
                1.创建一个模块    module.js
                   var name;
                   exports.setName = function(thyName){
                   name =thyName;
                   }
                   exports.sayHello = function(){
                   console.log('Hello '+name);
                   }

                2.同一目录下创建 getModule.js 调用模块
                   var myModule = require('./module');
                   myModule.setName('Jay');
                   myModule.sayHello();

                3.运行结果
                   Hello Jay

                4.程序说明
                   module.js通过exports对象把setName和sayHello作为模块的访问接口,在getModule.js中通过require('./module')加载这个模块,
                   然后就可直接访问module.js中exports对象的成员函数了,npm提供了的上万个模块都是通过这种简单的方式搭建起来的


         2.单次加载   ---   require不会重复加载模块,即:无论调用多少次require,获得的模块都是同一个
                  eg: loadmodule.js
                     var hello1 = require('./module');
                     hello1.setName("aa");
                     var hello2 = require('./module');
                     hello2.setName("bb");
                     hell01.sayHello();
                  结果:
                       Hello bb

                  原因: 变量hello1和hello2指向的时同一个实例,因日hello1.setName的结构被hello2.setName覆盖,最终输出结果由后者决定

         3.覆盖exports
                           只想把一个对象封装到模块中      ----  特别注意:通过覆盖exports方式,防止因单次加载而覆盖变量的值
               eg:
                 1.定义hello.js模块

                  function Hello(){
                  var name;
                  this.setName = function(thyName){
                  name = thyName;
                  }
                  this.sayHello = function(){
                  console.log("Hello "+name);
                  }
                  };

                  推荐方式1:
                  module.exports = Hello;
                  原始方式2:
                  exports.Hello = Hello;


                 2.调用hello.js模块
                  方式1获取Hello模块对象:
                  var Hello = require('./hello');
                  //方式2获取Hello模块对象:
                  //var Hello = require("./hello").Hello;
                  hello = new Hello();
                  hello.setName("aa");
                  hello.sayHello();

              特别说明:
                 1.模块接口的唯一变化是使用module.exports=Hello代替了exports.Hello = Hello,
                   外部引用该模块时,其接口对象就是要输出的Hello对象本事,而是通过原始方式输出的exports
                 2.事实上,exports本身仅仅是一个普通的空对象,即:{},它专门用来声明接口,本质上是通过它
                   为模块闭包的内部建立了一个有限的访问接口,因为它没有任何特殊的地方,所有可用其他东西来代替

14.创建包
          1.包是在模块基础上更深一步的抽象,Node.js中的包类似Java中的类库,它将某个独立的功能封装起来,用于发布、更新、依赖管理和版本控制
          2.包通常是一些模块的集合,在模块的基础上提供了更高层的抽象,相当于提供了一些固定接口的函数库,通过定制package.json,我们可以创建更复杂、更完善、更符合规范的包用于发布。
    1.作为文件夹模块
            最简单的包,就是一个作为文件夹的的模块
         eg:
            1.创建一个somepackage文件夹
            2.在该文件夹下创建index.js
              exports.hello = function(){
              console.log("Hello from package");
              }
            3.在somepackage同级目录下创建getpackage.js文件,调用somepackage包下的模块
              var somePackage = require("./somepackage");
              somePackage.hello();
            4.输出结果
                     Hello

    2.package.json
            1.在上例中的somepackage文件夹下,创建一个package.json文件,内容如下
            {
            "main":"./lib/interface.js"
            }
            2.将index.js重命名为interface.js并放到lib子文件夹下,以同样的方式调用这个包,依然可正常使用
            3.说明:
                   Node.js在调用某个包时,会首先检查包中的package.json文件的main字段,将其作为包的接口模块,
                   如果package.json或main字段不存在,会尝试寻找index.js或index.node作为包的接口,
                   main字段指明了接口模块文件的位置


15.Node.js包管理器 npm 是Node.js官方提供的包管理工具,它已成为Node.js包的标准发布平台,用于Node.js包的发布、传播、依赖控制
                   npm 提供了命令行工具,可以方便的下载、安装、升级、删除包,也可让你作为开发者发布并维护包
    1.获取一个包
                格式:
                     npm [install/i] [package_name]
                eg:  安装express
                     $ npm install express
                  或
                     $ npm i express

                  说明:
                       安装信息显示完后,express就按照成功了,并放置在当前目录的node_modules子目录下,
                       npm在获取express时还将自动解析其依赖,并获取express依赖的mime、mkdirp、qs和connect
    2.本地模式和全局模式

                npm安装包时两种模式: 本地模式和全局模式,
                         本地模式   --- 默认    即:把包安装到当前目录的node_modules子目录下,本地模式不会注册PATH环境变量
                         全局模式   --- npm [install/i] -g [package_name],使用全局模式安装的包不能直接在js文件中require获得,全局模式会注册PATH环境变量
                   对比
                         模式                   可使用require使用            注册PATH
                         本地模式                 是                          否
                         全局模式                 否                          是

                   总结:
                        当我们是要把某个包作为工程运行时的一部分时,通过本地模式获取,如果要在命令行下使用,则使用全局模式安装

                   说明:
                         1.npm默认情况下会从http://npmjs.org搜索和下载包,将包安装到当前目录的node_modules子目录下
                         2.Node.js的require在加载模块时会尝试搜寻node_modules子目录,因此使用npm本地模式安装的包可直接被引用

    3.创建全局链接       npm link命令   ---  在本地包和全局包之间创建符号链接,使得require可以间接使用全局模式安装的包   --- 仅支持Linux,不支持Windows
                    eg:
                       1.全局模式安装express
                          npm install -g express
                       2.创建全局链接
                          npm link express ./node_modules/express -> /usr/local/lib/node_modules/express

    4.包的发布      npm init    ---  根据交互式问答产生一个符合标准的package.json



16.Node.js核心模块
            1.全局对象   JavaScript中的一个特殊对象,它及其所有属性都可在程序的任何地方访问,即全局变量。
                         浏览器JS中,window是全局底线
                         Node.js中的全局对象是global,所有全局变量都是global对象的属性
                         在Node.js中能够直接访问到对象通常都是global的属性,如:console、process等
            2.全局对象与全局变量
                          global最根本的作用是作为全局变量的宿主
                          全局变量  --  在最外层定义的变量,全局对象的属性,隐式定义的变量(未定义直接赋值的变量)
                          Node.js中你不可能在最外层定义变量,因为所有用户代码都是属于当前模块的,而模块本身不是最外层上下文
                特别注意:
                         永远使用var定义变量,以避免引入全局变量,因为全局变量会污染命名空间,提高代码的耦合风险


            3.两个常用全局变量   process 和  console
                1.process  --- 是一个全局变量,即global对象的属性。它用于描述当前的Node.js进程状态,提供了一个与操作系统的简单接口
                    process对象的一些最常用的成员方法
                        process.argv     ---  命令行参数数组
                                               第一个元素是 node, 第二个元素是  脚本文件名,  第三个元素开始每个元素是一个运行参数
                        process.stdout   ---  标准输出流,通常我们使用console.log()打印字符,而process.stdout.write()函数提供了更底层的接口
                        process.stdin    ---  标准输入流,初始时它是暂停的,要想从标准输入流读取数据,必须回复流,并手动编写流的事件响应函数
                                              eg:
                                                 process.stdin.resume();
                                                 process.stdin.on('data',function(data){
                                                 process.stdout.write('read from console: '+data.toString());
                                                 });
                        process.nextTick(callback)   ---  功能是为事件循环设置一项任务,Node.js会在下次事件循环响应时调用callback
                                              eg:把耗时操作拆分为两个事件,减少每个事件的执行时间,提供事件响应速度
                                                 function doSomething(args, callback){
                                                 somethingComplicated(args);
                                                 process.nextTick(callback());
                                                 }

                                                 doSomething(function onEnd(){
                                                 compute();
                                                 })

                2.console   ---  用于提供控制台标准输出
                     console对象常用方法
                         console.log()    ---  向标准输出流打印字符并以换行符结束
                         console.error()  ---  向标准错误流输出
                         console.trace()  ---  向标准错误流输出当前的调用栈


            4.常用工具util
                          util是一个Node.js核心模块,提供常用函数的集合
                1.util.inherits     ---  util.inherits(constructor, superConstructor)是一个实现对象间原型继承的函数
                   eg:
                      var util = require('util');

                      function Base(){
                      this.name = 'base';
                      this.base = 1991;

                      this.sayHello(){
                      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);

                   结果:
                         base
                         Hello base
                         { name: 'base', base: 1990, sayHello: [Function] }
                         sub
                         { name: 'sub' }

                   分析:
                        我们定义了一个基础对象 Base 和一个继承自 Base 的 Sub,Base 有三个在构造函数
                        内定义的属性和一个原型中定义的函数,通过 util.inherits 实现继承。运行结果如下:
                        base
                        Hello base
                        { name: 'base', base: 1991, sayHello: [Function] }
                        sub
                        { name: 'sub' }
                        注意,Sub 仅仅继承了 Base 在原型中定义的函数,而构造函数内部创造的 base 属
                        性和 sayHello 函数都没有被 Sub 继承。同时,在原型中定义的属性不会被 console.log 作
                        为对象的属性输出。如果我们去掉 objSub.sayHello(); 这行的注释,将会看到:
                        node.js:201
                        throw e; // process.nextTick error, or 'error' event on first tick
                        ^
                        TypeError: Object #<Sub> has no method 'sayHello'
                        at Object.<anonymous> (/home/byvoid/utilinherits.js:29:8)
                        at Module._compile (module.js:441:26)
                        at Object..js (module.js:459:10)
                        at Module.load (module.js:348:31)
                        at Function._load (module.js:308:12)
                        at Array.0 (module.js:479:10)
                        at EventEmitter._tickCallback (node.js:192:40)

                2.util.inspect   --- util.inspect(object, [showHidden], [depth], [colors]) 是一个将任意对象转换为字符串的方法,通常用于调试和错误输出
                                     它只是接受一个参数Object,即:要转换的对象
                                     showHidden    ---  可选参数,如果是true,建辉输出更多隐藏信息
                                     depth         ---  表示最大递归层数
                                     color         ---  true用于在终端显示更漂亮效果


            5.事件驱动 events    ---   Node.js最重要的模块,它提供了唯一接口,是Node.js事件编程的基石
                 1.事件发射器
                             events模块只提供了一个对象: events.EventEmitter
                             EventEmitter核心就是事件发射与事件监听器功能的封装,EventEmitter的每个事件由一个事件名和若干个参数组成,
                             对于每个事件,EventEmitter支持若干个事件监听器,当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递

                      eg:
                          var events = require("events");
                          var emitter = new events.EventEmitter();

                          //为emitter添加事件监听
                          emitter.on('someEvent',function(arg1, arg2){
                          console.log("listener1",arg1,arg2);
                          });

                           //为emitter再添加一个事件监听
                          emitter.on('someEvent',function(arg1, arg2){
                          console.log("listener2",arg1,arg2);
                          });

                          //生成一个someEvent事件
                          emitter.emit("someEvent","Jay",1990);

                      结果:
                           listener1 Jay 1990
                           listener2 Jay 1990

                      分析:
                           emitter为事件someEvent注册了两个事件监听器,然后发射了一个someEvent事件,两个事件监听器回调函数被先后调用。



                 2.EventEmitter常用的API         ---   特别注意

                         EventEmitter.on(event,listener)
                                                                       ---  为指定事件注册一个监听器,接受一个字符串event事件名 和 一个回调函数listener
                         EventEmitter.emit(event,[arg1],[arg2],[...])
                                                                       ---  发射名为event的事件,传递若干可选参数到事件监听器的参数表
                         EventEmitter.once(event,listener)
                                                                       ---  为指定事件注册一个单次监听器,即:监听器最多只会触发一次,触发后立刻解除该监听器
                         EventEmitter.removeListener(event,listener)
                                                                       ---   移除指定事件的某个监听器,listener必须是该事件已经注册过的监听器
                         EventEmitter.removeAllListeners([event])
                                                                       ---   移除所有事件的所有监听器,如果指定event,则移除指定事件的所有监听器

                 3.error事件
                            遇到异常时通常会发射error事件,当error被发射时,EventEmitter规定:如果没有相应的监听器,Node.js会把它当做异常,退出程序并打印调用栈。
                            一般要为会发射error事件的对象设置监听器,避免遇到错误后整个程序崩溃
                         eg:
                             var events = require("events");
                             var emitter = new events.EventEmitter();
                             //为error事件注册监听器
                             emitter.on("error",function(){
                             console.log("程序出现了error");
                             })
                             //发射error事件
                             emitter.emit("error");

                 4.继承EventEmitter      ---  大部分时候不会直接使用EventEmitter,而是在对象中继承它,包括:fs、net、http在内的,只要是支持事件响应的核心模块都是EventEmitter的子类


            6.文件系统  fs     ---  fs模块时文件操作的封装,它提供了文件的读取、写入、更名、删除、遍历目录、链接等文件系统操作
                                    fs模块中所有的操作都提供了异步和同步的两个版本
                                    eg:
                                        fs.readFile()      ---    异步读取文件内容
                                        fs.readFileSync()  ---    同步读取文件内容

                        1.fs.readFile(filename,[encoding],[callback(err,data)])    ---  异步读取文件
                                err   ---  表示有没有错误发生,如果有错误发送,err将会是Error对象
                                data  ---  文件内容,如果指定了encoding,data是一个解析后的字符串,否则data将会是以Buffer形式表示的二进制数据
                                eg:
                                    var fs = require("fs");
                                    fs.readFile("context.txt","utf-8",function(err,data)){
                                    if(err){
                                    console.log(err);
                                    }else{
                                    console.log(data);
                                    }
                                    }

                        2.fs.open(path,flags,[model],[callback(err,fd)])
                                 与 C 语言标准库中的 fopen 函数类似。它接受两个必选参数,path 为文件的路径,
                                 flags 可以是以下值。
                                  r :以读取模式打开文件。
                                  r+ :以读写模式打开文件。
                                  w :以写入模式打开文件,如果文件不存在则创建。
                                  w+ :以读写模式打开文件,如果文件不存在则创建。
                                  a :以追加模式打开文件,如果文件不存在则创建。
                                  a+ :以读取追加模式打开文件,如果文件不存在则创建。
                                 mode 参数用于创建文件时给文件指定权限,默认是 0666①。回调函数将会传递一个文件描述符 fd②。

                        3.fs.read(fs,buffer,offset,length,position,[callback(err,bytesRead,buffer)])
                                ---  从指定的文件描述符fs中读取数据并写入buffer指向的缓冲区对象
                                offset   ---  是buffer的写入偏移量
                                length   ---  是要从文件中读取的字节数
                                position ---  是文件读取的起始位置,如果position未null,则会从当前文件指针的位置读取
                                callback ---  回调函数传递bytesRead和buffer,分布表示读取的字节数和缓冲区对象
                              eg:
                                 var fs = require("fs");
                                 fs.open("content.txt",'r',function(err,fd)){
                                 if(err){
                                 console.error(err);
                                 return;
                                 }

                                 var buf = new Buffer(8);
                                 fs.read(fd,buf,0,8,null,function(err,bytesRead,buffer){
                                 if(err){
                                 console.error(err);
                                 return;
                                 }

                                 console.log("bytesRead: "+bytesRead);
                                 console.log(buffer);
                                 });
                                 }


            7.HTTP服务器与客户端
                        Node.js标准库提供了http模块,其中封装了一个高效的HTTP服务器和一个简易的HTTP客户端
                        http.Server是一个基于事件的HTTP服务器,它的核心由Node.js下层C++部分实现,而接口由JavaScript封装,兼顾了高性能与简易性
                        http.request是一个HTTP客户端工具,用于向HTTP服务器发起请求

                    1.HTTP服务器
                                 http.Server是http模块中的HTTP服务器对象,用Node.js做的所有基于HTTP协议的系统,eg:网站、社交应用甚至服务器,都是基于http.Server实现的。
                                 它提供了一套封装级别很低的API,仅仅是流控制和简单的消息解析,所有的高层功能都要通过它的接口来实现。

                             eg:使用http实现一个服务器,通过 127.0.0.1:30000来访问

                                 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>');
                                 }).listener(30000);

                                 console.log("Http Server is listening at port 30000");

                             解析:
                                  http.createServer创建了一个http.Server实例,将一个函数作为HTTP请求处理函数。
                                  函数的两个参数:
                                                req    ---  请求对象
                                                res    ---  响应对象

                          1.http.Server事件
                                  http.Server是一个基于事件的HTTP服务器,所有的请求都被封装为独立的事件,开发者只需对它的事件编写响应函数即可实现HTTP服务器的所有功能,
                                  它继承自EventEmitter,提供了以下几个事件
                                      request:  当客户端请求到来时,该事件被触发,提供两个参数req和res,分别是http.ServerRequest和http.ServerResponse的实例,表示请求和响应信息
                                      connection : 当TCP连接建立时,该事件被触发,提供一个参数socket,为net.Socket的实例
                                                    connection事件的粒度要大于request,因为客户端在Keep-Alive模式下可能会在同一个连接内发送多次请求
                                      close:    当服务器关闭时,该事件被触发。忒别注意:不是在用户连接断开时


                          2.requets: 最常用的事件 :
                                          http提供了 http.createServer([requestListener])         --- 创建一个HTTP服务器并将requestListener作为request事件的监听函数
                                  eg:
                                      var http = require("http");
                                      var server = new http.Server();
                                      server.on("request",function(req,res)){
                                      ......
                                      }
                                      server.listen(8888);
                                  <==等价于==>
                                      var http = require("http");
                                      http.createServer(function(rep,res){
                                      ......
                                      }).listen(8888);


                          3.http.ServerRequest
                                      1.http.ServerRequest是HTTP请求的信息,是后端开发者最关注的内容,它一般由http.Server的request事件发送,作为第一个参数传递
                                      2.ServerRequest提供的一些属性:
                                             HTTP请求分为两部分:请求头(Request Header)和请求体(Request Body).
                                             请求体长度较长,需要一定的时间传输,因此

                                             http.ServerRequest提供了3个事件用于控制请求体传输

                                                data     ---   当请求体数据到来时,该事件被触发。该事件提供一个参数chuk,表示接收到的数据。
                                                               如果该事件没有被监听,那么请求体将会被抛弃,该事件可能会被调用多次。
                                                end      ---   当请求体数据传输完成时,该事件被触发,伺候将不会再有数据到来
                                                close    ---   用户当前请求结束时,该事件被触发。不同于end,如果用户强制终止了传输,也还是调用close

                                             ServerRequest 的属性
                                             名 称                含 义
                                             complete           客户端请求是否已经发送完成
                                             httpVersion        HTTP 协议版本,通常是 1.0 或 1.1
                                             method HTTP        请求方法,如 GET、POST、PUT、DELETE 等
                                             url                原始的请求路径,例如 /static/image/x.jpg 或 /user?name=byvoid
                                             headers            HTTP 请求头
                                             trailers           HTTP 请求尾(不常见)
                                             connection         当前 HTTP 连接套接字,为 net.Socket 的实例
                                             socket             connection 属性的别名
                                             client             client 属性的别名
                          4.获取GET请求内容
                                     Node.js的url模块中的parse函数,提供了获取GET请求内容的功能
                                 说明:
                                      由于GET请求直接被嵌入到路径中,URL是完整的请求路径,包括了?后面的部分,因此可手动解析URL中的参数,或通过url模块解析请求参数
                                   eg:解析URL的GET路径和参数
                                       httpserverrequestget.js
                                        //1.引入所需模块
                                            var http = require("http");
                                            var url = require("url");
                                            var util = require("util");

                                        //2.创建HTTP服务器,并通过url默解析URL和参数
                                            http.createServer(function(req, res){
                                            req.writeHead(200,{"Content-type":"text/plain"});
                                            //3.将解析后的URL参数输出到网页
                                            req.end(util.inspect(url.parse(req.url,true)));
                                            })

                                        结果:http://192.168.1.246:8888/user?name=byvoid&email=byvoid@byvoid.com
                                             { protocol: null,
                                               slashes: null,
                                               auth: null,
                                               host: null,
                                               port: null,
                                               hostname: null,
                                               hash: null,
                                               search: '?name=byvoid&email=byvoid@byvoid.com',
                                               query: { name: 'byvoid', email: 'byvoid@byvoid.com' },
                                               pathname: '/user',
                                               path: '/user?name=byvoid&email=byvoid@byvoid.com',
                                               href: '/user?name=byvoid&email=byvoid@byvoid.com' }

                                        分析:
                                               通过url.parse,原始的path被解析为一个对象,其中
                                               query   ---  对应GET请求的参数内容
                                               path    ---  表示请求路径

                          5.获取POST请求内容   ---             POST请求的内容全部在请求体中
                                    http.ServerRequest 并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作,
                                    譬如上传文件。而很多时候我们可能并不需要理会请求体的内容,恶意的 POST请求会大大消耗服务器的资源。
                                    所以 Node.js 默认是不会解析请求体的,当你需要的时候,需要手动来做

                                    eg:一个简单的获取POST参数的例子,仅做演示:不能在生产环境中使用!!!
                                        var http = require("http");
                                        var querystring = require("querystring");
                                        var util = require("util");

                                        http.createServer(function(req, res){
                                            var post = "";

                                            req.on("data",function(chunk){
                                                post += chunk;
                                            });

                                            req.on("end",function(){
                                               post = querystring.parse(post);
                                               res.end(util.inspect(post));
                                            });
                                        }).listen(8888);

                            特别说明:
                                     在node js中常用的web server是express。
                                     在express中,可以通过req.param('key')来获取post回来的参数。


                          6.http.ServerResponse
                                               这是返回个客户端的信息,决定了用户最终能看到的结果,它由http.Server的request事件发送,作为第二个参数传递
                                   3个主要函数:
                                         response.writeHead(statusCode,[headers])
                                                                                   --- 向请求的客户端发送响应头,该函数在一个请求中最多调用一次,若不调用,则自动生成一个响应头
                                                                                       statusCode   : HTTP状态码 eg: 200,400(未找到)
                                                                                       headers      : 响应头的属性
                                         response.write(data,[encoding])
                                                                                   --- 向请求的客户端发送响应内容,在response.end调用之前,response.write可调用多次
                                                                                       data     :  一个Buffer或字符串,表示要发送的内容,如果data是字符串,需要制定encoding来说明它的编码方式,默认是 utf-8
                                                                                   说明:也可发送二进制文件
                                                                                       eg:
                                                                                            response.writeHead(200,{"Content-type":"image/jpeg"});
                                                                                            response.write(file,"binary");
                                         response.end([data],[encoding])           --- 结束响应,告知客户端所有发送已完成
                                                                                        当所有要返回的内容发送完毕的时候,该函数 必须 被调用一次。它接受两个可选参数,
                                                                                        意义和 response.write 相同。如果不调用该函数,客户端将永远处于等待状态。


                          7.HTTP客户端
                                      http模块提供了http.request和http.get两个函数,作为客户端向HTTP服务器发送请求
                                      http.request和http.get函数    --  都返回一个http.ClientRequest的实例

                                  1. http.request(options, callback)  --- 发起HTTP请求
                                                options  是一个类似关联数组的对象,表示请求的参数,常用参数如下
                                                     host:请求网站的域名或IP地址
                                                     port:请求网站的端口,默认:80
                                                     method:请求方法,默认GET
                                                     path  :请求相对于根的路径,默认 / ,GET请求时包括查询参数QueryString  eg: /search?query=jay
                                                     headers :请求头内容


                                                eg:下面是一个通过 http.request 发送 POST 请求的代码:
                                                   //httprequest.js
                                                   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();
                                                   运行后结果如下:
                                                   array(3) {
                                                   ["name"]=>
                                                   string(6) "byvoid"
                                                   ["email"]=>
                                                   string(17) "byvoid@byvoid.com"
                                                   ["address"]=>
                                                   string(30) "Zijing 2#, Tsinghua University"
                                                   }


                                                callback 是回调函数,传递一个http.ClientResponse实例的参数


                                  2. http.get(options, callback)   ---  http.request的简化版,自动将请求方法设置为了GET请求,同时不需要手动调用req.end()
                                               eg:
                                                  var http = require("http");
                                                  var req = http.get({host:"www.baidu.com"});
                                                  req.on("data",function(res){
                                                  res.setEncoding("utf8");
                                                  res.on("data",function(data){
                                                  console.log(data);
                                                  });
                                                  });

                            两个对象:
                                  http.ClientRequest
                                                  ---  由http.request或http.get返回产生的对象,表示一个已经产生而且正在进行中的HTTP请求。
                                                       它提供了一个response事件,即http.request或http.get第二个参数指定的回调函数的绑定对象参数。

                                  http.ClientResponse
                                                  ---  提供了三个事件data、end、close,分别在数据到达、传输结束和连接结束时触发。
                                                       data事件传递一个参数chunk,表示接收到的数据
                                         常见属性:
                                                  statusCode                  HTTP状态码,200,、400、500
                                                  httpVersion                 HTTP协议版本  1.0或1.1
                                                  headers                     HTTP请求头
                                         常见函数:
                                                  response.setEncoding([encoding])  -- 设置默认编码,当data事件被触发时,数据会以encoding编码,默认是null,不编码,以Buffer形式存储
                                                  response.pause()                  -- 暂停接收数据和发送事件,方便下载功能
                                                  response.resume()                 -- 从暂停的状态中恢复



                          8.Linux下完整版利用formidable上传图片并显示     ---- windows下只需要改变requestHandlers.js中的相关存取路径即可

                              index.js      ---  程序入口
                              route.js      ---  url路由规则模块
                              server        ---  服务器模块
                              requestHandlers.js   --- 不同url的响应处理模块

                              1.index.js
                                        //程序入口,调用其他3个模块
                                        var server = require("./server");
                                            router = require("./route");
                                            requestHandlers = require("./requestHandlers");

                                        //定义handle数组,即各个url请求索要启动的模块函数方法
                                        var handle = {};
                                        handle["/"] = requestHandlers.start;
                                        handle["/start"] = requestHandlers.start;
                                        handle["/upload"] = requestHandlers.upload;
                                        handle["/show"] = requestHandlers.show;

                                        //启动服务器
                                        server.start(router.route,handle);
                              2.route.js
                                        //在路由模块中定义url访问规则
                                        function route(handle,pathname,response,request){
                                            console.log("About to route a request for "+pathname);
                                            if(typeof handle[pathname]=="function"){
                                                handle[pathname](response,request);
                                            }else{
                                            console.log("No request handler found for "+pathname);
                                            response.writeHead(404,{"Content-type":"text/html"});
                                            response.write("404 not found");
                                            response.end();
                                            }
                                        }

                                        //导出route模块
                                        exports.route = route;
                              3.server.js
                                        //引入生成页面需要的http和url模块
                                        var http = require("http");
                                            url = require("url");
                                        function start(route,handle){
                                        function onRequest(request,response){
                                        //通过url模块提供的方法,获取url路径
                                        var pathname = url.parse(request.url).pathname;
                                        console.log("Request for "+pathname+" received");
                                        //调用路由模块制定的访问规则
                                        route(handle,pathname,response,request);
                                        }
                                        //创建并启动服务器
                                        http.createServer(onRequest).listen(8888);
                                        console.log("Server has started");
                                        }

                                        //导出模块
                                        exports.start = start;
                              4.requestHanlder.js
                                        //定义每个request请求的处理,即不同url请求返回不同的页面
                                        var querystring = require("querystring");
                                            fs = require("fs");
                                            formidable = require("formidable");

                                        //start请求对应的响应
                                        function start(response){
                                        console.log("Request handler start was called");
                                        //定义请求响应的的页面
                                        var body = '<html>'+
                                                    '<head>'+
                                                            '<meta http-equiv="Content-Type" content="text/html; '+
                                                                    'charset=UTF-8" />'+
                                                                            '</head>'+
                                                                                    '<body>'+
                                                                                            '<form action="/upload" enctype="multipart/form-data" '+
                                                                                                    'method="post">'+
                                                                                                            '<input type="file" name="upload" multiple="multiple">'+
                                                                                                                    '<input type="submit" value="Upload file" />'+
                                                                                                                            '</form>'+
                                                                                                                                    '</body>'+
                                                                                                                                            '</html>';

                                            response.writeHead(200, {"Content-Type": "text/html"});
                                                response.write(body);
                                                    response.end();
                                        }

                                        //upload请求对应的响应  --- 接收form表单上传的图片
                                        function upload(response,request){
                                            console.log("Request handler upload was called");
                                            //调用formidable对form表单进行解析和处理
                                            var form = formidable.IncomingForm();
                                            //定义上传路径
                                            form.uploadDir = "/home/develop/tmp/node/test";
                                            //处理表单内容
                                            form.parse(request,function(error,fields,files){
                                            console.log("about to parse form ");
                                            console.log(files.upload.path);
                                            fs.renameSync(files.upload.path,"/home/develop/tmp/node/test/test.jpg");
                                            response.writeHead(200, {"Content-Type": "text/html"});
                                            response.write("received image:<br/>");
                                            response.write("<img src='/show' />");
                                            response.end();
                                            });
                                        }

                                        //图片显示的请求   --- 将上传的图片显示到网页
                                        function show(response){
                                          console.log("Request handler show was called");
                                          fs.readFile("/home/develop/tmp/node/test/test.jpg","binary",function(error,file){
                                          if(error){
                                             response.writeHead(500,{"Content-type":"text/plain"});
                                             resonse.write(error+"\n");
                                             response.end();
                                          }else{
                                          //如果没有异常,就将图片写到网页
                                          response.writeHead(200,{"Content-type":"image/jpeg"});
                                          response.write(file,"binary");
                                          response.end();
                                          }
                                          });
                                        }

                                        //导出模块,供其他模块使用
                                        exports.start = start;
                                        exports.upload = upload;
                                        exports.show = show;

                              5.访问   http://192.168.1.246:8888




            17.使用Node.js进行Web开发    ---  使用Node.js实现一个微博系统
                       功能:
                             路由控制、页面模板、数据库访问、用户注册、登录、用户会话等内容
                       技术:
                             Express框架、MVC设计模式、ejs或jade模板引擎、MongoDB数据库操作
                       MVC架构:
                             由模板引擎生成HTML页面,由控制器定义路径解析,由对象关系模型处理数据访问

                       1.使用http模块
                                  Node.js提供了http模块,这是一个HTTP服务器内核的封装,你可用它做任何HTTP服务器能做的事,甚至实现一个HTTP代理服务器
                                  eg:获取表单post的数据title和text
                                      var http = require("http");
                                      var querystring = require("querystring");

                                      var server = http.createServer(function(req,res){
                                         var post = "";
                                         req.on("data",function(chunk){
                                             post +=chunk;
                                         });

                                         req.on("end",function(){
                                             post = querystring.parse(post);

                                             res.write(post.title);
                                             res.write(post.text);
                                             res.end();
                                         });
                                      }).listen(3000);


                       2.Express框架      ---    最稳定和广泛的,Node.js官方推荐的Web开发框架

                               提供的功能     ---  多数功能只是对HTTP协议中常用操作的封装,更多的功能需要插件或整合其他模块来完成
                                     路由控制
                                     模板解析支持
                                     动态视图
                                     用户会话
                                     CSRF保护
                                     静态文件服务
                                     错误控制器
                                     访问日志
                                     缓存
                                     插件支持

                                    eg:实现上面用http模块实现的功能
                                        var express = require("express");
                                        var app = express.createServer();

                                        app.use(express.bodyParse);
                                        app.all("/",function(req,res){
                                           res.send(req.body.title+req.body.text);
                                        });
                                        app.listen(3000);
                                    解析:
                                         不用手动编写req的事件监听器,只需加载express.bodyParse()就能直接通过req.body获取Post的数据了

                       3.快速开发
                                1.全局方式安装express
                                             npm install -g express
                                             npm install -g express-generator
                                       查看node版本
                                                    node -v
                                       查看express版本
                                                    express -V
                                2.使用express命令创建工程  --- 创建一个express+ejs项目
                                             express -e microblog
                                             说明:
                                                  express -e project_name     --- 使用ejs模块引擎创建express项目
                                                  express -J project_name     --- 使用jade模块引擎创建express项目
                                             express的版本不是通常的 “-v” 来查看,而是 “-V”

                                3.进入创建的项目 microblog,运行
                                             npm install
                                4.启动服务器  --- 进入microblog项目,运行命令
                                             npm start
                                5.访问项目
                                             IP:3000

                                6.Express项目结构:
                                             1.aap.js               --   项目入口
                                             2.node_modules         --   存放项目的依赖库
                                             3.package.json         --   项目依赖配置及开发者信息
                                             4.public               --   静态文件  css,js,img等
                                             5.routes               --   路由文件,类似MVC中的Controller控制器
                                             6.views                --   页面文件(ejs或jade的模板,默认是:jade)

                                7.常用文件解析
                                             1.routes/index.js   --- 路由文件,相当于控制器,用于组织展示的内容。
                                                  var express = require('express');
                                                  var router = express.Router();

                                                  /* GET home page. */
                                                  router.get('/', function(req, res, next) {
                                                    res.render('index', { title: 'Express' });
                                                  });

                                                  module.exports = router;

                                             说明:
                                                  router.get('/',function(...))  将 / 路径映射到function函数下,
                                                  res.render('index',{title:'Express'}) 调用模板解析引擎,解析名为index的模板,
                                                  并传入一个对象作为参数,这个对象只有一个属性,即 title:'Express'

                                             2.views/index.ejs    --- 模板文件,即  routes/index.js中调用的模板,用于显示res.render第二个参数传入的对象的属性
                                                  <h1><%= title %></h1>
                                                  <p>Welcome to <%= title %></p>

                       4.路由控制            ---  网站架构最核心的部分,即 MVC架构中的控制器
                                  工作原理:
                                          访问 http://localhost:3000,浏览器会向服务器发送以下请求:
                                          GET / HTTP/1.1
                                          Host: localhost:3000
                                          Connection: keep-alive
                                          Cache-Control: max-age=0
                                          User-Agent: Mozilla/5.0 AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.142
                                          Safari/535.19
                                          Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
                                          Accept-Encoding: gzip,deflate,sdch
                                          Accept-Language: zh;q=0.8,en-US;q=0.6,en;q=0.4
                                          Accept-Charset: UTF-8,*;q=0.5
                                          其中第一行是请求的方法、路径和 HTTP 协议版本,后面若干行是 HTTP 请求头。

                                       1.请求网页动态数据
                                          app.js 中  app.use('/', routes);调用routes/index.js模块,
                                          在routes/index.js模块中,router.get('/',function(...)) 作用是 对 / 路径的GET请求由function进行处理,
                                          通过res.render('index',{title:'Express'}) 调用模板解析引擎,解析名为index的模板,传递title变量,最终视图模板生成HTML页面,返回给浏览器

                                       2.请求静态数据
                                          浏览器接收到内容后,分析发现需要获取/stylesheets/style.css,因此会再次向服务器发起请求。
                                          app.js通过app.use(express.static(path.join(__dirname, 'public')));  --- 配置了静态文件服务器,
                                          因此/stylesheets/style.css会定向到app.js所咋目录的子目录中的文件public/stylesheets/style.css,返回给浏览器

                                             Express创建的网站架构图:
                                                                 ---------------------------------
                                                                 |              浏览器           |
                                                                 ---------------------------------
                                                                                  |
                                                                                  |
                                                                 ---------------------------------
                                                                 |            路由控制器         |
                                                                 ---------------------------------
                                                                  |               |             |
                                                                  |               |             |
                                                               ----------    -----------   ------------
                                                                模板引擎       静态文件      对象模型
                                                               ----------    -----------   ------------

                                             典型的MVC架构,浏览器发起请求,由路由控制接受,根据不同的路径定向到不同控制器。
                                             控制器处理用户具体请求,可能会访问数据库中对象(对象模型),控制器还有访问模板引擎,
                                             生成视图的HTML,最后再由控制器返回给浏览器,完成一次请求。

                                  路由规则:
                                           在routes/index.js中添加相应的url处理的路由规则
                                           eg:添加/hello的处理   --- 向页面输出当前时间
                                              var express = require('express');
                                              var router = express.Router();

                                              /* GET home page. */
                                              router.get('/', function(req, res, next) {
                                                res.render('index', { title: 'Express' });
                                              });

                                              /*GET hello page*/
                                              router.get('/hello',function(req, res){
                                                res.send("The time is "+new Date().toString());
                                              });

                                              module.exports = router;

                                  路径匹配:
                                           Express支持更高级的路径匹配模式   --- 支持JavaScript正则表达式
                                           eg:
                                               想要展示一个用户的个人页面,路径为 /user/[username]
                                               路由规则:
                                               router.get('/user/:username', function(req,res){
                                                   //向请求页面输出信息
                                                   res.send("user: "+req.params.username);
                                               });

                                  REST风格的路由规则:
                                            REST   ---  表征状态转移,它是一种基于HTTP协议的网络应用的接口风格,充分利用HTTP的方法实现统一风格接口的服务

                                            常用的4个HTTP方式:
                                               GET       :  获取
                                               POST      :  新增
                                               PUT       :  更新
                                               DELETE    :  删除

                                            Express对每种HTTP请求方法都设计了不同的路由绑定函数:
                                              方式                       绑定函数
                                               GET                     router.get(path,callback)
                                               POST                    router.post(path,callback)
                                               PUT                     router.put(path,callback)
                                               DELETE                  router.delete(path,callback)
                                               所有方式                router.all(path,callback)   --- 支持把所有的请求方式绑定到同一个响应函数

                                  控制权转移:
                                             Express支持同一路径绑定多个路由响应函数,提供了路由控制权转移方法,即:回调函数的第三个参数 next,
                                             通过调用next(),会将路由控制权转移给后面的规则    --- 类似 J2EE中的过滤器放行功能
                                             eg:router/index.js中

                                                router.all("/user/:username",function(req, res, next){
                                                console.log("all methods captured");
                                                //该路由规则处理完后,将路由控制权交给下一个路由规则   --- 放行
                                                next();
                                                });

                                                router.get("/user/:username",function(req, res){
                                                res.send("user: "+req.params.username);
                                                });

                                             解析:
                                                  同一个/user/:username绑定了两个路由响应函数,通过all的第三个参数 next,
                                                  处理完后,通过next()方法 将控制权转移类下一个路由规则,get方式才能处理该url
                                               注意:
                                                   get方法处理完后没有转移控制权(放行),即:后面如果还有其他的绑定函数,对/user/:username将不能处理


                                             next控制权转移是一个非常有用的工具,可以让我轻易实现中间件,而且还能提高代码的复用程度
                                                eg:
                                                   针对一个用户查询信息和修改信息的操作,分别对应了GET和PUT操作,而两者公有的步骤是检查用户是否存在,可用next()方法实现

                                                   var users = {
                                                   "jay":{
                                                   name:"Jay",
                                                   website:"http://www.feicool.com"
                                                   }
                                                   }

                                                   router.all("/user/:username", function(req, res, next){
                                                   //检查用户是否存在
                                                   if(users[req.params.username]){
                                                   //存在则交给下一个路由规则处理
                                                   next();
                                                   }else{
                                                   //不存在则抛出异常
                                                   next(new Error(req.params.username+" does not exist."));
                                                   }
                                                   });

                                                   //展示用户信息
                                                   router.get("/user/:username",function(req, res){
                                                   res.send(JSON.stringify(users[req.params.username]));
                                                   });

                                                   //修改用户信息
                                                   router.put("/user/:username", function(req, res){
                                                   res.send("Done !!!");
                                                   });

                                             解析:
                                                  router.all 这个路由规则起到了中间件的作用,把相似请求的相同部分提取出来,有利于代码维护,降低了代码耦合度


                       5.模板引擎          ---  MVC架构中的视图,将页面模板和要显示的数据结合起来生成HTML页面,由服务器端解析成HTML后传递给客户端。
                                  1.在Node.js的MVC架构中,模板引擎包含在服务器端,控制器得到用户请求后,从模型获取数据,调用模板引擎,
                                                                 模板引擎数据和页面模板为输入,生成HTML页面,然后返回给控制器,由控制器交回客户端。

                                             ------------    -----------
                                              页面模板          数据  ----------- |         |
                                             ------------    -----------          |         |
                                                  |              |                |         |
                                             ----------------------------         |         |
                                                      模板引擎                    | 控制器  |
                                             ----------------------------         |         |
                                                        |                         |         |
                                             ----------------------------         |         |
                                                      HTML页面        ----------- |         |
                                             ----------------------------

                                                            模板引擎在MVC架构中的位置
                                  2.使用模板引擎
                                                  ejs和jade

                                             1.在app.js中通过以下语句设置了模板引擎和页面模板位置
                                                app.set('views',__dirname+'/views');
                                                app.set('view engine', 'ejs');
                                               说明:
                                                    要使用的模板引擎是ejs,页面模板在views子目录下

                                             2.在routes/index.js的exports.index函数中调用模板引擎,并传递参数对象
                                                 res.render('index',{title:'Express'})
                                               说明:
                                                    res.render   --- 功能是调用模板引擎,并将其产生的页面直接返回给客户端。
                                                                     两个参数:
                                                                         参数一:模板名称,即views目录下的模板文件名,不包含扩展名
                                                                         参数二:传递给模板的数据对象,json格式,用于模板翻译

                                             3.ejs只有3中标签
                                                   <% code %>     : JavaScript代码
                                                   <%= code %>    : 显示替换过HTML特殊字符的内容
                                                   <%- code %>    : 显示原始HTML内容

                                  3.页面布局
                                              layout.ejs页面布局模板,它描述了整个页面的框架结构,默认情况下每个单独的页面都继承自这个框架。替换掉<%- body %>这个部分
                                              这个功能通常非常有用,因为一般为了保持整个网站的一致风格,HTML 页面的<head>部分以及页眉页脚中的大量内容是重复的,
                                              因此我们可以把它们放在 layout.ejs 中
                                              关闭这个功能,可在app.js中的app.configure中添加一下内容:
                                                    app.set('view options',{
                                                      layout:false
                                                    })

                                              另一种情况是,一个网站可能需要不止一种页面布局,例如网站分前台展示和后台管理
                                              系统,两者的页面结构有很大的区别,一套页面布局不能满足需求。这时我们可以在页面模
                                              板翻译时指定页面布局,即设置 layout 属性,例如:
                                              function(req, res) {
                                              res.render('userlist', {
                                              title: '用户列表后台管理系统',
                                              layout: 'admin'
                                              });
                                              };
                                              这段代码会在翻译 userlist 页面模板时套用 admin.ejs 作为页面布局。

                                  4.片段视图     --- partials
                                               它就是一个页面的片段,通常是重复的内容,用于迭代显示。
                                               通过它你可以将相对独立的页面块分割出去,而且可以避免显式地使用 for 循环。
                                               eg:在 app.js 中新增以下内容:
                                               app.get('/list', function(req, res) {
                                               res.render('list', {
                                               title: 'List',
                                               items: [1991, 'byvoid', 'express', 'Node.js']
                                               });
                                               });
                                               在 views 目录下新建 list.ejs,内容是:
                                               <ul><%- partial('listitem', items) %></ul>
                                               同时新建 listitem.ejs,内容是:
                                               <li><%= listitem %></li>
                                               访问 http://localhost:3000/list,可以在源代码中看到以下内容:
                                               <!DOCTYPE html>
                                               <html>
                                               <head>
                                               <title>List</title>
                                               <link rel='stylesheet' href='/stylesheets/style.css' />
                                               </head>
                                               <body>
                                               <ul><li>1991</li><li>byvoid</li><li>express</li><li>Node.js</li></ul>
                                               </body>
                                               </html>

                                         解析:
                                              partial是一个可在视图中使用的函数,接受两个参数
                                                        参数一:片段视图名称
                                                        参数二:对象或数组
                                                                如果是对象,则片段视图中上下文变量引用的就是这个对象
                                                                如果是数组,则其中每个元素依次迭代应用到片段视图
                                                        片段视图中的上下文变量就是视图文件名  ,上例中的 'listitem'



                       6.建立微博网站        --- 初级完整项目:参考附件
                                  功能分析:
                                           首先,微博应该以用户为中心,因此需要有用户的注册和登录功能。微博网站最核心的功能是信息的发表,这个功能涉及许多
                                           方面,包括数据库访问、前端显示等。一个完整的微博系统应该支持信息的评论、转发、圈点用户等功能,
                                           但出于演示目的,我们不能一一实现所有功能,只是实现一个微博社交网站的雏形.
                                  路由规划:
                                           根据功能设计划分路由url规则
                                              /:首页
                                              /u/[user]:用户的主页
                                              /post:发表信息
                                              /reg:用户注册
                                              /login:用户登录
                                              /logout:用户登出
                                           根据用户状态细分
                                              1.发表信息以及用户登出页面必须是已登录用户才能操作的功能。
                                              2.用户注册和用户登入所面向的对象必须是未登入的用户。
                                              3.首页和用户主页则针对已登入和未登入的用户显示不同的内容。

                                           打开 routes/index.js添加路由规则

                                  界面设计:
                                          1. 从http://twitter.github.com/bootstrap/下载bootstrap.zip
                                          2. 其中所有的 JavaScript 和 CSS 文件都提供了开发版和产品版,前者是原始的代码,后者经过压缩,
                                             文件名中带有 min。将 img 目录复制到工程 public 目录下,将 bootstrap.css、bootstrap-responsive.css
                                             复制到 public/stylesheets 中,将 bootstrap.js 复制到 public/javascripts 目录中,
                                          3. 然后从http://jquery.com/下载一份最新版的 jquery.js 也放入 public/javascripts 目录中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值