Node.js Lession

Chapter01.簡(jiǎn)介

  • NodeJS是讓JavaScript脫離瀏覽器運(yùn)行在服務(wù)器的一個(gè)平臺(tái),不是語言。
  • NodeJS采用的JS引擎來自GoogleChromeV8肄梨,運(yùn)行在瀏覽器外不用考慮JS兼容性奥邮。
  • NodeJS采用單線程、異步IO关顷、事件驅(qū)動(dòng)的設(shè)計(jì)來實(shí)現(xiàn)高并發(fā)
  • NodeJS內(nèi)建HTTP服務(wù)器

安裝

// 測(cè)試命令
node -v
npm -v

NPM

NPM(Node Package Modules)用于安裝豐富的NodeJs庫(kù)來完成開發(fā)需求。

// 查看幫助
npm help
npm h
// 安裝模塊
npm install <module_name>
// 全局安裝模塊
npm install -g <module name>
// 卸載模塊
npm uninstall <module_name>
// 已安裝模塊
npm list

Chapter02.事件模塊

events事件是NodeJS重要模塊武福,events模塊提供events.EventEmitter對(duì)象用于事件發(fā)射和事件監(jiān)聽议双。
NodeJS大部分模塊都繼承自Events模塊。與DOM事件不同的是捉片,Events不存在事件冒泡和逐層捕獲等行為平痰。
EventEmitter支持若干事件監(jiān)聽器,當(dāng)事件發(fā)射時(shí)注冊(cè)到此事件的事件監(jiān)聽器被依次調(diào)用伍纫,事件參數(shù)作為回調(diào)函數(shù)參數(shù)傳遞宗雇。

// 加載模塊
require('events');

Chapter03.模塊

核心模塊

NodeJS提供核心模塊編譯成二進(jìn)制文件,是用require('module_name')去獲取莹规,核心模塊具有最高的加載優(yōu)先級(jí)赔蒲。

文件模塊

NodeJS的文件模塊可以是JS代碼、JSON文件、C/C++編輯過的文件舞虱。

文件模塊訪問是通過 require('dir/file.ext') 來訪問欢际。

NodeJS加載優(yōu)先級(jí)是:js文件>json文件>node文件

案例:自定義計(jì)數(shù)器模塊

Chapter04.與MySQL交互

NodeJS與MySQL交互操作有很多庫(kù),暫時(shí)選擇 felixge/node-mysql砾嫉。

// 安裝
npm install mysql

執(zhí)行CURD

//加載mysql庫(kù)
var mysql = require('mysql');

//創(chuàng)建連接
var dbcfg = {
    host:'127.0.0.1',
    user:'root',
    password:'root',
    port:'3306',
    database:'test'
};
var link = mysql.createConnection(dbcfg);
link.connect(function(err){
   if(err){
       console.log(err);
       return;
   }
   console.log('mysql create connect success!');
});
//執(zhí)行sql語句
var sql = 'SELECT 1+1 AS solution';
link.query(sql,function(err,rows,fields){
   if(err){
       console.log(err);
       return;
   }
   var msg = 'The solution is '+rows[0].solution;
   console.log(msg);
});

//插入數(shù)據(jù)
var sql = "INSERT INTO user(username,password) VALUES(?,?)";
var para = ['alice','123456'];
link.query(sql,para,function(err,ret){
   if(err){
       console.log(err.message);
       return;
   }
   console.log(ret);
});


//關(guān)閉連接
link.end(function(err){
    if(err){
        return;
    }
    console.log('mysql connect end!');
});

//斷線重連
function handleDisconnect(){
    link = mysql.createConnection(dbcfg);
    link.connect(function(err){
       if(err){
           console.log('數(shù)據(jù)庫(kù)斷線重連:'+new Date());
           setTimeout(handleDisconnect,2000);//定時(shí)2秒重連
           return;
       }
       console.log('數(shù)據(jù)庫(kù)連接成功:'+new Date());
    });
    //監(jiān)聽數(shù)據(jù)庫(kù)連接是否斷掉
    link.on('error',function(err){
        console.log('數(shù)據(jù)庫(kù)連接斷線: ',err);
       if(err.code === 'PROTOCOL_CONNECTION_LOST'){
           handleDisconnect();
       } else{
           throw err;
       }
    });
}
handleDisconnect();

執(zhí)行事務(wù)

關(guān)閉連接

  • end() 在執(zhí)行query()后執(zhí)行幼苛,end()接收一個(gè)回調(diào)函數(shù),query()執(zhí)行出錯(cuò)仍結(jié)束連接焕刮,錯(cuò)誤會(huì)返回給回調(diào)函數(shù)err參數(shù)可在回調(diào)函數(shù)中出力舶沿。
  • destory() 暴力關(guān)閉,無回調(diào)函數(shù)配并,立即執(zhí)行不管query() 是否完成括荡。

斷線重連

[錯(cuò)誤代碼] error code: PROTOCOL_CONNECTION_LOST

Chapter05.Express框架與EJS模板引擎

NodeJS提供HTTP模塊,HTTP模塊提供底層接口但不便于開發(fā)溉旋。從Express框架著手去進(jìn)行Web開發(fā)畸冲,Express實(shí)現(xiàn)更好更高層的接口,使Web開發(fā)更加便捷观腊。
Express是一個(gè)輕量級(jí)邑闲、簡(jiǎn)潔、易用的NodeJS Web MVC開發(fā)框架梧油,Express基于NodeJS原有模塊并對(duì)Web開發(fā)所需的功能封裝苫耸。

安裝

// -g 表示全局安裝
npm install -g express-generator
express -V

案例:創(chuàng)建express的web應(yīng)用

express webapp

下載依賴

npm install

添加端口監(jiān)聽

app.listen(8100,function(){
  console.log('server start');
});

安裝supervisor每次修改后自動(dòng)重啟

npm install -g supervisor
supervisor app.js

express默認(rèn)實(shí)用模板引擎為jade

模板引擎ejs

創(chuàng)建express+ejs項(xiàng)目

express -e webapp
cd webapp && npm install 

webapp/app.js 添加8100端口監(jiān)聽

app.listen(8100,function(){
    console.log('server start');
});

webapp/routes/index.js修改路由

router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express', list:[{username:'alice'},{username:'ben'},{username:'carl'}] });
});

