async

可控制并发、串行执行函数。

//async.waterfall(tasks, [callback]);
//task是函数组成的数组,callback是中途出错或者全部执行完后的回调函数。
async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
   // result now equals 'done'    
});
//async.mapLimit(arr, limit, iterator, callback)
//mapLimit是同时发起多个异步操作,然后一起等待callback的返回,返回一个就再发起下一个。
superagent.get(cnodeUrl).end(function(err, res) {
    if (err) {
        return console.error(err)
    }
    // 存放标题url的数组
    var topicUrls = [];
    var $ = cheerio.load(res.text);
    //获取首页所有的链接
    $('#topic_list .topic_title').each(function (idx, el) {
        if (idx < 40) {
            var $el = $(el);
            var href = url.resolve(cnodeUrl, $el.attr('href'));
            topicUrls.push(href);           
        }
    });
    //并发连接数的计数器
    var concurrencyCount = 0;
    var fetch = function (url, callback) {
        console.time('  耗时');
        concurrencyCount++;
        superagent.get(url).end( function (err, res) {
            console.log('并发数:', concurrencyCount--, 'fetch', url);
            //var $ = cheerio.load(res.text);
            callback(null, [url, res.text]);
        });
    }
    async.mapLimit(topicUrls, 11, function (topicUrl, callback) {
        fetch(topicUrl, callback);
        console.timeEnd("  耗时");
    }, function (err, result) {
        result = result.map( function (pair) {
            var $ = cheerio.load(pair[1]);
            return ({
                title: $('.topic_full_title').text().trim(),
                href: pair[0],
                comment1: $('.reply_content').eq(0).text().trim(),
                author1: $('.reply_author').eq(0).text().trim() || "评论不存在",    
            });
        });
        console.log('final:\n',result);
    });
});

node-xlsx

生成 xlsx 表格文件。

fs.writeFile('cnbNews.xlsx', xlsx.build(dataArr), 'utf-8', function(err){
    if(err){
        console.log('write file error!');
    }else{
        console.log('write file success!');
    }
});

mailer

NodeJS 邮件发送模块,支持定制基于 Mustache 的模板正文。

var nodemailer = require('nodemailer').createTransport({
    service: 'qq',
    auth: {
        user: 'binylu@qq.com',
        pass: 'ohclxvvmydtejejf' //授权码,通过QQ获取
    }
});
var mailOptions = {
    from: 'binylu@qq.com', // 发送者
    to: ['1559867170@qq.com,1393302015@qq.com'], // 接受者,可以同时发送多个,以逗号隔开
    subject: '小陆测试nodemailer2.5.0邮件发送', // 标题
    //text: 'Hello world', // 文本
    html: `<h2>小陆测试nodemailer基本使用:</h2>`,
    attachments: [
        {
            filename: 'package.json',
            path: './package.json'
        },
        {
            filename: 'content',
            content: '发送内容'
        }
    ]
    //attachments:1可发送文件,2可发送自定义文件内容
};
nodemailer.sendMail(mailOptions, function(err, info) {
    if (err) {
        console.log(err);
        return;
    }
    ...
});

socket.io

适合构建跨浏览器的实时应用,提供类似 WebSockets 的API。常用:聊天室。

//后端
io.on('connection', function(socket){
  console.log('a user connected')
  socket.on("join", function (name) {
    usocket[name] = socket
    io.emit("join", name)
  })
  socket.on("message", function (msg) {
    io.emit("message", msg) //将新消息广播出去
  })
});
//前端
var name   = prompt("请输入你的昵称:");
var socket = io()
//发送昵称给后端,并更改网页title
socket.emit("join", name)
document.title = name + "的群聊"
socket.on("join", function (user) {
  addLine(user + " 加入了群聊")
})
//接收到服务器发来的message事件
socket.on("message", function(msg) {
  addLine(msg)
})
//当发送按钮被点击时
$('form').submit(function () {
  var msg = $("#m").val() //获取用户输入的信息
  socket.emit("message", msg) //将消息发送给服务器
  $("#m").val("") //置空消息框
  return false //阻止form提交
})
function addLine(msg) {
  $('#messages').append($('<li>').text(msg));
}

express

Express 是基于Node.js,高性能、一流的 web 开发框架。

const app = express();
//设置可读取的静态目录
app.use('/', express.static(__dirname + '/'));
//全局use
app.use('/',(req,res,next)=>{})
//指定局部post,新增
app.post('/',(req,res,next)=>{})
//指定局部get,获取
app.get('/',(req,res,next)=>{})
//其他:put更新修改,delete删除
app.put('/',(req,res,next)=>{})
app.delete('/',(req,res,next)=>{})
// 打开监听端口
app.listen(3000, () => {
    console.log('running => 3000')
})
//设置允许跨域访问该服务
app.all('*', function(req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Methods', '*');
    // res.header('Access-Control-Allow-Headers', 'Content-Type');
    // res.header('Content-Type', 'application/json;charset=utf-8');
    next();
});

cookie-parser

登陆验证。

//引用
app.use(cookieParser());
//相关设置
app.use(session({
  secret: 'wenzi', // 建议使用 128 个字符的随机字符串
  cookie: { maxAge: 60*60*1000 },
  resave : false,
  saveUninitialized : true
}));
//路由
app.use(function(req, res, next){
    // 如果cookie中存在,则说明已经登录
    if( req.session.user ){
        //res.locals = res.render
        res.locals.user = {
            uid : req.session.user.uid,
            username : req.session.user.username
        }
    }else{
        res.locals.user = {};
    }
    next();
})

