码迷,mamicode.com
首页 > Web开发 > 详细

【读书笔记】《Node.js入门经典》

时间:2015-05-08 09:41:15      阅读:200      评论:0      收藏:0      [点我收藏+]

标签:node.js   http   program   require   

Node.js学习笔记

1. Node.js介绍

var http = require(‘http‘);
http.createServer(function(req,res){
   res.writeHead(200,{‘Content-Type‘:‘text/plain‘});
   res.end("I‘m writting program with Nodejs");
}).listen(3000,"127.0.0.1");
console.log("Server is running at ‘127.0.0.1:3000‘")

打开页面,显示Hello World

  • Node.js基于V8 JavaScript引擎,具有一次做多件事的能力(并发),用于简单构建快速的、可扩展的网络应用程序。
  • Node.js使用事件驱动的、非阻塞的I/0模型,这让其既轻量又高效,是运行于不同发布设备上的数据密集型实时应用程序的完美平台。
  • Node的事件化的I/O模型无需担心互锁和并发,这在多线程异步I/O常见。
  • Node适用于当应用程序需要在网络上发送和接受数据时。

2. npm(Node Package Manager)

安装模块:npm install [module_name],下载成功之后,在程序中使用需要请求才可以使用,例如:

var module_name = require(‘module_name‘);

例如:使用underscore.js库,首先需要在命令行中npm install underscore,然后require之后就可以使用。

var _ = require(‘underscore‘);
_.each([1,2,3],function(num){
  console.log("underscore.js says " + num);
})

2.1 npm常用命令

sudo npm install -g moduleName //全局安装模块
npm uninstall moduleName //卸载模块
npm docs moduleName //查询模块主页
npm view moduleName // 查看模块的package.json文件夹
npm view moduleName version// 查看模块最新版本
npm list //查看当前目录已经安装的node包
npm bugs moduleName //查看项目的bug
npm search moduleName //在命令行搜索npm包

3. I/O的不可预测性

  • 每次之下以下代码,响应时间都会不不同,而且响应的顺序也会发生变化。
  • Web服务器响应时间的影响因素:解析DNS请求的时间、服务器繁忙程度、网络的繁忙程度、要应答数据大小、带宽、输出数据、为响应而服务的软件的效率
var http = require(‘http‘),
   urls = [‘taobao.com‘,‘baidu.com‘,‘douban.com‘];
function fetchPage(url){
   var start = new Date();
   http.get({host:url},function(res){
       console.log("got response: "+url);
       console.log("request tock ",new Date()-start,‘ms‘);
   })
}
for(var i = 0; i < urls.length; i++){
  fetchPage(urls[i])
}

4. 回调

  • 类似于点餐,一个人点完餐拿号之后,就去一旁等着,其他人可以继续点餐拿到;等到饭菜做好之后叫号,那个人再拿号过来领餐。
    • 很好的支持并发、异步
    • 回调:将函数B作为参数传递给函数A,在函数A执行完成后调用B执行。下面的例子会在隐藏完段落<p>之后再弹出警告框。
    • 控制顺序,可以使用回调的回调:从数据库读取数据然后通过回调,调用另一个函数处理这些数据。
$(function () {
   $(‘p‘).hide(‘fast‘,function(){
       alert("The paragraph is now hidden");
   });
});

4.1 回调剖析

函数可以作为参数传递到另一个函数中,然后被调用。

function haveBreakfast(food,drink,callback){
   console.log("Having breakfast of " + food + ", " + drink);
   if(callback && typeof(callback) === "function"){
       callback();
   }
}
haveBreakfast("toast","coffee",function(){
   console.log("finished breakfast!")
})

4.2 Node在读写文件使用回调

  • Node到处都使用回调,尤其是在I/O操作发生的地方
  • err保存在读取文件时返回的错误,data保存读取文件返回的数据
  • 一旦文件被读取,回调就会被调用
var fs = require("fs");
fs.readFile("somefile.txt","utf8",function(err,data){
   if(err) throw err;
   console.log(data);
});

4.3 Node在HTTP使用回调

  • 回调函数的调用发生在远程服务器发回响应之后,而不是之前。
  • http.get()方法可以用来请求Web服务器获得响应数据以便使用
var http = require("http");
http.get({host:"baidu.com"}, function (res) {
   console.log("Got response: " + res.statusCode);//状态码
   console.log("Headers: " + res.headers);//头信息
   res.on(‘data‘,function(d){//返回网页源代码
       process.stdout.write(d);
   })
}).on("error",function(e){
   console.log("Got error: " + e.message);
});

4.4 回调顺序

  • 4个不同的I/O操作都在发生,他们都在使用回调
  • 发生顺序不可预知
  • 从磁盘读取的两个文件先返回,因为无需进入网络
  • 回调首先是负责解决不可预测性的方法,它也是处理并发(一次做多件事情)的高效方法。
var fs = require("fs"),
   http = require("http");
http.get({host:"taobao.com"}, function (res) {
   console.log("taobao.com");
}).on("error",function(e){
   console.log(e.message);
})
fs.readFile("a.txt","utf8",function(err,data){
   if(err) throw err;
   console.log(data);
})
http.get({host:"baidu.com"}, function (res) {
   console.log("baidu.com");
}).on("error",function(e){
   console.log(e.message);
})
fs.readFile("b.txt","utf8",function(err,data){
   if(err) throw err;
   console.log(data);
})

4.5 同步与异步代码

  • 同步意味着每次执行一个操作,在一个操作完成之前,代码的执行会被堵塞,无法移到下一个操作上。
  • Node.js运行在单一的进程中,并要求开发者使用异步的编程风格:异步的调用回调。
  • 爬虫的小例子
  • 一旦调用了,两个函数都会侦听远程服务器的返回,并以此触发回调函数。
  • 脚本无需等待某个操作的结果才能继续前进,因为操作结果会在事件发生时由回调来处理。
  • 事件循环:事件循环使得系统可以将回调函数先保存起来,而后当事件在将来发生时再运行,因为回调函数的执行被推迟到事件发生之后,
  • 于是就无需停止执行,控制流可以返回到Node运行时环境,从而让其他事情发生.
  • 核心思想:将代码围绕着事件来架构,而不是按照期待中的输入顺序来架构。
  • Node.js不适合:处理大量数据或者长时间运行计算等,Node.js旨在网络中推送数据并瞬间完成。
var http = require("http");
function fetchPage(){
   console.log("fetching page");
   http.get({host:"douban.com"},function(res){//要爬的网址
       console.log("data returned from page");
       res.on("data",function(d){//返回该网页的源码
           process.stdout.write(d)//将网页源码输出
       })
   }).on("error",function(e){
       console.log("There was an error " + e)
   })
}
function fetchapi(){
   console.log("fetching api");
   http.get({host:"movie.douban.com"},function(res){
       console.log("data returned from the api");
   }).on("error",function(e){
       console.log("There was an er ror " + e);
   })
}
fetchPage();
fetchapi();