webapp/view/index.ejs修改模板

<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <h1><%= title %></h1>
    <% list.forEach(function(item){ %>
    <p><%=item.username%></p>
    <% }) %>
    <p>Welcome to <%= title %></p>
  </body>
</html>

瀏覽器查看結(jié)果

http://localhost:8100

Chapter06.構(gòu)建網(wǎng)絡(luò)應(yīng)用基礎(chǔ)

案例1:接收GET提交

創(chuàng)建應(yīng)用

cd workspace
express -e webapp
cd webappp && npm install

添加路由

# webapp/app.js
var subform = require('./routes/subform');
app.use('/subform', subform);

監(jiān)聽端口

# webapp/app.js
app.listen(8100,function(){
    console.log('server start');
});

創(chuàng)建路由文件

# webapp/routes/subform.js
var express = require('express');
var router = express.Router();

router.get('/',function(req,res){
    /*
   //接收GET參數(shù)并輸入控制臺(tái)
    var username = req.query.username;
    var password = req.query.password;

    var username = req.param('username');
    var password = req.param('password');

    console.log(username,password);
    */

    //渲染頁面
    res.render('subform',{title:'提交表單'});
});

module.exports = router;

創(chuàng)建公共模板

# webapp/views/nav.ejs

<ul class="list-group">
    <li class="list-group-item"><a href="/">首頁</a></li>
    <li class="list-group-item"><a href="/subform">提交表單</a></li>
    <li class="list-group-item"><a href="/session">回話控制</a></li>
    <li class="list-group-item"><a href="/cookie">Cookie設(shè)置</a></li>
    <li class="list-group-item"><a href="/crypto">字符串加密</a></li>
</ul>

創(chuàng)建表單模樣

# webapp/views/subform.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<% include nav %>
<form role="form">
    <p class="form-group">
        <label for="username">賬戶</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="賬戶"/>
    </p>
    <p class="form-group">
        <label for="password">密碼</label>
        <input type="password" class="form-control" name="password" id="password" placeholder="密碼"/>
    </p>
    <p class="form-group">
        <label for="repassword">重復(fù)密碼</label>
        <input type="password" class="form-control" name="repassword" id="repassword" placeholder="重復(fù)密碼"/>
    </p>
    <button class="btn btn-primary" id="submit">注冊(cè)</button>
    <a href="/login" class="btn btn-default">登錄</a>
</form>
</body>
</html>

案例2:接收POST提交

路由文件:webapp/routes/subform.js

router.post('/',function(req,res){
   //接收參數(shù)
   //  var username = req.body.username;
   //  var password = req.body.password;

    var username = req.param('username');
    var password = req.param('password');

    console.log(username,password);

    res.render('subform',{title:'表單提交'});
});

小結(jié)

GET和POST方式接收值,從直接效果上來看儡陨。

  • req.query 接收GET方式提交參數(shù)
  • req.body 接收POST方式提交參數(shù)
  • req.params 接收GET和POST提交參數(shù)

關(guān)于req.body
Express出力post請(qǐng)求是通過中間件bodyParser來完成的褪子,從app.js文件可發(fā)現(xiàn):

var bodyParser = require('body-parser');
...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

bodyParser中間件分析 application/x-www-form-urlencodedapplication/json請(qǐng)求,并將變量存入 req.body后才能獲取骗村。

案例3:字符串加密

表單提交后比如密碼等敏感信息需進(jìn)行加密出力嫌褪,NodeJS提交加密模塊crypto

入口文件添加路由
webapp/app.js

var crypto = require('./routes/crypto');
app.use('/crypto', crypto);

創(chuàng)建路由
webapp/routes/crypto.js

var express = require('express');
var router = express.Router();

var crypto = require('crypto');//載入加密模塊

// 獲取GET參數(shù)
router.get('/',function(req,res){
   res.render('crypto', {title:'字符串加密'});
});

//處理POST提交
router.post('/',function(req,res){
    //獲取參數(shù)
    var username = req.body.username;
    var password = req.body.password;

    //生成口令的散列值
    var md5 = crypto.createHash('md5');
    var md5pwd = md5.update(password).digest('hex');

    console.log(username,password,md5pwd);

    res.render('crypto',{title:'加密處理'});
});

//暴露接口
module.exports = router;

創(chuàng)建視圖
webapp/views/crypto.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<% include nav %>
<form method="post">
    <p class="form-group">
        <label for="username">賬戶</label>
        <input type="text" class="form-control" id="username" name="username" placeholder="賬戶"/>
    </p>
    <p class="form-group">
        <label for="password">密碼</label>
        <input type="password" class="form-control" name="password" id="password" placeholder="密碼"/>
    </p>
    <button class="btn btn-primary" type="submit">登錄</button>
</form>
</body>
</html>

小結(jié):
實(shí)用crypto提供的createHash(algorithm)胚股,采用給定算法生成hash對(duì)象笼痛。NodeJS的Hash算法提供了md5、sha1琅拌、sha264等缨伊。
update(data,[input_encoding])通過指定的input_encoding和傳入的data數(shù)據(jù)更新hash對(duì)象,input_encoding為可選參數(shù)财忽,無傳入則作為buffer處理倘核。

案例:會(huì)話控制

Internet通訊協(xié)議分為stateful和stateless泣侮,http是stateless協(xié)議及客戶端發(fā)送請(qǐng)求到服務(wù)器建立連接即彪,請(qǐng)求得到響應(yīng)后立即終端,服務(wù)器不記錄狀態(tài),服務(wù)器想確定是那個(gè)客戶端提交的請(qǐng)求隶校,就必須借助于session和cookie漏益。session存在于服務(wù)器端,需cookie協(xié)助才能完成深胳,服務(wù)端和客戶端通過session_id來建立聯(lián)系绰疤。

express可實(shí)用中間件express-session來實(shí)用session。

思路:登錄判斷舞终,不同頁面中判斷是否具有session轻庆,若有則表示登錄,無則表示未登錄敛劝。
步驟1:npm安裝中間件
webapp/package.json

  "dependencies": {
    "express-session":"latest",
  }

執(zhí)行并下載更新

npm install

步驟2:入口文件添加session模塊
webapp/app.js