ejs

模板,express自带,无需require,语法较与原生匹配。

app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

node_redis

是为 NodeJS 而写的 Redis client,它支持所有 Redis 命令。

body-parser

主要处理post的请求数据。

//post支持传送的json数据量
app.use(bodyParser.json({
    limit: '100mb'
}));
// extended: true:表示使用第三方模块qs来处理
app.use(bodyParser.urlencoded({
    limit: '100mb',
    extended: false
}));

swig

模板。语法有点生涩。

// 静态页面类型为 html
app.engine('html', swig.renderFile);
// 读取地址固定: ./views
app.set('views', './');
app.set('view engine', 'html');
// 缓存关闭 
swig.setDefaults({
    cache: false
});

formidable

上传文件处理。

app.use('/upload', (req, res) => {
    var form = new formidable.IncomingForm();
    form.keepExtensions = true;
    form.uploadDir = "./upload";
    form.multiples = true;
    form.maxFieldsSize = 50 * 1024 * 1024;
    form.parse(req, function(err, fields, files) {
        for (var k in files) {
            fs.rename(files[k].path, "./upload/" + files[k].name,()=>{});
        }
        res.json({
            msg: 'success'
        })
    })
});

querystring

字符串、对象处理。

//querystring.parse
name=lisa&age=20&sex= => { name: 'lisa', age: '20', sex: '女' }
//querystring.stringify
{ name: 'lisa', age: '20', sex: '女' } => name=lisa&age=20&sex=
//querystring.escape
name=大卫 => name%3D%E5%A4%A7%E5%8D%AB
//querystring.unescape
name%3D%E5%A4%A7%E5%8D%AB => name=大卫

morgan

请求日志。

// 请求日志
app.use(logger('dev'));

compressing

文件压缩与解压缩。

// 解压缩 参数:压缩包路径,输出路径
compressing.zip.uncompress(newName, './files/' + filesName + '/').then(() => {
    console.log('解压缩成功');
}).catch(err => {
    console.error(err);
});
// 压缩 参数:文件路径,压缩包输出路径
compressing.zip.compressDir('demo.js', 'demo/demo.zip').then(() => {
    console.log('success');
}).catch(err => {
    console.error(err);
});

mysql

数据库操作。

var connection = mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'root',
    port: '3306',
    database: 'book',
    multipleStatements: true //是否支持多行语句执行
});
connection.connect(function(err) {
    if (err) {
        console.log('数据库连接失败!');
        return;
    }
    console.log('数据库连接成功!');
});
var sql = 'SELECT COUNT(*) FROM book; SELECT * FROM book limit 1,10';
connection.query(sql, function(err, result) {
    if (err) {
        console.log('数据查询发生错误!', err);
        return;
    } else {
        ...
    }
});

apidoc

自动生成API文档。

// 安装:npm install apidoc -g
// 生成:apidoc -i ./ -o apidoc/
// -i 后面是注释文件地址(可改动),-o 后面是生成文件地址(默认不改动)
/**
@api {post} http://www.binylu.cn:3005/login
@apiVersion 1.0.0
@apiGroup 登陆接口
@apiDescription 通过POST请求传递账号密码获取返回参数。
@apiParam {String} username 账号
@apiParam {String} password 密码
@apiSuccess {String} code 提示代码
@apiSuccess {String} msg 提示信息
@apiSuccessExample 请求成功例子
  {
    code: "200",
    msg: "登陆成功"
  }
@apiErrorExample  错误信息1
  {
    code: "-1",
    msg: "账号错误,没有此用户"
  }
@apiErrorExample  错误信息2
  {
    code: "-2",
    msg: "密码错误"
  }
**/

cheerio

类似JQ操作DOM。

var $ = cheerio.load(data.text);

superagent

请求获取页面数据信息。

superagent.get(url).end((err, res) => {
    if (err) {
        console.log('抓取失败 ', err)
    } else {
        var $ = cheerio.load(res.text);
        ...
    }
})

chalk

控制台打印字体颜色。

console.log(chalk.green('成功'));

request

请求获取页面数据信息。

request({
    method: 'POST',
    url: url,
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
}, (err, red, body) => {
    let $ = cheerio.load(body);
    ...
})

nightmare

自动化测试包,处理动态页面。

const Nightmare = require('nightmare'); // 自动化测试包,处理动态页面
const nightmare = Nightmare({
    show: true
}); // show:true  显示内置模拟浏览器
nightmare
    .goto('http://news.baidu.com/')
    .wait("div#local_news")
    .evaluate(() => document.querySelector("div#local_news").innerHTML)
    .then(htmlStr => {
        // 获取本地新闻数据
        localNews = getLocalNews(htmlStr)
    })
    .catch(error => {
        console.log(`本地新闻抓取失败 - ${error}`);
    })

pkg

打包生成软件。

//首先安装pkg
npm install -g pkg
//然后在项目目录下执行
pkg -t win package.json
//注意这里使用path.join(__dirname, 'dist')而不是'dist',虽然在命令行中执行起来效果是一样的,不过pkg打包会无法识别到dist目录
app.use(express.static(path.join(__dirname, 'dist')));
{
    //其他配置项
    "bin": "service.js",//指定入口文件
    "pkg": {
        "assets": [
            "dist/**/*"//指定要打包的静态文件目录
        ]
    }
}

nrm

用来换npm源的 nrm ls,能看每个源的延迟 nrm test ,然后nrm use XXX。

npm i nrm -g