5. HTTP

  • HTTP定义了服务器和客户端在通信的时候,如何发送和接受数据。
  • Node.js可以创建HTTP服务器和HTTP客户端
  • HTTP头发送的是附加的信息,包括内容类型、服务器发送响应的日期以及HTTP状态码。
  • 默认发送了:
    • HTTP/1.1 200 OK HTTP版本是1.1,状态码200表示成功响应
      • Connection: keep-alive 连接是持久的,是HTTP1.1开始具备的,让很多实时功能成为可能。
  • 检查响应头,在chrome中安装Live HTTP Headers扩展插件

    var http = require(‘http‘);
    http.createServer(function(req,res){//创建了新的Web服务器对象,匿名函数告诉Web服务器,每当接收到请求时会发生什么。
    res.writeHead(501,{‘Content-Type‘:‘text/plain‘});//加入响应HTTP头
    res.writeHead(301,{‘Location‘:‘http://www.baidu.com‘});//重定向,发送了301响应代码以及位置头,告诉客户端重定向到哪里。
    res.end("I‘m writting program with Nodejs");
    }).listen(3000,"127.0.0.1");//定义了Web服务器的端口和主机
    console.log("Server is running at ‘127.0.0.1:3000‘")
    

    5.1 HTTP响应状态代码

    100 客户端可以继续
    101 指示服务器正根据 Upgrade 头切换协议
    200 请求正常成功
    201 指示请求成功并在服务器上创建了一个新资源
    202 指示已接受请求进行处理但处理尚未完成
    203 指示客户端呈现的元信息并不源自服务器
    204 指示请求成功但没有返回新信息
    205 指示代理应该 重置导致请求被发送的文档视图
    206 指示服务器已完成对资源的部分 GET 请求
    300 请求的资源对应于表示形式集合中的某种表示形式,每种表示形式都有自己的特定位置
    301 指示已经将资源永久地移动到了某个新位置,并且将来的引用应将新 URI 用于其请求
    302 指示已经将资源暂时地移动到了另一个位置,但将来的引用仍应使用原来的 URI 访问该资源。 保留此定义是为了向后兼容。SC_FOUND 现在是首选定义
    303 指示可在另一个 URI 之下找到该请求的响应
    304 指示条件 GET 操作发现资源可用但不可修改
    305 指示必须 通过 Location 字段给定的代理访问请求资源
    307 指示请求的资源暂时驻留在另一个 URI 之下。临时 URI 应该 通过响应中的 Location 字段提供
    400 指示客户端发送的请求在语法上不正确
    401 指示请求需要进行 HTTP 验证
    402 保留此代码以备将来使用
    403 指示服务器理解请求但拒绝完成它
    404 指示请求的资源不可用
    405 指示 Request-Line 中指定的方法不支持 Request-URI 标识的资源
    406 指示请求标识的资源只能生成响应实体,根据请求中发送的 accept 头,这些响应实体具有不可接受的内容特征
    407 指示客户端必须 首先通过代理验证其自身
    408 指示客户端没有在服务器准备等待的时间内生成请求
    409 指示由于与当前资源状态冲突请求无法完成
    410 指示资源在服务器上不再可用并且不知道转发地址。应该 认为此条件是永久性的
    411 指示在没有定义 Content-Length 的情况下无法处理请求
    412 指示在服务器上测试一个或多个请求头字段中给出的前提时,该前提被求值为 false
    413 指示因为请求实体大于服务器愿意或能够处理的实体,所以服务器拒绝处理请求
    414 指示因为 Request-URI 的长度大于服务器愿意解释的 Request-URI 长度,所以服务器拒绝为请求提供服务
    415 指示因为请求实体的格式不受请求方法的请求资源支持,所以服务器拒绝为请求提供服务
    416 指示服务器无法服务于请求的字节范围
    417 指示服务器无法服务于请求的字节范围
    500 指示 HTTP 服务器内存在错误使服务器无法完成请求
    501 指示 HTTP 服务器不支持完成请求所需的功能
    502 指示 HTTP 服务器在充当代理或网关时从它参考的服务器接收到一个无效响应
    503 指示 HTTP 服务器暂时过载,并且无法处理请求
    504 指示服务器在充当网关或代理时没有从上游服务器接收到及时的响应
    505 指示服务器不支持或拒绝支持请求消息中使用的 HTTP 协议版本

5.2 使用URL模块响应不同的请求

路由是指应用程序要响应的请求

var url = require("url");
var requestURL = "http://daily.zhihu.com/story/4688312";
console.log(url.parse(requestURL).hostname);//获取主机名称
console.log(url.parse(requestURL).port);//获取端口
console.log(url.parse(requestURL).pathname);//获取路径名称

下面例子通过URL创建对于许多不同请求进行响应的服务器,这是基础层面的,比较复杂,可以通过使用Express

var http = require("http");
var url = require("url");
http.createServer(function(req,res){
   var pathname = url.parse(req.url).pathname;
   if(pathname ===‘/‘){
       res.writeHead(200,{"Content-Type":"text/plain"});
       res.end("home page\n")
   }
   if(pathname === ‘/about‘){
       res.writeHead(200,{"Content-Type":"text/plain"});
       res.end("about")
   }
   if(pathname === ‘/yll‘){
       res.writeHead(200,{"Content-Type":"text/plain"});
       res.end("my name is yll.")
   }
   if(pathname === "/redirect"){
       res.writeHead(301,{"Location":"/"});
       res.end();
   }
   else{
       res.writeHead(404,{"Content-Type":"text/plain"});
       res.end("page not found!");
   }
}).listen(3000,"127.0.0.1");
console.log("server running at http://127.0.0.1:3000");

5.3 使用Node.js创建HTTP客户端

  • HTTP客户端可以对任何服务请求响应的东西:Web浏览器、搜索引擎机器人、电子邮件客户端、Web Scraper
  • HTTP客户端用来:
  • 监控服务器的正常工作时间、爬取不能通过API获取的Web内容、创建将来自Web的两个或者更多信息来源组合在一起的mashup,调用其他Web Service的API。
  • 通过使用http.get()方法实现对服务器的GET请求,需要提供options对象(主机、端口、路径)
  • 下面例子创建一个HTTP客户端,并获取一个页面的HTTP状态码并检查响应代码
var http = require("http");
var options = {
   host:"shapeshed.com",
   port:80,
   path:"/"
};
http.get(options,function(res) {
   if (res.statusCode === 200) {
       console.log("The site is up!");
   }
   else {
       console.log("The Site is down!");
   }
}).on("error",function(e){
   console.log("error");
})

5.4 将函数发布为URL服务,提供客户端接口

  • http://127.0.0.1:1333/?a=3&b=8
  • 得到结果:11
  • .cmd文件让程序自动执行: “%~dp0node.exe” “%~dp0app.js”
  • 在文件夹中放入node.exe和app.js,把以上语句保存为.cmd文件,则自动执行

    var HTTP  = require(‘http‘);
    var queryServer = HTTP.createServer(queryConnectionHandler);
    queryServer.listen(1333, function (err) {
    console.log(‘query server listen at: %d‘, 1333);
    if (err) throw err;
    });
    function queryConnectionHandler(req, res) {
    var args   = require(‘url‘).parse(req.url.toLowerCase(), true);
    var a = args.query.a;//获得参数a
    var b = args.query.b;//获得参数b
    var states = {
       error: function () {
           res.writeHead(200, {
               ‘Content-Type‘: ‘text/plain‘,
               ‘Access-Control-Allow-Origin‘: ‘*‘
           });
           res.end(‘false‘);
       },
       ok: function () {
           res.writeHead(200, {
               ‘Content-Type‘: ‘text/plain‘,
               ‘Access-Control-Allow-Origin‘: ‘*‘
           });
           res.end(‘ok‘);
       },
       message: function (obj) {
           res.writeHead(200, {
               ‘Content-Type‘: ‘application/json;charset=utf-8‘,
               ‘Access-Control-Allow-Origin‘: ‘*‘
           });
           res.end(JSON.stringify(obj));
       }
    };
    // 这里写具体实现
    states.message(addTwoInt(a,b));
    // states.message(/* 这里返回值 */);
    }
    function addTwoInt(a,b){
    a = parseInt(a);
    b = parseInt(b);
    var c = a + b;
    return c;
    }
    

5.5 两个js文件相互调用函数

  • a.js
  • 将函数或者变量exports出去,以供别的js文件调用

    function add(teacher){
    console.log("Add Teacher:" + teacher)
    }
    exports.add = add;

    b.js

    • 输出Add Teacher:111
var teacher = require(‘./a‘);
teaher.add("111");

6 数据持久化

6.1 将数据写入文件

  • 场景:备份数据、储存时间戳、记录PID、记录日志
  • 若文件不存在,fs.writeFile将自动创建文件
var fs = require("fs");
var data = "Some Data I want to write to a file";
fs.writeFile("file.txt",data,"utf8",function(err){
   if(!err){
       console.log("ok")
   }else{
       throw err;
   }
});
  • 从文件读取数据
  • 若不提供编码,则会返回原始的缓冲区内容
  • err包括文件不存在或没有读取文件的权限
var fs = require("fs")
fs.readFile("file.txt","utf8",function(err,data){
   if(!err){
       console.log(data);
   }else{
       throw err;
   }
})

6.2 读取环境变量

  • 使用环境变量存储小数据:连接字符串的细节、用户名和密码、数据库设置等
  • process.env.PATH获取了系统环境变量的PATH值
  • win设置环境变量:set 变量名 = 变量值
  • win查看环境变量:set 变量名
  • linux设置环境变量:export 变量名 = 变量值
console.log(process.env.PATH)

6.3 使用mongodb保存数据

7. 调试

7.1 使用STDIO模型进行调试

  • console.erro()可以提供错误详细信息
console.log("Debugging message");
function notDefined(){
    try{
        someFunction();//undefined
    }catch(e){
        console.err(e);
    }
}
notDefined();
  • 使用console.time()和console.timeEnd()进行性能基准测试
  • 得到执行的时间
var sum = 0;
var arr = new Array(1000000);
for(var i = 0; i < arr.length; i++){
    arr[i] = Math.random();
}
console.time("for-loop-1");
for(var i in arr){
    sum += arr[i];
}
console.timeEnd("for-loop-1");

console.time("for-loop-2");
for(var i = 0; i < arr.length; i++){//效率更高
    sum += arr[i];
}
console.timeEnd("for-loop-2");
  • 得到脚本执行过程中任意一点的堆栈内的位置踪迹内容
  • 堆栈踪迹时应用程序中某个点的函数或方法调用清单
  • 使用console.trace()方法
function notDefined(){
    console.trace();
    try{
        someFunction();
    }catch(e){
        console.error(e);
    }
}
notDefined();

7.2 使用v8调试器调试代码

  • 在希望断点发生的地方加入:debugger
  • 通过:node debug app.js进行带有调试功能的程序
  • 键入“cont”进行下一个断点,键入”repl”可以查询a,b,c的值,ctrl+c退出
  • REPL:Read,Evaluate,Print,Loop——在断点处详细调试
var foo = function(){
    var a = 3, b = 5;
    // debugger;
    var bar = function(){
        var b = 7; c = 11;
        a += b + c;
        // debugger;
    }
    bar();
    // debugger;
};
foo();

7.3 使用 Node Inspector进行调试

  • 在root权限下进行安装: npm install -g node-inspector
  • 运行脚本: node –debug-brk app.js
  • 在另一个终端中启动:node-inspector
  • 在chrome中打开url即可进行调试
  • 或者,先启动终端: –web-port=8888 &,设置监听端口
  • 在另一个终端: node –debug-brk=5858 app.js
  • 再或者,执行运行node-debug app.js

8. 测试

  • 编写测试,当对代码做出修改之后,不会给应用程序引入新的bug。

8.1 TDD

  • TDD:测试驱动开发:首先编写描述了他们希望程序如何工作的测试,然后编写代码让测试通过。
  • 增加稳定性,便于重构,方便集成

8.1.1 Assert模块

  • assert模块给Node.js提供了一个基础的测试框架,而第三方模块在assert基础上改进,
  • 包括测试报告,测试异步代码以及使用DBB或者TDD风格测试。
  • 建议使用===,而不是==
  • 需要require(“assert”)
var assert = require("assert");
assert.equal("8",8); // 沉默是金
assert.strictEqual("8",8); //抛出错误

8.1.2 nodeunit模块

  • 使用nodeunit进行单元测试,在assert基础上添加了建立与拆卸测试的能力,异步测试能力,mock和stub的功能。
  • 全局安装:npm install -g nodeunit
  • 运行:nodeunit app.js
exports.firstTest = function(test){//每个测试都声明成exports.testName(描述)
    test.expect(1);//n为期望的断言数
    test.strictEqual("hello","hello");
    test.done();//避免通过假测试,完成之后一定要调用test.done()表示测试完成
}
exports.secendTest = function(test){
    test.expect(1);
    test.strictEqual("hello","there");
    test.done();
}
  • 使用nodeunit测试异步代码
  • 测试位于回调函数中
var fs = require("fs");
exports.asyncTest = function(test){
    fs.stat("file.txt",function(err,stats){
        test.expect(2);
        test.strictEqual(err,null);
        test.notStrictEqual(stats.size,0);
        test.done();//容易忘记
    });
}

8.2 BDD

  • BDD:行为驱动开发
  • 开发从外部往内考虑程序,测试以应用程序与来自外界的交互为基础,而不需要理解应用程序内部原理。
  • 在写bdd风格测试时,要使用更为平白的语言并描述正在测试什么,然后声明其如何工作。

8.2.1 Vows模块

  • Vows模块用于以BDD风格测试Node.js应用程序,建立在assert模块之上。
  • 全局安装Vows:npm install -g vows(取得root权限,sudo -i)
  • 运行:node app.js,若测试通过,则列出honored数量
var vows = require("vows");
var assert = require("assert");
vows.describe("Compare String").addBatch({ // 描述 Description,对测试的描述
    "when comparing the same stirngs":{    // 上下文 Context,测试运行的上下文
        topic:"hello",                     // 主题 Topic,测试的什么
        "they should be equal":function(topic){ //宣告 Vow,期望在测试中发生什么
            assert.strictEqual(topic,"ello");
        }
    },
    "when conparing different stirngs":{
        topic:"hello",
        "they should not be equal":function(topic){
            assert.notStrictEqual(topic,"there");
        }
    }
}).run();
  • 使用vows进行异步测试
  • 回调时特殊的this.callback函数,在所有的Vows主题中均可用,它使回调的结果可以被传递到测试函数中
var vows   = require("vows");
var assert = require("assert");
var fs     = require("fs");

vows.describe("Async testing").addBatch({
    "When using fs.stat on a file":{
        topic:function(){
            fs.stat("file.txt",this.callback);//必须要有this.callback
        },
        "it should be present": function(err,stat){
            assert.notStrictEqual(err,null);
        },
        "it should not be empry":function(err,stat){
            assert.notStrictEqual(stat.size,0)
        }
    }
}).run();

9.发布Node.js应用

9.1 使用heroku发布Node.js应用

  • 安装heroku:brew install heroku ,若未成功,在线下载安装https://toolbelt.heroku.com
  • 安装完成后登陆: heroku login 5*********@qq.com(1*********)
  • 将app.js最上面加上一句更改port,因为Heroku会随机分配端口
  • 在程序的根目录下,添加一个名为Procfile的文件,内容为:web: node app.js
  • 创建Git库:git init git add . git commit -m “initial commit”
  • 在Heroku上创建应用程序,返回url:heroku create –stack cedar,
  • 这条命令结束要出现Git remote heroku added提示才可以继续,否则发布失败,见hour11/examples02
  • 将站点发布到Heroku: git push heroku master
  • PaaS代表服务即平台:为开发人员提供整个托管平台,而开发人员无需涉入系统管理工作
var port = process.env.PORT || 3000;//随机分配端口
var http = require(‘http‘);
http.createServer(function(req,res){
   res.writeHead(200,{‘Content-Type‘:‘text/plain‘});
   res.end("I‘m writting program with Nodejs");
}).listen(port);//更改端口

10. Socket.IO

10.1 动态Web简史

  • 29世纪90年代后期,DHTML,引入了简单的动态功能,描述了用户与Web页面交互的方法以及当交互发生时会发生什么。
  • Ajax:用户无需刷新页面就能从服务器请求数据,但想从服务器推动数据到浏览器比较困难,且在不同浏览器工作方式不同,每次通信都需要建立连接
  • WebSocket:服务器和客户端之间实现双向实时通信,在Web服务器和浏览器之间保持连接持久打开,不支持重连接,但
  • Socket.IO库提供了重连接reconnection handling/心跳heartbeat功能,并对某些跨浏览器的问题进行了抽象。

10.2 Socket.IO

  • 添加socket.io为依赖模块
  • Socket.IO是通过WebSocket进行通信的一种简单方式,提供了服务器和客户端双方的组件
  • 涉及Web服务器和浏览器之间的通信,选择Node.js和Socket.IO极佳!
  • Web页面也服务器连接,需要加入以下代码:
 <script src = "/socket.io/socket.io.js"></script>
 <script>
  var socket = io.connect("http://127.0.0.1:3000"); //连接服务器
  socket.on("message",function(data){//侦听在“message”事件上接受的数据
      alert(data.text)
  })
  socket.emit("fromClient",{text:"message from client"});//浏览器发信息给服务器
 </script>

app.js内容:

var http = require("http");
var fs    = require("fs");
var server = http.createServer(function(req,res){
    fs.readFile("./index.html",function(error,data){
        res.writeHead(200,{"Content.Type":"text/html"});
        res.end(data,"utf-8");
    })
}).listen(3000,"127.0.0.1");

var io = require("socket.io").listen(server);//将Socket.IO绑定到服务器,可以侦听事件
io.sockets.on("connection",function(socket){//侦听连接事件,注意sockets和socket
    console.log("User connected");
    socket.emit("message",{text:"you hava receive message from server!"});//从服务器将数据发送给单个客户的
    // socket.broadcast.emit("message",{text:"you hava receive message from server!"});//给已连接的客户端发送数据

    socket.on("disconnect",function(){//侦听断开连接事件
        console.log("User disconnected!");
    })
    socket.on("fromClient",function(data){
        console.log(data.text);
    })
})

11. JSON

  • JSON:JavaScript对象标记:轻量级的数据交换格式,由Douglas Crockford发明。
  • JSON的键值对用逗号分开,键值对可以嵌套,创建更为复杂的数据结构。键值对必需位于双引号内才有效。
  • 使用Node.js已JSON发送数据,必须将头Header设置为application/json,普通的为text/plain,设置为application/text会下载文本。
var http = require("http");
http.createServer(function(req,res){
    res.writeHead(200,{"Content-Type":"application/json"});
    res.end(‘{"name":"yll","age":"22","home":"huozhou"}‘);
}).listen(3000,"127.0.0.1");
console.log("Server running at 3000");
  • 将javaScript对象obj转换为JSON:JSON.stringify(obj)
  • 将Json转换为javaScript对象:JSON.parse(json)
var obj = {
    name:"yll",
    age :"22"
}
myJson = JSON.stringify(obj);//将javaScript对象obj转换为JSON
console.log(myJson);//输出{"name":"yll","age":"22"}
myObj = JSON.parse(myJson);//将Json转换为javaScript对象
console.log(myObj)//输出{ name: ‘yll‘, age: ‘22‘ }
  • 调用第三方JSON的API
  • 通过http客户端查询Twitter搜索api关于Node.js的最新推文,返回JSON,而后进行分析
var http = require("http");
var data = "";
var tweets = "";
var options = {
    host:"api.twitter.com",
    path:"/1.1/search/tweets.json?q=%23node.js"
};
var request = http.get(options,function(res){
    res.on("data",function(chunk){
        console.log(chunk)
        data += chunk;
    })
    res.on("end",function(){
        tweets = JSON.parse(data);
        for(var i = 0; i < tweets.results.length; i++){
            console.log(tweets.results[i].text);
        }
    });
    res.on("error",function(err){
        console.log("there was an error: " + err.message);
    })
})

12. Node.js调用进程Process

  • 当运行Node.js程序时,它运行于单个进程之上,process.pid获取node的pid
  • 进程id仅被指派了一小段时间,当执行完成后,进程就不存在了
  • 在Linux中,终端输入pa aux命令可以看到进程列表
console.log(process.pid)

12.1 捕获进程事件

  • 退出进程事件exit
  • 捕获未捕获的异常,uncaughtException
  • 能够让开发者了解在应用程序中通过正常的错误处理过程不曾遇到的问题。
process.on("exit",function(){
    console.log("exited");
});
process.on("uncaughtException",function(err){
    console.error(err.stack);
});

12.2 进程与信号

  • 在linux中,进程可以接受信号,从而让进程可以以某种方式来响应
  • SIGINT是发送中断信号(Interrupt)的缩写,在退出进程前可以关闭连接,写入日志文件,对下一个进程做些什么等等。
  • process模块可以让Node.js侦听这些信号并据此响应。
process.stdin.resume();//防止脚本在初始化从stdin的读取时退出
process.on("SIGINT",function(){
    console.log("Got a SIGINT. Exiting");
    process.exit(0);//退出进程
})

12.3 向进程发送信号

  • 在Linux中,使用kill向进程发送信号,默认发送SIGTERM,进程立即终止。kill [process_id]
  • kill可以发送其他信号,如: kill -s SIGINT [process_id]
  • 在process中,可以设置这些信号的侦听器并据此响应

    process.stdin.resume();//防止脚本在初始化从stdin的读取时退出
    process.on("SIGTERM",function(){
    console.log("Got a SIGTERM. Exiting");
    process.exit(0);//退出进程
    })
    

12.4 使用Node.js创建脚本

  • 首先在脚本顶部加shebang:#! /usr/bin/env node
  • 然后将脚本设置为可执行:chmod +x app.js
  • Shebang的名字来自于SHArp和bang,或haSH bang的缩写,指代Shebang中#!两个符号的典型Unix名称。
  • Unix术语中,井号通常称为sharp,hash或mesh;而叹号则常常称为bang。
    • 如果脚本文件中没有#!这一行,那么它执行时会默认用当前Shell去解释这个脚本(即:$SHELL环境变量)。
    • 如果#!之后的解释程序是一个可执行文件,那么执行这个脚本时,它就会把文件名及其参数一起作为参数传给那个解释程序去执行。
    • 如果#!指定的解释程序没有可执行权限,则会报错“bad interpreter: Permission denied”。如果#!指定的解释程序不是一个可执行文件,那么指定的解释程序会被忽略,转而交给当前的SHELL去执行这个脚本。
    • 如果#!指定的解释程序不存在,那么会报错“bad interpreter: No such file or directory”。注意:#!之后的解释程序,需要写其绝对路径(如:#!/bin/bash),它是不会自动到$PATH中寻找解释器的。
    • 若使用”bash test.sh”这样的命令来执行脚本,那么#!这一行将会被忽略掉,解释器当然是用命令行中显式指定的bash。
 #! /usr/bin/env node
 console.log("hello world");//需要将脚本设置为可执行

12.5 给脚本传递参数

  • process模块支持将参数以process.argv的形式传递给脚本
  • 默认输出”node”和脚本路径,若传参数给脚本,则参数显示在数组中的上述元素之后
  • ./test one two three
  • 输出:
    [ ‘node’,
    ‘/Users/yll/百度云同步盘/macbook/nodenote/test’,
    ‘one’,
    ‘two’,
    ‘three’ ]
  • 通过使用参数,可以编写从一个公共API获取数据的脚本,可以让计算机定时执行它。
  • 可以使用Node.js创建出可使用的,自动解决许多问题的网络脚本。
  • 参数为process.argv[2],process.argv[3]等……
  • 开发命令行脚本可以使用commander模块
  • 确保脚本运行着,使用forever模块
#! /usr/bin/env node
console.log(process.argv);//输出[ ‘node‘, ‘/Users/yll/百度云同步盘/macbook/nodenote/test‘ ]

12.6 子进程

  • 子进程是由另一个进程(父进程)创建的进程,一个父进程可以有许多子进程,一个子进程只能有一个父进程。
  • 使用子进程的场景:复杂计算,在Node.js外部使用系统工具操作数据,执行清理操作,执行复杂操作。
  • 父进程与子进程数据交换:数据流通过stdin/stdout/sterr来交换。
  • ping命令通过请求-响应来查看主机或者网关是否工作。
  • 在Node.js中使用ping程序,可以通过生成spawn一个子进程并通过侦听来自子进程的stdout来实现。
  • Node.j创建子进程方法:spawn()方法,参数:Command(想要运行的命令),Arguments(传递给命令的任何参数),Options(工作目录/环境变了等)
  • 该方法可以访问操作系统的中的任何软件,例如对上传的视频文件进行编码:ffmpeg工具
  • 若使用Pssa,则可能无法使用系统命令
var spawn = require("child_process").spawn;
var ping = spawn("ls",["www.baidu.com"]);
ping.stdout.setEncoding("utf8");//必须对指定数据进行编码,否则显示的是原始流
ping.stdout.on("data",function(data){
    console.log(data)
})

12.6.1 杀死子进程

  • 父进程对子进程发送kill信号
  • Child Process模块提供了kill()方法,默认发送SIGTERM信号
  • 例如父进程发送kill信号给子进程,父进程侦听子进程退出事件并做出处理。
var spawn = require("child_process").spawn;
var ping = spawn("ping",["www.baidu.com"]);
ping.on("exit",function(code,signal){//侦听子进程退出事件
    console.log("child process was killed with a " + signal + "signal");
})
ping.kill("SIGINT");//中断子进程

12.6.2 与子进程进行通信

  • 创建提供父子进行通信能力的方法:fork()——创建Node.js子进程,每个fork()的开销在于每个子进程是全新的V8实例。
  • 每个Node.js进程需要花费30ms启动,并占用10M内存。
  • 若与系统命令交互,则使用spawn()。
  • parent.js
var fork = require("child_process").fork;
var child = fork(__dirname+"/child.js");
child.on("message",function(m){
    console.log("p:",m);
})
child.send({message:"hello,child!"})
  • child.js
process.on("message",function(m){
    console.log("zi:",m);
})
process.send({message:"hello parent!"})

12.7 集群Cluster模块

  • 按计算机上可以使用的处理器数量来扩展应用程序,每个处理器都有一个子进程,还有一个父进程来管理它们。
  • 子进程常称为worker process
  • Cluster模块中的fork()方法是建立在Child Process模块的fork()方法之上,基本相同
var cluster = require("cluster");
var http    = require("http");
var cpus    = require("os").cpus().length;//系统cpu数量
if(cluster.isMaster){
    console.log("Master process started with PID: ",process.pid);
    for(var i = 0; i < cpus; i++){
        cluster.fork();
    }
    cluster.on("exit",function(worker){//侦听进程死亡事件
        console.log("worker " + worker.process.pid + " died");
        cluster.fork();
    })
}else{
    console.log("Worker process started with PID: " ,process.pid);
    http.createServer(function(req,res){
        res.writeHead(200,{"Content-Type":"text/plain"});
        res.end("hello world\n");
    }).listen(3000);
}

13. 事件

  • Node.js运行于单一的线程中,若有长时间运行的代码,事件循环会被阻塞。
  • 别阻塞:Node.js是单线程的,若代码阻塞的话,其他一切都停止了。
  • 快速返回:操作应该快速返回,若不能快速返回,就应当将其移到另一个进程中。
  • 下面代码为对ID为target的HTML元素设置单击侦听器,当事件发生时,事件被触发。
  • 不再围绕事件出现的顺序编程,而是围绕事件发生的顺序编程
var target = document.getElementById("target");
target.addEventListener("click",function(){
    alert("click event fired.");
},false);

13.1 Node.js处理网络事件

  • 包括来自Web服务器的响应,从文件读取数据,从数据库返回数据
  • 要使用events模块,必须先创建一个新的EventEmitter实例
var EventEmitter = require("events").EventEmitter;
var ee = new EventEmitter();
//加入上述代码,就可以添加事件和侦听器了。
ee.on("message",function(data){//侦听器侦听事件并在事件触发时处理它。
    console.log(data);//emit的第二个参数传递给了匿名函数使用
})
ee.emit("message","This emits a message.")//第一个参数是对事件进行描述的字符串,与侦听器匹配

通过HTTP演示事件

var http = require("http");
http.get({host:"baidu.com"}, function (res) {
   res.on(‘data‘,function(d){//这是一个来自Events模块的事件侦听器
       process.stdout.write(d);
   })
}).on("error",function(e){
   console.log("Got error: " + e.message);
});

13.2 用事件玩乒乓

  • 默认如果事件超过10个侦听器,就会发出警告
  • 可以通过emitter.setMaxListeners(n)来更改这个数量
var EventEmitter = require("events").EventEmitter;
var pingPong = new EventEmitter();
setTimeout(function(){
    console.log("Send firstping");
    pingPong.emit("ping");
},2000);
pingPong.on("ping",function(){
    console.log("Got Ping");
    setTimeout(function(){
        pingPong.emit("pong");
    },2000);
});
pingPong.on("pong",function(){
    console.log("Got Pong");
    setTimeout(function(){
        pingPong.emit("ping");
    },2000);
});
 // 动态编写事件侦听器程序
var logPing = function(){
    console.log("4s后增加一个事件侦听器")
}
setTimeout(function(){
    pingPong.on("ping",logPing);
},4000)

setTimeout(function(){
    pingPong.removeListener("ping",logPing);//12s后移除增加的事件
},12000)

14 缓冲区模块

  • 需要进行二进制传输的示例有 :
  • 通过TCP连接发送和接收数据,从图像或者压缩文件读取二进制数据
  • 从文件系统读写数据,处理来自网络的二进制数据流。
  • Buffer模块可以存储二进制数据
  • 若不设置文件编码,则输出原始Buffer对象:
  • 设置编码:fs.readFile(“a.txt”,”utf8”,function(err,data){});
  • Node.js中可以设置的编码:ascii,ucs3,base64,hex,utf8
var fs = require("fs");
fs.readFile("a.txt",function(err,data){
    if(err){
        throw err;
    }else{
        console.log(data)
    }
})

14.1 Node.js中的缓冲区

  • 在node控制台中输入
  • 缓冲区时对原始内存的分配,以便于Node.js对此读写数据
var buffer = new Buffer(8);//创建带有8个字节的缓冲区
//得到buffer为:<Buffer 50 59 40 01 01 00 00 00>
//缓冲区所代表的是计算机上所分配的原始内存。
console.log(buffer.toString());
//得到:‘\u0000 \u0000\u0000\u0000\u0000\u0000\u0000‘
var buffer = new Buffer([85,86]);//85是字符U
console.log(buffer.toString("utf8"));//将编码传递给缓冲区,输出字符UV

14.2 写入缓冲区

  • 写入缓冲区,向缓冲区追加数据,复制缓冲区,修改缓冲区中的字符串
  • Buffer模块没有提供修改字符串的方法,若想修改:
  • 使用toString()方法读缓冲区,对String对象执行修改,将修改后的字符串写会缓冲区
var buffer = new Buffer(8);
buffer.write("hi","utf8");//写入缓冲区,控制台中输出1,说明该编码占用1个字节
buffer.toString()
buffer.write(" buffer",2,"utf8");//向缓冲区追加数据,2表示偏移量(从0开始)
buffer.toString();//控制台输出hi buffer
var buffer2 = new Buffer(8);
buffer.copy(buffer2);//将buffer复制给buffer2
buffer2.toString();//控制台输出hi buffer

15. 流模块

  • 流可以:创建代理服务器,流服务器,操作文件上传的服务(图像调整,视频编码转换等)
  • 流是移动数据的高校方式:来自流的数据在其完成操作之前就可以使用了。
  • 包括:标准输入,标准输出,标准错误
  • 在Linux中,sort < test.txt 将对test中的文本进行排序,并输出到终端
  • sort < test.txt > sorted.txt 将排序后的结果重定向,输出到sorted文件中
  • 使用流来读写文件,若想读入所有数据,必须将其连接到一个变量中。

    var fs = require("fs");
    var stream = fs.ReadStream("a.txt");
    var data = "";
    stream.on("data",function(chunk){
    data += chunk;
    console.log("read some data");
    });
    stream.on("close",function(){
    console.log("all the data is read");
    // console.log(data)
    })
    

15.1 可写流

  • 使用流读入文件,然后写入另一个文件
var fs = require("fs");
var readableStream = fs.ReadStream("a.txt");
var writableStream = fs.WriteStream("out.txt");
readableStream.setEncoding("utf8");
readableStream.on("data",function(chunk){
    writableStream.write(chunk);
})
readableStream.on("close",function(){
    writableStream.end();
})

15.2 通过管道连接流

  • Node.js提供了连接两个可读和可写流,并在它们之间通过管道传输数据的方法:pipe()
  • 感觉跟复制一样
  • 缓冲区被用于读写流中的数据
var fs = require("fs");
var readableStream = fs.ReadStream("a.txt");
var writableStream = fs.WriteStream("out.txt");
readableStream.pipe(writableStream);

15.3 流的MP3服务器

  • 在HTTP模块中,响应对象是一个可写流,它可以让文件以可读流的方式读入。
  • 然后经过管道成为进入响应对象的可写流
  • pipe()可以处理所需要的暂停和恢复
  • 使用流可以使得MP3文件可以先读,然后通过管道输送到响应中
var http = require("http");
var fs   = require("fs");
http.createServer(function(req,res){
    var mp3 = "红玫瑰.mp3";
    var stat = fs.statSync(mp3);
    res.writeHead(200,{
        "Content-Type":"audio/mpeg",
        "Content-Length":stat.size
    })
    var readableStream = fs.createReadStream(mp3);
    readableStream.pipe(res);
}).listen(3000,"127.0.0.1");
 console.log("Server is running at ‘127.0.0.1:3000‘")

15.4 使用流下载文件到本地

var http = require("http");
var fs   = require("fs");
var writableStream = fs.WriteStream("a.jpg");
var address = "http://g.hiphotos.baidu.com/image/pic/item/94cad1c8a786c917399b1438cb3d70cf3bc75712.jpg";
var request = http.get(address,function(res){
    res.on("data",function(chunk){
        writableStream.write(chunk);
        console.log("get data");
    })
    res.on("end",function(){
        console.log("over")
    })
    res.on("error",function(err){
        console.log("error : ",err.message);
    })
})

16. 创建Node.js模块

  • 避免一次次编写同样的代码
  • 提供Node.js核心中不存在的特定功能
  • 模块能解决一个问题并且能很好地解决这个问题:做一件事情并且把他做好。

16.1 package.json文件信息

  • 包名称
  • 描述
  • 包版本
  • 项目主页
  • 项目的Git库
  • 作者姓名
  • 作者电子邮件
  • 作者URL
  • 主模块/进入点bin——模块的主文件
  • 测试命令test——运行模块测试的命令
  • Node版本——该模块支持哪些Node.js版本

16.2 Node.js模块文件夹结构

  • .gitignore——从Git库中忽略的文件清单
  • .npmingore——从npm库中忽略的文件清单
  • LICENSE——授权文件
  • README.md——以Markdown格式编写的README文件
  • bin——保存模块的可执行文件
  • doc——包括模块文档
  • examples——保存如何使用模块的实际示例的文件夹
  • lib——保存模块代码
  • man——保存模块的说明手册
  • package.json——描述模块的JSON文件
  • src——保存源文件(常用于CoffeeScript)
  • test——保存模块的测试文件

17. 中间件

  • 在Node.js中,中间件是通过客户和应用程序间添加一个痩层来过滤应用程序中的请求和响应的方法,位于应用程序逻辑之前。
  • 例如:通过缩进和重新格式化内容清理HTML标记,日志记录器,监控程序性能,缓存异常通知器,重定向,一个Cookie分析器,跨站点请求伪造保护
  • Connect模块给Node的HTTP模块添加了对中间件的支持,以便操作HTTP请求和响应。
  • 中间件适合于操纵请求和响应,与应用程序区分开,处理数据或查询数据库不应该放在中间件中
  • 一个请求要按中间件的声明顺序流过中间件。
  • 调用next()将请求和响应传递到下一个中间件(如果是下一个中间件,则传递给应用程序逻辑),可使用res.end()来返回响应。
  • Connect包装了来自Node的HTTP模块的Server,ServerRequest,ServerResponse对象,以便添加中间件功能。
  • Connect中的中间件只是一个以请求,响应对象,下一个回调作为参数的函数。一个请求在到达应用程序逻辑之前必须流过这个清单中的函数。
  • 通过在Connect服务器中mount中间件,可以给整个应用程序mount,也可以对特别路由mount,按照中间件声明顺序串接在一起。
  • 需要在package.json中添加connect
  • 终端查看头信息: curl -i http://127.0.0.1:3000
var connect = require("connect");
var http = require("http");
var app = connect()
    .use(nothingMiddleware)//仅仅起到传递到helloWorld的工作,而helloWorld返回响应
    .use(addHeader)
    .use(forceDomain("127.0.0.1:3000"))//将用户强制到单个域上,例如输入:localhost:3000——127.0.0.1:3000
    .use(filterByIp([‘127.0.0.1‘]))//限定用户的ip
    .use(nineToFive)
    .use(helloWorld)
function nothingMiddleware(req,res,next){
    next();//当next()回调函数被调用时,中间件就完成了工作,请求被传递到了下一个中间件或者应用层。
}
function addHeader(req,res,next){//给应用程序发送的任何响应添加自定义头。
    res.setHeader("X-Custom-Header","My header content");
    next();
}
function nineToFive(req,res,next){//使用中间件进行访问控制,时间在早上9点到下午5点之间可以打开,其他时间关闭。
    var currentHour = new Date().getHours();
    if(currentHour < 9 || currentHour >17){
        res.writeHead(503,{"Content-Type":"text/plain"});
        res.end("we are closed");
    }else{
        next();
    }
}
function forceDomain(domain){//将用户强制到单个域上
    domain = domain || false;
    return function(req,res,next){
        if(domain && (req.headers.host != domain)){
            res.writeHead(301,{"Location":"http://" + domain + req.url});
            res.end();
        }else{
            next();
        }
    }
}
function filterByIp(ips){//按IP地址限制访问
    var ips = ips || [];
    return function(req,res,next){
        if(ips.indexOf(req.connection.remoteAddress) == -1){
            res.writeHead(401,{"Content-Type":"text/plain"});
            res.write("sorry");
            res.end();
        }else{
            next();
        }
    }
}
function helloWorld(req,res){
    res.writeHead(200,{"Content-Type":"text/plain"});
    res.end("hi! helloe world!");//是用res.end,当响应发送之后即完成了请求的处理。
}
http.Server(app).listen(3000,"127.0.0.1");
console.log("server....")

18. Express

  • Express是Node.js的一个Web框架
  • 安装Express:npm install -g express
  • 创建一个Express站点:express expressTest
  • 安装依赖模块:cd expressTest && npm install
  • 运行:node app.js
  • 打开127.0.0.1:3000可以看到Express服务的一个基础网站。

18.1 Express文件结构

  • app.js:用来启动应用程序,包含配置信息。
  • node_modules:保存package.json中定义并以安装的Node模块
  • package.json:提供应用程序信息,包括运行应用程序所需安装的依赖模块
  • pulic:提供给Web服务的公共文件夹,包括stylesheets,javascripts和images等,不会有任何应用程序逻辑
  • routes:保存了应用程序应该响应的路由页面
  • views:定义了应用程序的布局layout

18.2 Express中的路由

Express使用HTTP动词来定义路由,HTTP动词描述对服务器的请求类型
HTTP动词:

  • GET: 请求指定的页面信息,并返回实体主体。
  • HEAD: 只请求页面的首部。
  • POST: 请求服务器接受所指定的文档作为对所标识的URI的新的从属实体。
  • PUT: 从客户端向服务器传送的数据取代指定的文档的内容。
  • DELETE: 请求服务器删除指定的页面。
  • OPTIONS: 允许客户端查看服务器的性能。
  • TRACE: 请求服务器在响应中的实体主体部分返回所得到的内容。
  • PATCH: 实体中包含一个表,表中说明与该URI所表示的原内容的区别。
  • MOVE: 请求服务器将指定的页面移至另一个网络地址。
  • COPY: 请求服务器将指定的页面拷贝至另一个网络地址。
  • LINK: 请求服务器建立链接关系。
  • UNLINK: 断开链接关系。
  • WRAPPED: 允许客户端发送经过封装的请求。
  • Extension-mothed:在不改动协议的前提下,可增加另外的方法。

当加载页面时,浏览器使用GET请求获取HTML,CSS,JavaScript和图片文件;在提交表单的时候,使用POST请求。

18.2.1 添加GET路由

app.get("/about",function(req,res){
    res.send("hello from the about route"!);//发送响应
})

18.2.2 添加POST路由

在index.jade加入以下带代码,以便在index试图中添加表单

form(method="post",action="/")
fieldset
    legend Add a user
    p
        label First name
        input(name="user[first_name]")
    p
        label Last name
        input(name="user[surname]")
    p
        input(type="submit",value="Save")

在app.js中添加一个接收POST请求的路由

app.post("/",function(req,res){
    res.send(req.body);
})

18.2.3 在路由中使用参数

http://127.0.0.1:3000/users/12
将以下内容添加到app.js文件的routes节中:

app.get("/users/:id",function(req,res){
    res.send("show content for user id " + req.params.id);
})

18.2.4 让路由保持可维护性

将路由移到一个单独的文件或不同的文件中,随后在主app.js文件中请求这些路由

var routes = require("./routes")

这样就可以在主应用程序中以routes.index来使用

app.get("/",routes.index);

在routes文件夹中,包含路由声明文件index.js

exports.index = function(req,res){
    res.render("index",{title:"Express"})
}

18.3 视图渲染

  • 之前一直使用res.render和res.send来指定把什么内容作为响应来发送。
  • 视图渲染描述应用程序在对HTTP请求的响应中要如何渲染,渲染什么。
  • 用视图渲染的响应示例:发送HTML,发送JSON,发送XML
  • 使用res.render可以渲染模版,将本地变量传递给模版,使用layout文件,发送HTTP响应代码
app.get("/",function(req,res){
    res.render("index.jade",{title:"My site"});
})

以上代码定义了:

  • 位于view/index.jade的index.jade模版要用于渲染页面;
  • 本地变量title要被传递给views/index.jade模版;
  • 使用默认布局文件views/layout.jade。
    • 若不想使用 布局文件,这样制定:
    res.render("page.jade",{layout:false})

18.4 使用本地变量

可以设置要在视图中展示的数据,将数据传递到视图层。

app.get("/",function(req,res){
    var user = {
        firstName: "Lord",
        surname: "Lucan",
        address:"I‘m not telling"
    };
    res.rend("index.jade",{title:"User",user:user});
})

这样,数据就可以在视图中使用,在views/index.jade模版中访问

h1 Accessing data
h3 First Name
p = user.firstName
h3 Surname
p = user.surname

19 Jade

  • Jade是基于缩进的HTML模版引擎,将视图编译成HTML,通常在视图文件夹中使用。
  • 使用Jade可以使得应用程序可以动态输出数据到HTML中,真正能力是操纵数据并输出数据到HTML中。
  • Jade使用缩进来定义HTML文档的层次结构,Jade无需使用标记,无需关闭HTML标记。
<div class="wrapper">
    <h1>My holiday in Prague</h1>
    <p>I had a great holidy.</p>
    <img src="images/photo.jpg" alt="Me on holiday"/>
</div>
.wrapper
    h1 My holiday in Prague
    p I had a great holidy.
    img(src="images/photo.jpg",alt="Me on holiday")

19.1 使用Jade定义页面结构

  • html ——编译后——<html></html>
  • section#wrapper——编译后——<section id="wrapper"></section>
  • p.highlight——编译后——<p class="highlight"></p>
  • section#wrapper.class_name——编译后——<section id="wrapper" class="class_name"></section>
  • section.first.second.third——编译后——<section class="first second third"></section>
  • h1 Very important heading——编译后——<h1>Very important heading</h1>
  • p
        span
    ——编译后—— <p><span></span></p>
  • 管道描述符”|”用来组织大的文本主体
    p
         | Text can be over
         | many lines
         | after a pipe symbol
    ——编译后—— <p>Text can be over many lines after a pipe symbol<p>

19.2 使用Jade输出数据

  • Jade使用减号”-“告诉随后的代码应该被执行,使用等号”=”告诉对随后的代码进行演算、转义,然后输出。
  • “#{变量}”告诉Jade将变量替换为字符串值
- var foo = "bar" // 必须加引号
p Foo is #{foo}!——编译后——<p>Foo is bar!</p>

19.3 循环

- users = ["Sally","Joseph","Michael","Sanjay"]
- each user in users
    p = #{user}

- for user in users
    p = user

- obj = {first_name:"George", surname:"Ornbo"}
- each val,key in obj
    li #{key}: #{val}
````





<div class="se-preview-section-delimiter"></div>

### 19.4 条件





<div class="se-preview-section-delimiter"></div>
  • awake = false
  • if(awake)
    p You are awake!//必须要有tab
  • else
    p You are sleeping





<div class="se-preview-section-delimiter"></div>

###19.5 内联JavaScript




<div class="se-preview-section-delimiter"></div>

script alert(“use inline javascript in jade!”);






<div class="se-preview-section-delimiter"></div>

### 19.6 包含
- 包含:页面组成部分(页眉,页脚,边栏),将网站的公共部分移到单一的文件中




<div class="se-preview-section-delimiter"></div>

- Jade通过include关键字后跟想要包含的模版来支持包含功能
- 




<div class="se-preview-section-delimiter"></div>

html
body
include includes/header

上述代码从views/includes/header.jade文件中包含代码





<div class="se-preview-section-delimiter"></div>

### 19.7 Mixin:代码的包含




<div class="se-preview-section-delimiter"></div>

mixin users(users)
ul
each user in users
li = #{user}
- users = [“Krist”,”Kurt”,”Dave”]
mixin users(users) //一旦定义了mixin,就可以使用它,并在模版中重用






<div class="se-preview-section-delimiter"></div>

## 20 执行效率

 * 以下代码在浏览器中执行,用函数和不用函数,效率相差5倍,解释:





<div class="se-preview-section-delimiter"></div>

// function foo(){
for(var i = 1; i <= 10; i++){
console.time(i);
for(var j = 0; j < 256*256*256; j++){

    }
    console.timeEnd(i);
}

// }
// foo();

“`

【读书笔记】《Node.js入门经典》

标签:node.js   http   program   require   

原文地址:http://blog.csdn.net/u013819585/article/details/45566487

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!