//加載回話模塊
var session = require('express-session');
//傳入秘鑰加session_id
app.use(cookieParser('junchow'));
//實(shí)用中間件
app.use(session({secret:'junchow'}));

步驟3:入口添加登錄控制路由
webapp/app.js

var login = require('./routes/login');//表單提交
app.use('/login',login);

步驟4:創(chuàng)建登錄路由控制
webapp/routes/login.js

var express = require('express');
var router = express.Router();

var title = '登錄';
router.get('/',function(req,res){
    //清除session
    //req.session.destroy();
    //判斷session中是否具有登錄標(biāo)識(shí)符isLogin
    if(req.session.isLogin){
        //控制臺(tái)輸出
        console.log('session isLogin : '+req.session.isLogin);
        //頁面分配變量
        res.locals.isLogin = req.session.isLogin;
    }
    //頁面渲染
    res.render('login',{title:title});
});
router.post('/',function(req,res){
    //提交成功并寫入session
    req.session.isLogin = true;
    req.locals.isLogin = req.session.isLogin;

    res.render('login',{title:title});
});
module.exports = router;

步驟5:創(chuàng)建登錄視圖
webapp/views/login.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
    <% include nav %>
    <% if(locals.isLogin){ %>
        <p class="alert alert-success">用戶已登錄</p>
    <% }else{ %>
        <form method="post">
            <input type="submit" value="登錄" class="btn btn-primary">
        </form>
    <% } %>
</body>
</html>

出現(xiàn)問題:

Cannot set property 'isLogin' of undefined

案例:Cookies

cookies保存在客戶端安全性比較低余爆,一般要存入加密后的信息,建議要設(shè)置實(shí)用過期時(shí)間或不使用時(shí)刪除夸盟。cookies使用場(chǎng)景例如登錄中“記錄密碼”或“自動(dòng)登錄”蛾方。
步驟1:webapp/app.js

var cookie = require('./routes/cookie');
app.use('/cookie',cookie);

步驟2:webapp/routes/cookie.js

var express = require('express');
var router = express.Router();

var title = 'cookies';
router.get('/',function(req,res){
    if(req.cookies.isLogin){
        console.log('cookies isLogin: '+req.cookies.isLogin);
        req.session.isLogin = req.cookies.isLogin;
    }
    if(req.session.isLogin){
        console.log('cookies isLogin: ' + req.session.isLogin);
        res.locals.isLogin = req.session.isLogin;
    }
   res.render('cookie',{title:title});
});
router.post('/',function(req,res){
    req.session.isLogin = true;
    res.locals.isLogin = req.session.isLogin;
    //設(shè)置cookie過期時(shí)長(zhǎng)為60000毫秒(1分鐘)
    res.cookie('isLogin',true,{maxAge:60000});
    res.render('cookie',{title:title});
});

module.exports = router;

步驟3:webapp/views/cookie.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<% include nav %>
<% if(locals.isLogin){ %>
<p class="alert alert-warning">已登錄</p>
<% }else{ %>
<form method="post">
    <input type="submit" value="登錄" class="btn btn-primary">
</form>
<% } %>
</body>
</html>

小結(jié):清除session和cookie
// 清除cookies
res.clearCookie('isLogin');
// 清除session
req.session.destroy();

Chatper07.構(gòu)架網(wǎng)絡(luò)應(yīng)用案例

步驟1:創(chuàng)建項(xiàng)目

cd workspace
express -e webapp
cd webapp && npm install

步驟2:創(chuàng)建數(shù)據(jù)庫(kù)

CREATE DATABASE IF NOT EXISTS `test` CHARSET utf8;
USE test;
SET FOREIGN_KEY_CHECKS=0;

DROP TABLE IF EXISTS `user`;
CREATE TABLE IF NOT EXISTS `user`(
    id int(11) unsigned NOT NULL PRIMARY KEY COMMENT '主鍵',
    username varchar(20) NOT NULL DEFAULT '' COMMENT '賬戶',
    password varchar(64) NOT NULL DEFAULT '' COMMENT '密碼'
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用戶表';

步驟3:安裝模塊
修改package.json安裝session和mysql模塊

{
  "name": "webapp",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./bin/www"
  },
  "dependencies": {
    "body-parser": "~1.17.1",
    "cookie-parser": "~1.4.3",
    "debug": "~2.6.3",
    "ejs": "~2.5.6",
    "express": "~4.15.2",
    "morgan": "~1.8.1",
    "serve-favicon": "~2.4.2",
    "express-session":"latest",
    "mysql":"latest"
  }
}

安裝模塊

npm install

webapp/app.js添加代碼

//加載回話
var session = require('express-session');
//傳入秘鑰加session_id
app.use(cookieParser('junchow'));
//實(shí)用中間件
app.use(session({secret:'junchow'}));

步驟4:前端資源

npm install bootstrap
npm install jquery

從webapp/node_module/中尋找bootstrap和jquery提取文件加入public目錄。

步驟5.規(guī)劃路由
首頁:/
注冊(cè):register

routes/register.js
views/register.ejs

登錄:login

routes/login.js
views/login.ejs

安全退出:logout

routes/logout.js
views/logout.ejs

公共頁面:

header.ejs

步驟6:具體實(shí)現(xiàn)
創(chuàng)建數(shù)據(jù)訪問方法
webapp/models/user.js

var mysql = require('mysql');
var dbname = 'test';

//創(chuàng)建連接
var dbcfg = {host:'127.0.0.1', user:'root', password:'root'};
var pool = mysql.createPool(dbcfg);
pool.on('connection', function(connection){
   connection.query('SET SESSION auto_increment_increment = 1');
});
pool.getConnection(function(err,connection){
    //選擇數(shù)據(jù)庫(kù)
    var sql = "USE "+ dbname;
    connection.query(sql, function(err){
       if(err){
           console.log("USE ERROR: "+err.message);
           return;
       }
       console.log("USE SUCCESS");
    });
    //保存數(shù)據(jù)
    User.prototype.save = function save(callback){
        var obj = {username:this.username, password:this.password};
        var sql = "INSERT INTO user(username,password) VALUES(?,?)";
        connection.query(sql, [obj.username, obj.password], function(err,result){
            if(err){
                console.log("save error: "+sql, err.message);
                return;
            }
            connection.release();
            console.log("invoked save");
            callback(err,result);
        });
    };
    // 根據(jù)賬戶獲取用戶數(shù)目
    User.getUserNumByName = function getUserNumByName(username,callback){
        var sql = "SELECT COUNT(1) AS num FROM user WHERE username=?";
        connection.query(sql,[username],function(err,result){
            if(err){
                console.log("getUserNumByName error:"+err.message);
                return;
            }
            console.log("invoked getUserNumByName");
            callback(err,result);
        });
    };
    //根據(jù)賬戶獲取用戶信息
    User.getUserByUsername = function getUserByUsername(username,callback){
      var sql = "SELECT * FROM user WHERE 1=1 AND username=?";
      connection.query(sql, [username], function(err,result){
          if(err){
              console.log("getUserByUsername error: "+err.message);
              return;
          }
          connection.release();
          console.log("invoked getUserByUsername");
          callback(err,result);
      });
    };
});
//暴露接口
function User(obj){
    this.username = obj.username;
    this.password = obj.password;
}
module.exports = User;
2017-04-18_17-08-15.png

注冊(cè)模塊
webapp/app.js

var register = require('./routes/register');
app.use('/register',register);

webapp/routes/register.js

var express = require('express');
var router = express.Router();

var User = require('../models/user.js');
var crypto = require('crypto');

var title = '注冊(cè)';
router.get('/', function(req, res, next) {
    res.render('register', { title: title});
});


router.post('/', function(req,res){
    var username = req.body['username'];
    var password = req.body['password'];
    var repassword = req.body['repassword'];

    var md5 = crypto.createHash('md5');
    password = md5.update(password).digest('hex');

    var obj = new User({username:username, password:password});

    //檢查賬戶是否存在
    User.getUserNumByName(obj.username, function(err,result){
        if(result!=null && result[0]['num']>0){
            err = '賬戶已存在';
        }
        if(err){
            res.locals.error = err;
            res.render('register', {title:title});
            return;
        }
        obj.save(function(err,result){
            console.log(err,result);
           if(err){
               res.locals.error = err;
               res.render('register',{title:title});
               return;
           }
           if(result.insertId > 0){
               res.locals.success = '注冊(cè)成功上陕,請(qǐng)<a class="btn btn-link" href="/login" role="button">登錄</a>桩砰!';
           }else{
               res.locals.error = err;
           }
           res.render('register',{title:title});
        });
    });
});

module.exports = router;

webapp/views/register.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<div id="container" class="container">
    <h2 class="page-header"><%=title%></h2>
    <% if(locals.success){%>
    <div id="success" class="alert alert-success"><%-success%></div>
    <%}%>
    <% if(locals.error){ %>
    <div id="warning" class="alert alert-warning"><%=error%></div>
    <%}%>
    <form role="form" method="post">
        <p class="form-group">
            <label for="username">賬戶</label>
            <input type="text" class="form-control" id="username" name="username" placeholder="賬戶"/>
        </p>
        <p class="form-group">
            <label for="password">密碼</label>
            <input type="password" class="form-control" name="password" id="password" placeholder="密碼"/>
        </p>
        <p class="form-group">
            <label for="repassword">重復(fù)密碼</label>
            <input type="password" class="form-control" name="repassword" id="repassword" placeholder="重復(fù)密碼"/>
        </p>
        <button class="btn btn-primary" id="submit">注冊(cè)</button>
        <a href="/login" class="btn btn-default">登錄</a>
    </form>
</div>
<script src="/javascripts/jquery.js"></script>
<script>
    String.prototype.format = function(args){
        var that = this;
        if(arguments.length > 0){
            if(arguments.length == 1 && typeof(args)=='object'){
                for(var k in args){
                    if(args[k] != undefined){
                        var reg = new RegExp('({'+k+'})', 'g');
                        that = that.replace(reg, args[k]);
                    }
                }
            }else{
                for(var i=0; i<arguments.length; i++){
                    if(arguments[i] != undefined){
                        var reg = new RegExp('({)'+i+'(})','g');
                        that = that.replace(reg, arguments[i]);
                    }
                }
            }
        }
        return that;
    }
    //表單驗(yàn)證
    $(function(){
       $('#submit').on('click',function(){
           var username = $('#username');
           var usernameVal = $.trim(username.val());

           var password = $('#password');
           var passwordVal = $.trim(password.val());

           var repassword = $('#repassword');
           var repasswordVal = $.trim(repassword.val());

           var tip = '<div id="tip" class="alert alert-warning">{0}</div>';

           $('#success,#warning,#tip').remove();

           if(usernameVal.length == 0){
               $('#container').prepend(tip.format('賬戶不能為空!'));
               username.focus();
               return false;
           }
           if(passwordVal.length == 0){
               $('#container').prepend(tip.format('密碼不能為空释簿!'));
               password.focus();
               return false;
           }
           if(repasswordVal.length == 0){
               $('#container').prepend(tip.format('重復(fù)密碼不能為空亚隅!'));
               repassword.focus();
               return false;
           }
           if(passwordVal != repasswordVal){
               $('#container').prepend(tip.format('兩次密碼不一致!'));
               repassword.focus();
               return false;
           }

           return true;
       });
    });
</script>
</body>
</html>
2017-04-18_17-24-29.png

登錄模塊

webapp/app.js

var login = require('./routes/login');
app.use('/login',login);

webapp/routes/login.js

var express = require('express');
var router = express.Router();

var crypto = require('crypto');
var User = require('../models/user.js');
var title = '登錄';

router.get('/',function(req,res){
   res.render('login',{title:title});
});
router.post('/',function(req,res){
    var username = req.body['username'];
    var password = req.body['password'];
    var rem = req.body['rem'];
    var md5 = crypto.createHash('md5');

    User.getUserByUsername(username,function(err,ret){
       if(ret==''){
           res.locals.error = '賬戶不存在';
           res.render('login',{title:title});
           return;
       }
       password = md5.update(password).digest('hex');
       if(ret[0].username!=username || ret[0].password!=password){
           res.locals.error = '賬戶或密碼錯(cuò)誤';
           res.render('login',{title:title});
           return;
       }else{
           if(rem){
               res.cookie('isLogin', username, {maxAge:60000});
           }
           res.locals.username = username;
           req.session.username = res.locals.username;
           console.log(req.session.username);

           res.redirect('/home');
           return;
       }
    });
});

module.exports = router;

webapp/views/login.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<div id="container" class="container">
    <h2 class="page-header"><%=title%></h2>
    <% if(locals.success){%>
    <div id="success" class="alert alert-success"><%-success%></div>
    <%}%>
    <% if(locals.error){ %>
    <div id="warning" class="alert alert-warning"><%=error%></div>
    <%}%>
    <form role="form" method="post">
        <p class="form-group">
            <label for="username">賬戶</label>
            <input type="text" class="form-control" id="username" name="username" placeholder="賬戶"/>
        </p>
        <p class="form-group">
            <label for="password">密碼</label>
            <input type="password" class="form-control" name="password" id="password" placeholder="密碼"/>
        </p>
        <p class="form-group">
            <label class="checkbox"><input type="checkbox" name="rem" id="rem">自動(dòng)登錄</label>
        </p>
        <button class="btn btn-primary" id="submit">登錄</button>
        <a href="/register" class="btn btn-default">注冊(cè)</a>
    </form>
</div>
<script src="/javascripts/jquery.js"></script>
<script>
    String.prototype.format = function(args){
        var that = this;
        if(arguments.length > 0){
            if(arguments.length == 1 && typeof(args)=='object'){
                for(var k in args){
                    if(args[k] != undefined){
                        var reg = new RegExp('({'+k+'})', 'g');
                        that = that.replace(reg, args[k]);
                    }
                }
            }else{
                for(var i=0; i<arguments.length; i++){
                    if(arguments[i] != undefined){
                        var reg = new RegExp('({)'+i+'(})','g');
                        that = that.replace(reg, arguments[i]);
                    }
                }
            }
        }
        return that;
    }
    //表單驗(yàn)證
    $(function(){
        $('#submit').on('click',function(){
            var username = $('#username');
            var usernameVal = $.trim(username.val());

            var password = $('#password');
            var passwordVal = $.trim(password.val());

            var tip = '<div id="tip" class="alert alert-warning">{0}</div>';

            $('#success,#warning,#tip').remove();

            if(usernameVal.length == 0){
                $('#container').prepend(tip.format('賬戶不能為空辕万!'));
                username.focus();
                return false;
            }
            if(passwordVal.length == 0){
                $('#container').prepend(tip.format('密碼不能為空枢步!'));
                password.focus();
                return false;
            }

            return true;
        });
    });
</script>
</body>
</html>

首頁模塊

webapp/app.js

var home = require('./routes/home');
app.use('/home',home);

webapp/routes/home.js

var express = require('express');
var router = express.Router();

var title = '主頁';
router.get('/',function(req,res){
    if(req.cookies.isLogin){
        console.log('cookies isLogin: '+req.cookies.isLogin);
        req.session.username = req.cookies.isLogin;
    }
    if(req.session.username){
        console.log('session username : '+req.session.username);
        res.locals.username = req.session.username;
    }else{
        res.redirect('/login');
        return;
    }
    res.render('home',{title:title});
});

module.exports = router;

webapp/views/home.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<% include header %>
<div id="container" class="container">

</div>
<script src="/javascripts/jquery.js"></script>
</body>
</html>

webapp/views/header.ejs

<div class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="container">
        <div class="navbar-header"><a href="/home" class="navbar-brand">home</a></div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right">
                <% if(locals.username){%><li><a href="#"><%=username%></a></li><%}%>
                <li><a href="/logout">安全退出</a></li>
            </ul>
        </div>
    </div>
</div>

退出模塊

webapp/app.js

var logout = require('./routes/logout');
app.use('/logout',logout);

webapp/routes/logout.js

var express = require('express');
var router = express.Router();

router.get('/',function(req,res){
    req.session.destroy();
    res.redirect('/home');
});

module.exports = router;

Chatper08.文件上傳

formidable的文件上傳模塊
安裝模塊:webapp/package.json

"formidable":"latest"

安裝依賴

npm install

入口文件添加:webapp/app.js

var upload = require('./routes/upload');
app.use('/upload',upload);

路由處理文件:web/routes/upload.js

var express = require('express');
var router = express.Router();

//上傳處理類
var formidable = require('formidable');
var fs = require('fs');
var UPLOAD_FOLDER = '/upload/';

var title = '上傳';
router.get('/',function(req,res){
    res.render('upload',{title:title});
});

router.post('/',function(req,res){
    //創(chuàng)建上傳表單
    var form = new formidable.IncomingForm();
    //設(shè)置字符集
    form.encoding = 'utf-8';
    //設(shè)置上傳陌路
    form.uploadDir = 'public'+UPLOAD_FOLDER;
    //是否保留文件后綴
    form.keepExtensions = true;
    //顯示上傳文件大小
    form.maxFieldsSize = 2*1024*1024;

    //上傳處理
    form.parse(req, function(err,fields,files){
       if(err){
           res.locals.error = true;
           res.render('upload', {title:title});
           return;
       }

       var ext = '';
       switch(files.type){
           case 'image/pjpeg': ext = 'jpg'; break;
           case 'image/jpeg': ext = 'jpg'; break;
           case 'image/png': ext = 'png'; break;
           case 'image/x-png': ext = 'png'; break;
       }

       if(ext.length == 0){
           res.locals.error = '僅支持png或jpg格式圖片';
           res.render('upload',{title:title});
           return;
       }

       var picname = Math.random()+'.'+ext;
       var picfile = form.uploadDir+picname;

       fs.renameSync(files.path, picfile);
    });

    res.locals.success = '上傳成功';
    res.render('upload', {title:title});
});


module.exports = router;

模板文件:webapp/views/upload.ejs

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title><%=title%></title>
    <link rel="stylesheet" href="/stylesheets/bootstrap.css"/>
</head>
<body>
<div id="container" class="container">
    <form class="form" role="form" method="post" enctype="multipart/form-data">
        <h2 class="page-header">文件上傳</h2>
        <p class="input-group">
            <input type="file" name="pid" id="pic" class="form-control"/>
            <span class="input-group-btn">
                <button class="btn btn-primary" type="submit" id="submit">上傳</button>
            </span>
        </p>
    </form>
</div>
<script src="/javascripts/jquery.js"></script>
<script>
    String.prototype.format = function(args){
        var that = this;
        if(arguments.length > 0){
            if(arguments.length == 1 && typeof(args) == 'object'){
                for(var i in args){
                    if(args[i] != undefined){
                        var reg = new RegExp("({"+i+"})", "g");
                        that = that.replace(reg, args[i]);
                    }
                }
            }else{
                for(var i=0; i<arguments.length;i++){
                    if(arguments[i] != undefined){
                        var reg = new RegExp("({)"+i+"(})","g");
                        that = that.replace(reg,arguments[i]);
                    }
                }
            }
        }
        return that;
    }

    $(function(){
       $('#submit').on('click',function(){
           var picVal = $('#pic').val();
           var tip = '<div id="tip" class="alert alert-warning">{0}</div>';

           if(picVal.length == 0){
               $('#container').prepend(tip.format('請(qǐng)選擇上傳文件'));
               return false;
           }

           var ext = picVal.substring(picVal.lastIndexOf('.'), picVal.length).toLowerCase();
           console.log(ext);
           if(ext!='.png' && ext!='.jpg'){
               $('#container').prepend(tip.format('僅支持png或jpg格式圖片!'));
               return false;
           }

           return true;
       });
    });
</script>
</body>
</html>

運(yùn)行中錯(cuò)誤

Error: Can't set headers after they are sent.
    at ServerResponse.OutgoingMessage.setHeader (_http_outgoing.js:357:11)
    at ServerResponse.header (D:\nodejs\webapp\node_modules\express\lib\response
.js:725:10)
    at ServerResponse.send (D:\nodejs\webapp\node_modules\express\lib\response.j
s:170:12)
    at done (D:\nodejs\webapp\node_modules\express\lib\response.js:962:10)
    at tryHandleCache (D:\nodejs\webapp\node_modules\ejs\lib\ejs.js:208:10)
    at View.exports.renderFile [as engine] (D:\nodejs\webapp\node_modules\ejs\li
b\ejs.js:412:10)
    at View.render (D:\nodejs\webapp\node_modules\express\lib\view.js:128:8)
    at tryRender (D:\nodejs\webapp\node_modules\express\lib\application.js:640:1
0)
    at EventEmitter.render (D:\nodejs\webapp\node_modules\express\lib\applicatio
n.js:592:3)
    at ServerResponse.render (D:\nodejs\webapp\node_modules\express\lib\response
.js:966:7)

Chapter09.與Redis交互

Redis簡(jiǎn)介

  • 高性能key-value存儲(chǔ)
  • 可采用in-memory數(shù)據(jù)集的方式渐尿,也可采用磁盤存儲(chǔ)方式醉途。
  • 支持主從同步、pub/sub等
    redis服務(wù)端
    redis客戶端

在windows中下載redis服務(wù)端解壓后運(yùn)行redis-server.exe砖茸,redis.conf作為配置文件隘擎。

安裝node_redis

npm install redis
npm install hiredis redis

區(qū)別:hiredis是非阻塞的,速度更快凉夯。node_redis默認(rèn)以它作為解釋器货葬,若沒安裝則會(huì)用純javascript解釋器。

創(chuàng)建連接

//連接到本地redis服務(wù)器
var redis = require('redis');
var client = redis.createClient();//返回RedisClient對(duì)象

//連接到遠(yuǎn)程redis服務(wù)器
var redis = require('redis');
var RDS_PORT = 6379;
var RDS_HOST = '127.0.0.1';
var RDS_OPTS = {};
var client = redis.createClient(RDS_PORT,RDS_HOST,RDS_OPTS);//返回RedisClient對(duì)象

設(shè)置遠(yuǎn)程連接密碼
在redis.conf配置文件打開requirepass foobared劲够,其中foobared是密碼震桶。

密碼認(rèn)證

//連接到redis服務(wù)器并認(rèn)證
var redis = require('redis');
var RDS_PORT = 6379;
var RDS_HOST = '127.0.0.1';
var RDS_PWD = 'foobared';
var RDS_OPTS = {auth_pass:RDS_PWD};
//返回RedisClient對(duì)象
var client = redis.createClient(RDS_PORT,RDS_HOST,RDS_OPTS);

//密碼認(rèn)證
client.auth(RDS_PWD,function(){
    console.log('通過認(rèn)證');
});

觸發(fā)事件

//當(dāng)與redis服務(wù)器連接成功后觸發(fā)ready事件
//表示已準(zhǔn)備好接收命令,當(dāng)此事件出發(fā)之前client命令會(huì)存在的隊(duì)列中征绎,當(dāng)一切準(zhǔn)備就緒后調(diào)用蹲姐。
client.on('ready',function(err){
    console.log('ready',err);
});
//在不設(shè)置client.options.no_ready_check情況下,客戶端觸發(fā)connect同時(shí)會(huì)發(fā)出ready。
//若設(shè)置了client.options.no_ready_check柴墩,當(dāng)這個(gè)stream被連接時(shí)會(huì)觸發(fā)connect忙厌,此時(shí)就可以自由嘗試發(fā)命令。
client.on('connect',function(){
    //單值設(shè)置
   client.set('author', 'junchow', redis.print);//設(shè)置單個(gè)key和value
   client.get('author',redis.print);//通過key獲取value

    //多值設(shè)置
    client.hmset('short',{'js':'javascript', 'C#':'C sharp'}, redis.print);
    client.hmset('short', 'SQL','Structured Query Language', 'HTML', 'HyperText Markup Language', redis.print);
    client.hgetall('short',function(err,res){
       if(err){
           console.log('Error:' + err);
           return;
       }
       console.dir(res);
   });
});

Chapter10.與MongoDB交互

MongoDB高性能的NoSQL數(shù)據(jù)庫(kù)江咳,支持索引逢净、集群、復(fù)制歼指、故障轉(zhuǎn)移爹土、各種語言驅(qū)動(dòng)程序,高伸縮性踩身。
MongoDB的NodeJS驅(qū)動(dòng):node-mongodb-native

[下載MongoDB](http://www.mongodb.org/downloads

安裝服務(wù)端
步驟1:創(chuàng)建數(shù)據(jù)庫(kù)和日志存放目錄
在MongoDB目錄下創(chuàng)建db和log兩個(gè)文件夾着饥,并在log目錄下新建mongodb.log文件。
步驟2:創(chuàng)建配置文件
在MongoDB下的bin目錄下創(chuàng)建mongo.conf文件惰赋,添加配置

##數(shù)據(jù)庫(kù)目錄
dbpath=D:\MongoDB\db
##日志輸出文件
logpath=D:\MongoDB\log\mongodb.log

步驟3:添加環(huán)境變量
D:\MongoDB\bin添加到環(huán)境變量path中
步驟4:安裝服務(wù)
mongod --config "D:\MongoDB\bin\mongo.conf" --install
步驟5:?jiǎn)?dòng)服務(wù)
net start mongodb
步驟6:打開服務(wù)端
mongo

備注:MongoDB默認(rèn)端口為27017

庫(kù)操作
//查看數(shù)據(jù)庫(kù)列表
show dbs
//檢查當(dāng)前選擇的數(shù)據(jù)庫(kù)
db
//創(chuàng)建數(shù)據(jù)庫(kù)
use test
//use創(chuàng)建的數(shù)據(jù)庫(kù)需插入數(shù)據(jù)后才能顯示
db.user.insert({"name":"alice"})
//刪除當(dāng)前數(shù)據(jù)庫(kù)
db.dropDatabase()

集合操作
//創(chuàng)建集合
db.createCollection(name,options)
//查看集合列表
show collections
//刪除集合
db.collection.drop()
//向集合中插入數(shù)據(jù)
db.user.insert({"name":"alice"})
//獲取集合數(shù)據(jù)
db.user.find()
//獲取集合條數(shù)
db.user.find().count()

NodeJS操作MongoDB
安裝
npm install mongodb
創(chuàng)建連接

var MongoClient = require('mongodb').MongoClient;
var MongoLink = 'mongodb://127.0.0.1:27017/test';
//連接數(shù)據(jù)庫(kù)
MongoClient.connect(MongoLink,function(err,db){
    console.log('連接成功');
});

插入數(shù)據(jù)

var MongoClient = require('mongodb').MongoClient;
var MongoLink = 'mongodb://127.0.0.1:27017/test';
//插入數(shù)據(jù)
var insertData = function(db,callback){
  //連接集合
    var collection = db.collection('user');
    //插入數(shù)據(jù)
    var data = [
        {"name":"alice","age":18},
        {"name":"ben","age":19},
        {"name":"carl","age":20}
    ];
    collection.insert(data,function(err,res){
       if(err){
           console.log(err);
           return;
       }
       callback(res);
    });
};
//連接數(shù)據(jù)庫(kù)
MongoClient.connect(MongoLink,function(err,db){
    console.log('連接成功');
    //插入數(shù)據(jù)
    insertData(db,function(res){
       console.log(res);
       db.close();//關(guān)閉數(shù)據(jù)庫(kù)
    });
});

查詢數(shù)據(jù)

var MongoClient = require('mongodb').MongoClient;
var MongoLink = 'mongodb://127.0.0.1:27017/test';

//查詢數(shù)據(jù)
var findData = function(db,callback){
    //連接集合
    var collection = db.collection('user');
    //查詢條件
    var where = {"name":"alice"};
    //查詢數(shù)據(jù)
    collection.find(where).toArray(function(err,res){
       if(err){
           console.log(err);
           return;
       }
       callback(res);
    });
};
//連接數(shù)據(jù)庫(kù)
MongoClient.connect(MongoLink,function(err,db){
    console.log('連接成功');
    //查詢數(shù)據(jù)
    findData(db,function(res){
       console.log(res);
       db.close();
    });
});

更新數(shù)據(jù)

var MongoClient = require('mongodb').MongoClient;
var MongoLink = 'mongodb://127.0.0.1:27017/test';
//修改數(shù)據(jù)
var updateData = function(db,callback){
  var collection = db.collection('user');
  var where = {"name":"alice"};
  var data = {$set:{"age":30}};
  collection.update(where, data, function(err,res){
      if(err){
          console.log(err);
          return;
      }
      callback(res);
  });
};
//連接數(shù)據(jù)庫(kù)
MongoClient.connect(MongoLink,function(err,db){
    console.log('連接成功');
    //更新數(shù)據(jù)
    updateData(db,function(res){
        console.log(res);
        db.close();
    });
});

注意:以上更新發(fā)現(xiàn)僅更新一條

刪除數(shù)據(jù)

var MongoClient = require('mongodb').MongoClient;
var MongoLink = 'mongodb://127.0.0.1:27017/test';
//刪除數(shù)據(jù)
var removeData = function(db,callback){
    var collection = db.collection('user');
    var where = {"name":"alice"};
    collection.remove(where,function(err,res){
        if(err){
            console.log(err);
            return;
        }
        callback(res);
    });
};
//連接數(shù)據(jù)庫(kù)
MongoClient.connect(MongoLink,function(err,db){
    console.log('連接成功');
    //刪除數(shù)據(jù)
    removeData(db,function(res){
       console.log(res);
       db.close();
    });
});

Chapter11.數(shù)據(jù)采集器

使用request和cheerio發(fā)送請(qǐng)求并實(shí)用正則解析數(shù)據(jù)來完成數(shù)據(jù)采集宰掉。

  • request 用于http請(qǐng)求
  • cheerio 用于提取request返回的html中所需數(shù)據(jù)

步驟1:安裝
webapp/package.json中依賴中添加

"request":"*",
"cheerio":"*"

執(zhí)行命令

cd webapp && npm install

采集代碼
思路:實(shí)用request一個(gè)get請(qǐng)求,請(qǐng)求回調(diào)中返回body即html代碼赁濒,通過cherrio庫(kù)的jquery語法操作解析轨奄,獲取所需要的數(shù)據(jù)。

/*數(shù)據(jù)采集*/
var request = require('request');
var cheerio = require('cheerio');
var url = 'http://36kr.com';

//開啟數(shù)據(jù)采集器
function startup(){
    doRequest(url);
}
//請(qǐng)求數(shù)據(jù)
function doRequest(url){
    request({url:url,method:'GET'},function(err,res,body){
       if(err){
           console.log(err,res,body);
           return;
       }
       //解析數(shù)據(jù)
        doParse(body);
    });
}
//解析數(shù)據(jù)
function doParse(body){
    var $ = cheerio.load(body);
    
    var articles = $('article');
    for(var i=0; i=articles.length; i++){
        var article = articles[i];
        var desc = $(article).find('.desc');
        if(desc.length==0){
            continue;
        }

        var coverDom = $(article).children().first();
        var titleDom = $(desc).find('.info_flow_news_title');
        var timeagoDom = $(desc).find('.timeago');

        var titleVal = titleDom.text();
        var urlVal = titleDom.attr('href');
        var timeVal = timeDom.attr('title');
        var timeSecs = new Date(timeVal).getTime()/1000;
        var converUrl = converDom.attr('data-lazyload');

        if(urlVal!=undefined){
            console.info('----------------------------------');
            console.info('標(biāo)題:'+titleVal);
            console.info('地址:'+urlVal);
            console.info('時(shí)間:'+timeSecs);
            console.info('封面:'+converUrl);
        }else{
            console.info('error');
        }
    }
}
startup();

升級(jí)版本:加入代理
若需長(zhǎng)期實(shí)用為避免網(wǎng)站屏蔽拒炎,還需添加一個(gè)代理列表挪拟。

/*數(shù)據(jù)采集*/
var request = require('request');
var cheerio = require('cheerio');
var url = 'http://36kr.com';

//長(zhǎng)期采集為防止網(wǎng)站屏蔽加入代理列表
var proxy = require('./proxylist.js');

//開啟數(shù)據(jù)采集器
function startup(){
    doRequest(url);
}
//請(qǐng)求數(shù)據(jù)
function doRequest(url){
    //實(shí)用代理發(fā)送請(qǐng)求
    request({url:url,method:'GET',proxy:proxy.getProxy()},function(err,res,body){
       if(err){
           console.log(err,res,body);
           return;
       }
       //解析數(shù)據(jù)
        doParse(body);
    });
}
//解析數(shù)據(jù)
function doParse(body){
    var $ = cheerio.load(body);

    var articles = $('article');
    for(var i=0; i=articles.length; i++){
        var article = articles[i];
        var desc = $(article).find('.desc');
        if(desc.length==0){
            continue;
        }

        var coverDom = $(article).children().first();
        var titleDom = $(desc).find('.info_flow_news_title');
        var timeagoDom = $(desc).find('.timeago');

        var titleVal = titleDom.text();
        var urlVal = titleDom.attr('href');
        var timeVal = timeDom.attr('title');
        var timeSecs = new Date(timeVal).getTime()/1000;
        var converUrl = converDom.attr('data-lazyload');

        if(urlVal!=undefined){
            console.info('----------------------------------');
            console.info('標(biāo)題:'+titleVal);
            console.info('地址:'+urlVal);
            console.info('時(shí)間:'+timeSecs);
            console.info('封面:'+converUrl);
        }else{
            console.info('error');
        }
    }
}
startup();
setInterval(startup,10000);//間隔執(zhí)行

升級(jí)版本:支持HTTPS

//請(qǐng)求數(shù)據(jù)
function doRequest(url){
    //支持HTTPS的header頭
    request({url:url,method:'GET',headers:{'User-Agent':'wilson'}},function(err,res,body){
       if(err){
           console.log(err,res,body);
           return;
       }
       //解析數(shù)據(jù)
        doParse(body);
    });
}

Chapter12.定時(shí)任務(wù)

定時(shí)任務(wù)應(yīng)用場(chǎng)景一般包括定時(shí)導(dǎo)出數(shù)據(jù)、定時(shí)發(fā)送消息击你、定時(shí)發(fā)送郵件給用戶玉组、定時(shí)備份文件。
目標(biāo):使用node-schedule來完成定時(shí)任務(wù)
安裝:
npm install node-schedule
定時(shí)任務(wù)

var schedule = require('node-schedule');
function cron(){
   //每隔30秒觸發(fā)
    schedule.scheduleJob('30 * * * * *',function(){
        console.log(new Date());
    })
}
cron();

遞歸定時(shí)器

var schedule = require('node-schedule');
function cron(){
    //遞歸定時(shí)器
    var rule = new schedule.RecurrenceRule();
    rule.second = 0;

    schedule.scheduleJob(rule,function(){
        console.log(new Date());
    });
}
cron();

對(duì)象文本定時(shí)器

var schedule = require('node-schedule');
function cron(){
    var cfg = {hour:1, minute:1, dayOfWeek:1};
    schedule.scheduleJob(cfg,function(){
        console.log(new Date());
    });
}
cron();

取消定時(shí)器

var schedule = require('node-schedule');
function cron(){
    var counter = 1;
    var i = schedule.scheduleJob('* * * * * *',function(){
       console.log(counter);
       counter++;
    });
    setTimeout(function(){
        console.log('cancel');
        i.cancel();
    },5000);
}
cron();
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丁侄,一起剝皮案震驚了整個(gè)濱河市惯雳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鸿摇,老刑警劉巖石景,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拙吉,居然都是意外死亡潮孽,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門筷黔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來往史,“玉大人,你說我怎么就攤上這事佛舱∽道” “怎么了揽乱?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粟矿。 經(jīng)常有香客問我,道長(zhǎng)损拢,這世上最難降的妖魔是什么陌粹? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮福压,結(jié)果婚禮上掏秩,老公的妹妹穿的比我還像新娘。我一直安慰自己荆姆,他們只是感情好蒙幻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著胆筒,像睡著了一般邮破。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仆救,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天抒和,我揣著相機(jī)與錄音,去河邊找鬼彤蔽。 笑死摧莽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的顿痪。 我是一名探鬼主播镊辕,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蚁袭!你這毒婦竟也來了征懈?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤揩悄,失蹤者是張志新(化名)和其女友劉穎受裹,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體虏束,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡棉饶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了镇匀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片照藻。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖汗侵,靈堂內(nèi)的尸體忽然破棺而出幸缕,到底是詐尸還是另有隱情群发,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布发乔,位于F島的核電站熟妓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏栏尚。R本人自食惡果不足惜起愈,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望译仗。 院中可真熱鬧抬虽,春花似錦、人聲如沸纵菌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽咱圆。三九已至笛辟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間序苏,已是汗流浹背隘膘。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杠览,地道東北人弯菊。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像踱阿,于是被迫代替她去往敵國(guó)和親管钳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355