nodejs总结

问题1 :解决node_modules目录太长无法删除的问题
解决:
使用npm里面一个专门用于删除的模块插件 :rimraf
操作:
npm install -g rimraf
cd xxx
rimraf node_modules

rimraf
A deep deletion module for node (like rm -rf)
Build Status Dependency Status devDependency Status

The UNIX command rm -rf for node.

Install with npm install rimraf, or just drop rimraf.js somewhere.

API
rimraf(f, [opts], callback)

The first parameter will be interpreted as a globbing pattern for files. If you want to disable globbing you can do so with opts.disableGlob (defaults to false). This might be handy, for instance, if you have filenames that contain globbing wildcard characters.

The callback will be called with an error if there is one. Certain errors are handled for you:

Windows: EBUSY and ENOTEMPTY - rimraf will back off a maximum of opts.maxBusyTries times before giving up, adding 100ms of wait between each attempt. The default maxBusyTries is 3.
ENOENT - If the file doesn’t exist, rimraf will return successfully, since your desired outcome is already the case.
EMFILE - Since readdir requires opening a file descriptor, it’s possible to hit EMFILE if too many file descriptors are in use. In the sync case, there’s nothing to be done for this. But in the async case, rimraf will gradually back off with timeouts up to opts.emfileWait ms, which defaults to 1000.
options
unlink, chmod, stat, lstat, rmdir, readdir, unlinkSync, chmodSync, statSync, lstatSync, rmdirSync, readdirSync

In order to use a custom file system library, you can override specific fs functions on the options object.

If any of these functions are present on the options object, then the supplied function will be used instead of the default fs method.

Sync methods are only relevant for rimraf.sync(), of course.

For example:

var myCustomFS = require(‘some-custom-fs’)

rimraf(‘some-thing’, myCustomFS, callback)
maxBusyTries

If an EBUSY, ENOTEMPTY, or EPERM error code is encountered on Windows systems, then rimraf will retry with a linear backoff wait of 100ms longer on each try. The default maxBusyTries is 3.

Only relevant for async usage.

emfileWait

If an EMFILE error is encountered, then rimraf will retry repeatedly with a linear backoff of 1ms longer on each try, until the timeout counter hits this max. The default limit is 1000.

If you repeatedly encounter EMFILE errors, then consider using graceful-fs in your program.

Only relevant for async usage.

glob

Set to false to disable glob pattern matching.

Set to an object to pass options to the glob module. The default glob options are { nosort: true, silent: true }.

Glob version 6 is used in this module.

Relevant for both sync and async usage.

disableGlob

Set to any non-falsey value to disable globbing entirely. (Equivalent to setting glob: false.)

rimraf.sync
It can remove stuff synchronously, too. But that’s not so good. Use the async API. It’s better.

CLI
If installed with npm install rimraf -g it can be used as a global command rimraf [ …] which is useful for cross platform support.

mkdirp
If you need to create a directory recursively, check out mkdirp.

问题2: nodejs mkdirp
能够生成创建文件夹中间所有层级

demo

var mkdirp = require(“mkdirp”);
mkdirp(‘/a/b/c/d’, function (err) {
if (err) console.error(err)
else console.log(‘pow!’)
});

问题3:node.js中的fs.rmdir方法使用说明
方法说明:
以异步的方式删除文件目录。
语法:
复制代码 代码如下:

fs.rmdir(path, [callback(err)])
由于该方法属于fs模块,使用前需要引入fs模块(var fs= require(“fs”) )
接收参数:
path 目录路径
callback 回调,回调函数传递一个err异常参数。
例子:
复制代码 代码如下:

var fs = require(‘fs’);
fs.rmdir(‘deldir’, function(err){
if(err){
console.log(err);
}else{
console.log(“done”);
}
});
源码:
复制代码 代码如下:

fs.rmdir = function(path, callback) {
callback = makeCallback(callback);
if (!nullCheck(path, callback)) return;
binding.rmdir(pathModule._makeLong(path), callback);
};

问题4:nodejs-ueditor(富文本编辑器)的实现
1.介绍
UEditor是由百度web前端研发部开发所见即所得富文本web编辑器,
具有轻量,可定制,注重用户体验等特点,开源基于MIT协议,允许自由使用和修改代码…

2.下载
下载地址 :http://ueditor.baidu.com/website/download.html#ueditor.
选择开发版 ,因为我们使用node ,所以随便下一个版本,这里下载1.4.3.3 jsp 版本
下载完成解压。
3.webstorm下创建nodejs项目
4.修改index.jade界面
doctype html
head
meta(http-equiv=”Content-Type” content=”text/html;charset=utf-8”)
script(type=”text/javascript” charset=”utf-8” src=”../ueditor/ueditor.config.js”)
script(type=”text/javascript” charset=”utf-8” src=”../ueditor/ueditor.all.min.js”)
script(type=”text/javascript” charset=”utf-8” src=”../ueditor/lang/zh-cn/zh-cn.js”)
body
script(id=”editor” type=”text/plain” style=”width:1024px;height:500px;”)

script(type="text/javascript").var ue = UE.getEditor('editor')

然后启动项目,编辑器初始化成功!(注意引入文件路径的问题,如果初始化失败可以f12查看报错进行调试)
4.后端配置
仅仅这样是不能进行上传的
我们需要要配置后端文件。
修改 ueditor.config.js 文件

//找到这一行代码 修改成这样,这里的url为请求的路径
// 服务器统一请求接口路径
,serverUrl: URL + “ue”

接下来我们安装ueditor 模块
UEditor API http://ueditor.baidu.com/doc/
$npm install ueditor –save

修改app.js:
//加载ueditor 模块
var ueditor = require(“ueditor”);

//使用模块  
app.use("/ueditor/ue", ueditor(path.join(__dirname, 'public'), function (req, res, next) {  
    // ueditor 客户发起上传图片请求  
    if (req.query.action === 'uploadimage') {  
        var foo = req.ueditor;  

        var imgname = req.ueditor.filename;  

        var img_url = '/images/ueditor/';  
        res.ue_up(img_url); //你只要输入要保存的地址 。保存操作交给ueditor来做  
        res.setHeader('Content-Type', 'text/html');//IE8下载需要设置返回头尾text/html 不然json返回文件会被直接下载打开  
    }  
    //  客户端发起图片列表请求  
    else if (req.query.action === 'listimage') {  
        var dir_url = '/images/ueditor/';  
        res.ue_list(dir_url); // 客户端会列出 dir_url 目录下的所有图片  
    }  
    // 客户端发起其它请求  
    else {  
        // console.log('config.json')  
        res.setHeader('Content-Type', 'application/json');  
        res.redirect('/ueditor/jsp/config.json');  
    }  
}));        

修改完成重启一下服务即可



问题:node.js模块之socket IO
解决:
Socket.IO的为了在浏览器和移动设备上创建实时应用而产生的,它可以模糊不同传输机制之间的差异。
Socket.IO是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用。它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5,应该可以满足绝大部分需求了。

  1. 安装部署
    2.1 安装
    首先安装非常简单,在node.js环境下只要一句:
    npm install socket.io
    2.2 结合express来构建服务器
    express是一个小巧的Node.js的Web应用框架,在构建HTTP服务器时经常使用到,所以直接以Socket.IO和express为例子来讲解。
    var express = require(‘express’)
    , app = express()
    , server = require(‘http’).createServer(app)
    , io = require(‘socket.io’).listen(server);
    server.listen(3001);
    若不使用express,请参考socket.io/#how-to-use
  2. 基本使用方法
    主要分为服务器端和客户端两段代码,都非常简单。
    Server(app.js):
    //接上面的代码
    app.get(‘/‘, function (req, res) {
    res.sendfile(__dirname + ‘/index.html’);});
    io.sockets.on(‘connection’, function (socket) {
    socket.emit(‘news’, { hello: ‘world’ });
    socket.on(‘other event’, function (data) {

    console.log(data);
    

    });
    });
    首先io.sockets.on函数接受字符串”connection”作为客户端发起连接的事件,当连接成功后,调用带有socket参数的回调函数。我们在使用socket.IO的时候,基本上都在这个回调函数里面处理用户的请求。
    socket最关键的是emit和on两个函数,前者提交(发出)一个事件(事件名称用字符串表示),事件名称可以自定义,也有一些默认的事件名称,紧接着是一个对象,表示向该socket发送的内容;后者接收一个事件(事件名称用字符串表示),紧接着是收到事件调用的回调函数,其中data是收到的数据。
    在上面的例子中,我们发送了news事件和收到了other event事件,那么客户端应该会有对应的接收和发送事件。没错,客户端代码和服务器正好相反,而且非常相似。
    Client(client.js)


    有两点要注意的:socket.io.js路径要写对,这个js文件实际放在了服务器端的node_modules文件夹中,在请求这个文件时会重定向,因此不要诧异服务器端不存在这个文件但为什么还能正常工作。当然,你可以把服务器端的socket.io.js这个文件拷贝到本地,使它成为客户端的js文件,这样就不用每次都向Node服务器请求这个js文件,增强稳定性。第二点是要用var socket = io.connect(‘网站地址或ip’);来获取socket对象,接着就可以使用socket来收发事件。关于事件处理,上面的代码表示收到“news”事件后,打印收到的数据,并向服务器发送“other event”事件。
    注:内置默认的事件名例如“disconnect”表示客户端连接断开,“message”表示收到消息等等。自定义的事件名称,尽量不要跟Socket.IO中内置的默认事件名重名,以免造成不必要的麻烦。

  3. 其他常用API
    1).向所有客户端广播:socket.broadcast.emit(‘broadcast message’);
    2).进入一个房间(非常好用!相当于一个命名空间,可以对一个特定的房间广播而不影响在其他房间或不在房间的客户端):socket.join(‘your room name’);
    3).向一个房间广播消息(发送者收不到消息):socket.broadcast.to(‘your room name’).emit(‘broadcast room message’);
    4).向一个房间广播消息(包括发送者都能收到消息)(这个API属于io.sockets):io.sockets.in(‘another room name’).emit(‘broadcast room message’);
    5).强制使用WebSocket通信:(客户端)socket.send(‘hi’),(服务器)用socket.on(‘message’, function(data){})来接收。

  4. 使用Socket.IO构建一个聊天室
    最后,我们通过一个简单的实例来结束本篇。用Socket.IO构建一个聊天室就是50行左右的代码的事情,实时聊天效果也非常好。以下贴出关键代码:
    Server(socketChat.js)
    //一个客户端连接的字典,当一个客户端连接到服务器时,
    //会产生一个唯一的socketId,该字典保存socketId到用户信息(昵称等)的映射
    var connectionList = {};
    exports.startChat = function (io) {
    io.sockets.on(‘connection’, function (socket) {
    //客户端连接时,保存socketId和用户名
    var socketId = socket.id;
    connectionList[socketId] = {
        socket: socket
    };
    //用户进入聊天室事件,向其他在线用户广播其用户名
    socket.on('join', function (data) {
        socket.broadcast.emit('broadcast_join', data);
        connectionList[socketId].username = data.username;
    });
    //用户离开聊天室事件,向其他在线用户广播其离开
    socket.on('disconnect', function () {
        if (connectionList[socketId].username) {
            socket.broadcast.emit('broadcast_quit', {
                username: connectionList[socketId].username
            });
        }
        delete connectionList[socketId];
    });
    //用户发言事件,向其他在线用户广播其发言内容
    socket.on('say', function (data) {
        socket.broadcast.emit('broadcast_say',{
            username: connectionList[socketId].username,
            text: data.text
        });
    });
    
    })
    };
    Client(socketChatClient.js)
    var socket = io.connect(‘http://localhost‘);
    //连接服务器完毕后,马上提交一个“加入”事件,把自己的用户名告诉别人
    socket.emit(‘join’, {
    username: ‘Username hehe’
    });
    //收到加入聊天室广播后,显示消息
    socket.on(‘broadcast_join’, function (data) {
    console.log(data.username + ‘加入了聊天室’);
    });
    //收到离开聊天室广播后,显示消息
    socket.on(‘broadcast_quit’, function(data) {
    console.log(data.username + ‘离开了聊天室’);
    });
    //收到别人发送的消息后,显示消息
    socket.on(‘broadcast_say’, function(data) {
    console.log(data.username + ‘说: ‘ + data.text);
    });
    //这里我们假设有一个文本框textarea和一个发送按钮.btn-send
    //使用jQuery绑定事件
    $(‘.btn-send’).click(function(e) {
    //获取文本框的文本
    var text = $(‘textarea’).val();
    //提交一个say事件,服务器收到就会广播
    socket.emit(‘say’, {
    username: 'Username hehe'
    text: text
    
    });
    });
    这就是一个简单的聊天室DEMO,你可以根据你的需要随意扩展。Socket.IO基本上就是各种事件的提交和接收处理,思想非常简单。

问题:npm发布自己的代码
解决:
1.验证npm
新建一个文件夹用来存包,进入该文件夹(windows的同学可以按住shift键+右键,选择’在此处打开命令窗口‘来快速调出控制台)。
首先需要验证一下你是否安装了npm,如果确认已装请跳过此步,在控制台输入命令:
npm -v
2.新建package.json
在项目根目录下新建一个package.json,这是一个用来描述你的包的json文件,例如包名,依赖的其他包,作者等等。
借助npm来初始化可快速新建,在根目录下输入命令:
npm init
接着npm会提示你输入一些关于你的包的信息,如果你暂时不知道怎么填那一项就直接按回车就好:
你会发现你的根目录下多了一个package.json文件。
当然’name’要注意填,它将是你发布出去的npm包名,如果不小心填错了,可以直接在新建好的package.json修改’name’字段。
3.编写代码
虽然npm是允许发布一个空包(只含有package.json的包),但是这样的一个包是没有意义的。
我们如果没有代码,可以先加一个README.md(给使用者看的说明书)来说明一下自己的包:
在根目录下新建一个README.md文件,输入如下内容:

这是我的一个npm包

未完待续…
如果你想好了写说明代码(js,html,css等)请试着放进来。

4.创建npm账号
如果你没有创建过npm账号,可输入如下命令添加一个npm账号,并跟着提示填写要注册的账号和密码:
npm adduser

5.发布!
现在可以发布了。还是在根目录下输入命令:
npm publish
这样你就可以在https://www.npmjs.com/搜索并找到你刚才发布的npm包名(例如我的是:article-npm-publish,如果需要登录,请使用刚才你在控制台注册的账号):
现在,你可以试着使用npm install xxx(xxx为你的包名)来安装自己的包啦。

常见问题
1.如果需要更新包,在修改完代码后请记得修改package.json包的’version’字段,然后 npm publish。否则会无法发布;
2.如果在发布中显示类似’请确认你是否有权限更新xxx包’的英文提示,这就说明你的包名有人使用了。换个名字就好啦。
3.如果你想删除一个自己发布过的包,请使用命令 npm unpublish –force xxx (xxx为包名),一些没有意义的包还是建议删掉。

问题:Node.js监控工具:nodemon
解决:
Nodemon 是一款非常实用的工具,用来监控你 node.js 源代码的任何变化和自动重启你的服务器。 Nodemon 是一款完美的开发工具,可以使用 npm 安装。
npm install -g nodemon
在doc下输入上面的,就安装成功了,以后我们启动我们的项目的时候,就输入nodemon server.js

以前是node server.js
这样,每次我们的js文件修改后,node就会自动重启,就省的我们手动去重启了,这样方便了很多。
还有一个工具也可以实现同样的需求——supervisor

例如测试结果如下:


P.S. 这里有一个坑:我一直使用下面的方法启动node: node
这样启动是没有问题的,因为环境变量中配置了node了嘛,但是如果nodemon d:/js/node/app.js
这样nodemon监视的是启动目录,而不是d:/js/node这个目录,比如 c:\program>nodemon d:/js/node/app.js
他实际上监视的是c:\program这个目录,你需要修改启动目录里才可以。
[热启动需要全局安装nodemon 另外nodeJS最好安装稳定版本]

问题:
解决:
背景
一个网站必然会涉及很多功能,tab选项卡、slide轮播图、pop弹出层、美化alert、paging分页等等等等,如果是企业网站那整合成一个js文件就够了,即 插件库 。

但是对于一个大的网站平台来说需要的功能可能会有很多,而且随着平台的发展,功能会越来越多。到最后会发展成怎样的情况呢?
我们希望一个页面按需引入,这个页面需要什么功能就引入什么功能。现在主流的工具有两种,amd规范的RequireJS、cmd规范的Seajs。
什么是Seajs
Seajs是一个加载器 http://kb.cnblogs.com/page/211942/
遵循 CMD 规范模块化开发,依赖的自动加载、配置的简洁清晰。
兼容性

Chrome 3+
Firefox 2+
Safari 3.2+
Opera 10+
IE 5.5+
基本应用
导入Seajs库
去官网下载最新的seajs文件, http://seajs.org/docs/#downloads
在页尾引入seajs:


然后在它下面写模块的配置和入口。

// seajs 的简单配置
seajs.config({
base: “../sea-modules/“,
alias: {
“jquery”: “jquery/jquery/1.10.1/jquery.js”
}
});

// 加载入口模块
seajs.use(“../static/hello/src/main”);
配置和入口
这里解释下配置和入口的意思。

配置

通常在配置上修改seajs的路径和别名。

seajs的 路径是相对于前面引入的seajs文件的 。假如是这样的目录结构:
examples/
|– index.html
|
--about | |-- news.html |– script
|– seajs.js
|– jquery.js
`– main.js

我们平时如果我们在index.html上引用main.js路径应该是这样写的 script/main.js ,从news.html引用main.js就要这样写, ../script/main.js 。

而在seajs是相对于seajs文件的,一律直接使用 main.js 就OK了,是不是很方便呢?

既然这么方便那在什么情况需要配置呢?一般情况是用不到的。但是假如你的路径特别深 或者要做路径映射的时候它的作用就来了。下面介绍下常用的几个配置。

seajs.config({
// Sea.js 的基础路径(修改这个就不是路径就不是相对于seajs文件了)
base: ‘http://example.com/path/to/base/‘,
// 别名配置(用变量表示文件,解决路径层级过深和实现路径映射)
alias: {
‘es5-safe’: ‘gallery/es5-safe/0.9.3/es5-safe’,
‘json’: ‘gallery/json/1.0.2/json’,
‘jquery’: ‘jquery/jquery/1.10.1/jquery’
},
// 路径配置(用变量表示路径,解决路径层级过深的问题)
paths: {
‘gallery’: ‘https://a.alipayobjects.com/gallery
}
});

入口

入口即加载,需要加载什么文件(模块加载器)就在这里引入。 sea.js 在下载完成后,会自动加载入口模块。

seajs.use(“abc/main”); //导入seajs.js同级的abc文件夹下的main.js模块的(后缀名可略去不写)
seajs.use()还有另外一种用法。

有时候我们写一个简单的单页并不想为它单独写一个js文件,选择在直接把js代码写在页面上,seajs通过 seajs.use() 实现了这个。 接收两个参数第一个是文件依赖(单个用字符串数组都可以,多个需用数组表示),第二个是回调函数。

加载单个依赖

//加载模块 main,并在加载完成时,执行指定回调
seajs.use(‘./main’, function(main) {
main.init();
});
加载多个依赖

//并发加载模块 a 和模块 b,并在都加载完成时,执行指定回调
seajs.use([‘./a’, ‘./b’], function(a, b) {
a.init();
b.init();
});
这里回掉函数中的a和b参数是与前面的模块暴露出来的接口一一对应的。有时候也许只需要使用b的接口,但是也要把a参数写上。什么是暴露接口下面会解释。

模块开发
这里才是重点,其实也很简单就是一个书写规范(CMD)而已。

// 所有模块都通过 define 来定义
define(function(require, exports, module) {

// 通过 require 引入依赖
var $ = require(‘jquery’);
var Spinning = require(‘./spinning’);

// 通过 exports 对外提供接口
exports.doSomething = …

// 或者通过 module.exports 提供整个接口
module.exports = …

});
模块是通过define()方法包装的,然后内部痛过require()方法引入需要的依赖文件(模块)。(也可以引入.css文件哦~)

模块最好是面向对象开发的,这样最后可以方便的通过 exports.doSomething 或 module.exports 把模块的接口给暴露出来。如果你是写的是jq插件的话就不需要这个功能了,因为你的接口是写在jquery的对象里的。如果你不需要提供接口的话也可以不使用这两个属性哦!

事实上define方法还有另外几个参数,一般情况我们用不到。具体看 官方API 。

小结
其实Seajs的基本使用就这么简单,日常使用足够了,之前看官网的 5分钟教程 楞是没看懂,后来想想真的是5分钟学会啊,悟性太低- -||

注意事项
模块内的函数依赖必须交代清楚,防止模块在函数依赖加载前先加载出来。而且还增强了模块的独立性。
引入seajs的时候最好给


● 接下来修改aaa.js文件:
第一处:因为templates.js中的module为template;所以aaa.js中的module就要依赖template
[图片]第二处: 修改路由。
[图片]按照如上的这个方法;找到templates.js中查看a,b分别代表什么;将aaa.js中的路由都替换成a。
[图片][图片][图片]

问题:Handlebars.js 模板引擎
解决:
Handlebars.js同jade和ejs一样都是模板引擎。
Handlebars 是 JavaScript 一个语义模板库,通过对view和data的分离来快速构建Web模板。它采用”Logic-less template”(无逻辑模版)的思路,在加载时被预编译,而不是到了客户端执行到代码时再去编译, 这样可以保证模板加载和运行的速度。Handlebars兼容Mustache,你可以在Handlebars中导入Mustache模板。

问题:Node批量处理文件名
解决:
node使用fs模块进行批量修改文件名
注意:需要node环境

使用:1:代码目录新建src文件夹
2:需要处理的文件存入src文件夹
3:当前目录运行node rename -n 文件名 -i 文件名增量

var fs = require('fs'),
    src = 'src',
    dist = 'dist',
    stat = fs.stat;

var args = process.argv.slice(2),name,index=0;

//show help
if (args.length === 0 || args[0].match('help')) {
    console.log('--help\n\t-n  file name 文件名\n\t-i  file name index 文件索引\n');
}

args.forEach(function (item, _index) {
    if (item.match('-n')) {
        name = args[_index + 1];
    } else if (item.match('-i')) {
        index = args[_index + 1];
    }
});

//read file directors
fs.readdir(src, function (err, files) {
    if (err) {
        console.log(err);
    } else {
        fs.exists(dist, function (exist) {
            if (exist) {
                copy(files);
            } else {
                fs.mkdir(dist, function () {
                    copy(files);
                })
            }
        });
    }

    function copy(_files) {
        //foreach files
        _files.forEach(function (filename) {
            var readStream, writeStream;
            var arr = filename.split('.');
            var oldPath = src + '/' + filename,
                newPath = dist + '/' + name + index + '.' + arr[arr.length - 1];
            stat(oldPath, function (err, file) {
                if (err) {
                    console.log(err);
                } else if (file.isFile()) {
                    //create read stream
                    readStream = fs.createReadStream(oldPath);
                    //create write stream
                    writeStream = fs.createWriteStream(newPath);
                    //pipe copy
                    readStream.pipe(writeStream);
                }
            });
            index++;
        })
    }
});           

效果:




问题:Grunt讲解
解决:
Grunt和Grunt的插件都是通过Node.js的包管理器npm来安装和管理的。

Grunt 0.4.x要求Node.js的版本>=0.8.0(也就是0.8.0及以上版本的Node.js才能很好的运行Grunt)。

安装Grunt之前,可以在命令行中运行node -v查看你的Node.js版本。
安装CLI
如果你是从Grunt 0.3升级而来的,请查看Grunt 0.3的说明。(在这篇文档的底部)

为了方便使用Grunt,你应该在全局范围内安装Grunt的命令行接口(CLI)。要做到这一点,你可能需要使用sudo(OS X,*nix,BSD等平台中)权限或者作为超级管理员(Windows平台)来运行shell命令。

npm install -g grunt-cli
这条命令将会把grunt命令植入到你的系统路径中,这样就允许你从任意目录来运行它(定位到任意目录运行grunt命令)。

注意,安装grunt-cli并不等于安装了grunt任务运行器!Grunt CLI的工作很简单:在Gruntfile所在目录调用运行已经安装好的相应版本的Grunt。这就意味着可以在同一台机器上同时安装多个版本的Grunt。

CLI如何工作
每次运行grunt时,它都会使用node的require()系统查找本地已安装好的grunt。正因为如此,你可以从你项目的任意子目录运行grunt。

如果找到本地已经安装好的Grunt,CLI就会加载这个本地安装好的Grunt库,然后应用你项目中的Gruntfile中的配置(这个文件用于配置项目中使用的任务,Grunt也正是根据这个文件中的配置来处理相应的任务),并执行你所指定的所有任务。

想要真正的了解这里发生了什么,可以阅读源码。这份代码很短。

用一个现有的Grunt项目进行工作
假设已经安装好Grunt CLI并且项目也已经使用一个package.json和一个Gruntfile文件配置好了,那么接下来用Grunt进行工作就非常容易了:

进入到项目的根目录(在命令行面板定位到项目根目录。在windows系统下,也可以进入项目根目录的文件夹后,按Shift+鼠标右键,打开右键菜单,选择“在此处打开命令窗口(W)”)。
运行npm install安装项目相关依赖(插件,Grunt内置任务等依赖)。
使用grunt(命令)运行Grunt。
就是这么简单。已经安装的Grunt任务可以通过运行grunt –help列出来,但是通常最好还是先查看一下项目的文档。

准备一个新的Grunt项目
一个典型的配置过程通常只涉及到两个文件:package.json和Gruntfile。

package.json:这个文件被用来存储已经作为npm模块发布的项目元数据(也就是依赖模块)。你将在这个文件中列出你的项目所依赖的Grunt(通常我们在这里配置Grunt版本)和Grunt插件(相应版本的插件)。

Gruntfile:通常这个文件被命名为Gruntfile.js或者Gruntfile.coffee,它用于配置或者定义Grunt任务和加载Grunt插件。

package.json

package.json与Gruntfile相邻,它们都应该归属于项目的根目录中,并且应该与项目的源代码一起被提交。在上述目录(package.json所在目录)中运行npm install将依据package.json文件中所列出的每个依赖来自动安装适当版本的依赖。

这里有一些为项目创建package.json文件的方式:

大多数的grunt-init模板都会自动创建一个项目特定的package.json文件。

npm init命令会自动创建一个基本的package.json文件。

从下面的例子开始并根据规范来按需扩展。

{
“name”: “my-project-name”, // 项目名称
“version”: “0.1.0”, // 项目版本
“devDependencies”: { // 项目依赖
“grunt”: “~0.4.1”, // Grunt库
“grunt-contrib-jshint”: “~0.6.0”, //以下三个是Grunt内置任务
“grunt-contrib-nodeunit”: “~0.2.0”,
“grunt-contrib-uglify”: “~0.2.2”
}
}
原文中注释仅作说明,使用时请自行检查编辑。其他地方如有雷同,参考这条提示。
安装Grunt和grunt插件

添加Grunt和Grunt插件到一个现有的package.json中最简单的方式就是使用npm install –save-dev命令。这不仅会在本地安装,它还会使用一个波浪形字符的版本范围自动将所安装的添加到项目依赖中。

例如使用下面的命令将会安装最新版的Grunt到你的项目中,并自动将它添加到你的项目依赖中:

npm install grunt –save-dev
上述命令也可以用于Grunt插件和其他的node模块的安装。当完成操作后请确保更新后的package.json文件也要与你的项目一起提交。

Gruntfile

Gruntfile.js或者Gruntfile.coffee文件都是归属于你项目根目录中的一个有效的JavaScript或者CoffeeScript文件(和package.json文件一样都在根目录中),并且它(Gruntfile)也应该与你的项目源文件一起提交。

一个Gruntfile由下面几部分组成:

“wrapper”函数(包装函数)
项目和任务配置
加载的Grunt插件和任务
自定义任务
一个Gruntfile示例

在下面的Gruntfile中,项目的元数据会从项目的package.json文件中导入到grunt配置中,同时grunt-contrib-uglify插件的uglify任务被配置用于压缩一个源文件,同时使用该元数据(导入的元数据)动态的生成一个标语(banner)注释。在命令行运行grunt时默认会运行uglify任务。

module.exports = function(grunt){

// 项目配置
grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    uglify: {
        options: {
            banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
        },
        build: {
            src: 'src/<%=pkg.name %>.js',
            dest: 'build/<%= pkg.name %>.min.js'
        }               
    }
});

// 加载提供"uglify"任务的插件
grunt.loadNpmTasks('grunt-contrib-uglify');

// 默认任务
grunt.registerTask('default', ['uglify']);

}
现在你已经看到到了一个完整的Gruntfile,下面让我们来看看它的各个组成部分:

”wrapper”函数

每个Gruntfile(和Grunt插件)都使用这个基本格式,并且所有你的Grunt代码都必须指定在这个函数里面:

module.exports = function(grunt) {
// 在这里处理Grunt相关的事情
}
项目和任务配置

大多数Grunt任务所依赖的配置数据都被定义在传递给grunt.initConfig方法的一个对象中。

在这个例子中,grunt.file.readJSON(‘package.json’)会把存储在package.json中的JSON元数据导入到Grunt配置中。由于<% %>模板字符串可以引用任意的配置属性,因此可以通过这种方式来指定诸如文件路径和文件列表类型的配置数据,从而减少一些重复的工作(比如我们通常需要通过复制粘贴的方式来在不同的地方引用同一属性, 使用<% %>的方式可以简单的理解为将某些特定的数据存储在变量中,然后在其他地方像使用变量一样就可以使用这些数据属性)。

你可以在这个配置对象中(传递给initConfig()方法的对象)存储任意的数据,只要它不与你任务配置所需的属性冲突,否则会被忽略。此外,由于这本身就是JavaScript,你不仅限于使用JSON;你可以在这里使用任意的有效的JS代码。如果有必要,你甚至可以以编程的方式生成配置。

与大多数任务一样,grunt-contrib-uglify插件的uglify任务要求它的配置被指定在一个同名属性中。在这里有一个例子, 我们指定了一个banner选项(用于在文件顶部生成一个注释),紧接着是一个单一的名为build的uglify目标,用于将一个js文件压缩为一个目标文件(比如将src目录jquery-1.9.0.js压缩为jquery-1.9.0.min.js然后存储到dest目录)。

// 项目配置
grunt.initConfig({
pkg: grunt.file.readJSON(‘package.json’),
uglify: {
options: {
banner: ‘/! <%= pkg.name %> <%= grunt.template.today(“yyyy-mm-dd”) %> /\n’
},
build: {
src: ‘src/<%=pkg.name %>.js’,
dest: ‘build/<%= pkg.name %>.min.js’
}
}
});
加载grunt插件和任务

许多常用的任务像concatenation,minification和linting都被作为grunt插件来使用。只要一个插件被作为一个依赖指定在项目的package.json文件中,并且已经通过npm install安装好,都可以在你的Gruntfile文件中使用下面这个简单的命令启用它(所依赖的任务)。

// 加载提供”uglify”任务的插件
grunt.loadNpmTasks(‘grunt-contrib-uglify’);
注意: grunt –help命令可以列出所有可用的任务。

自定义任务

你可以通过定义一个default任务来配置Grunt,让它默认运行一个或者多个任务。在下面的例子中,在命令行中运行grunt而不指定特定的任务将自动运行uglify任务。这个功能与显示的运行grunt uglify或者等价的grunt default一样。你可以在任务参数数组中指定任意数量的任务(这些任务可以带参数,也可以不带参数)。

// 默认任务
grunt.registerTask(‘default’, [‘uglify’]);
如果你的项目所需的任务没有对应的Grunt插件提供相应的功能,你可以在Gruntfile内定义自定义的任务。例如,下面的Gruntfile就定义了一个完整的自定义的default任务,它甚至没有利用任务配置(没有使用grunt.initConfig()方法):

module.exports = function(grunt) {
// 一个非常基础的default任务
grunt.registerTask(‘default’, ‘Log some stuff.’, function() {
grunt.log.write(‘Logging some stuff…’).ok();
});
};
自定义的项目特定的任务可以不定义在Gruntfile中;它们可以定义在一个外部.js文件中,然后通过grunt.loadTasks方法来加载。

扩展阅读
安装Grunt指南中有关于安装特定版本的,发布的或者开发中版本的Grunt和Grunt-cli的详细信息。

配置任务指南中有对于如何在Gruntfile中配置任务,目标,选项和文件的详细解释,还有模板,匹配模式和导入外部数据相关的说明。

创建任务指南列出了Grunt任务类型之间的不同,还展示了许多实例任务和配置。

对于关于编写自定义任务或者Grunt插件的更多信息,请参考开发者文档。

Grunt 0.3说明
如果你从Grunt 0.3升级而来的,请确保先卸载全局的grunt(使用下面的命令):

npm uninstall -g grunt
上面这些说明文档是针对Grunt 0.4.x编写的,但仍然适用于Grunt 0.3.x。只是注意0.3.x版本中的插件名称和任务配置选项可能与上面的”Gruntfile”中所展示的不同。

对于0.3.x版本的Grunt, Grunfile名为grunt.js。

问题:

  • 解决:
    目前主流的node版本管理工具有两种,nvm和n。
    管理 node 版本,选择 nvm 还是 n?总的来说,nvm有点类似于 Python 的 virtualenv 或者 Ruby 的 rvm,每个node版本的模块都会被安装在各自版本的沙箱里面(因此切换版本后模块需重新安装),因此考虑到需要时常对node版本进行切换测试兼容性和一些模块对node版本的限制,我选择了使用nvm作为管理工具,下面就来说说nvm的安装和使用过程。

问题:node之assert
node.js 组件 – assert

概念
assert模块是Node的内置模块,主要用于断言。如果表达式不符合预期,就抛出一个错误。该模块提供11个方法,但只有少数几个是常用的。

  1. assert()
    assert方法接受两个参数,当第一个参数对应的布尔值为true时,不会有任何提示,返回undefined。当第一个参数对应的布尔值为false时,会抛出一个错误,该错误的提示信息就是第二个参数设定的字符串。

    // 格式
    assert(value, message)

    // 例子
    var assert = require(‘assert’);

    function add (a, b) {
    return a + b;
    }

    var expected = add(1,2);
    assert( expected === 3, ‘预期1加2等于3’);

上面代码不会有任何输出,因为assert方法的第一个参数是true。

assert( expected === 4, '预期1加2等于3')
// AssertionError: 预期1加2等于3

上面代码会抛出一个错误,因为assert方法的第一个参数是false。

  1. assert.ok()
    ok是assert方法的另一个名字,与assert方法完全一样。

  2. assert.equal()
    equal方法接受三个参数,第一个参数是实际值,第二个是预期值,第三个是错误的提示信息。

    // 格式
    assert.equal(actual, expected, [message])

    assert.equal(true, value, message);
    // 等同于
    assert(value, message);

    // 例子
    var assert = require(‘assert’);

    function add (a, b) {
    return a + b;
    }

    var expected = add(1,2);

    // 以下三句效果相同
    assert(expected == 3, ‘预期1+2等于3’);
    assert.ok(expected == 3, ‘预期1+2等于3’);
    assert.equal(expected, 3, ‘预期1+2等于3’);

equal方法内部使用的是相等运算符(==),而不是严格运算符(===),进行比较运算。

  1. assert.notEqual()
    notEqual方法的用法与equal方法类似,但只有在实际值等于预期值时,才会抛出错误。

    // 格式
    assert.equal(actual, expected, [message])

    assert.equal(true, value, message);
    // 等同于
    assert(value, message);

    // 例子
    var assert = require(‘assert’);

    function add (a, b) {
    return a + b;
    }

    var expected = add(1,2);

    // 以下三句效果相同
    assert(expected == 3, ‘预期1+2等于3’);
    assert.ok(expected == 3, ‘预期1+2等于3’);
    assert.equal(expected, 3, ‘预期1+2等于3’);

equal方法内部使用的是相等运算符(==),而不是严格运算符(===),进行比较运算。

  1. assert. deepEqual()
    deepEqual方法用来比较两个对象。只要它们的属性一一对应,且值都相等,就认为两个对象相等,否则抛出一个错误。

    // 格式
    assert.deepEqual(actual, expected, [message])

    // 例子
    var assert = require(‘assert’);

    var list1 = [1, 2, 3, 4, 5];
    var list2 = [1, 2, 3, 4, 5];

    assert.deepEqual(list1, list2, ‘预期两个数组应该有相同的属性’);

    var person1 = { “name”:”john”, “age”:”21” };
    var person2 = { “name”:”john”, “age”:”21” };

    assert.deepEqual(person1, person2, ‘预期两个对象应该有相同的属性’);

  2. assert. notDeepEqual()
    notDeepEqual方法与deepEqual方法正好相反,用来断言两个对象是否不相等。

    // 格式
    assert.notDeepEqual(actual, expected, [message])

    // 例子
    var assert = require(‘assert’);

    var list1 = [1, 2, ,3, 4, 5];
    var list2 = [1, 2, 3, 4, 5];

    assert.notDeepEqual(list1, list2, ‘预期两个对象不相等’);

    var person1 = { “name”:”john”, “age”:”21” };
    var person2 = { “name”:”jane”, “age”:”19” };

    // deepEqual checks the elements in the objects are identical
    assert.notDeepEqual(person1, person2, ‘预期两个对象不相等’);

  3. assert. strictEqual()
    strictEqual方法使用严格相等运算符(===),比较两个表达式。

    // 格式
    assert.strictEqual(actual, expected, [message])

    // 例子
    var assert = require(‘assert’);

    assert.strictEqual(1, ‘1’, ‘预期严格相等’);
    // AssertionError: 预期严格相等

  4. assert. notStrictEqual()
    assert.notStrictEqual方法使用严格不相等运算符(!==),比较两个表达式。

    // 格式
    assert.notStrictEqual(actual, expected, [message])

    // 例子
    var assert = require(‘assert’);

    assert.notStrictEqual(1, true, ‘预期严格不相等’);

  5. assert. throws()
    throws方法预期某个代码块会抛出一个错误,且抛出的错误符合指定的条件。

    // 格式
    assert.throws(block, [error], [message])

    // 例一,抛出的错误符合某个构造函数
    assert.throws(
    function() {

    throw new Error("Wrong value");
    

    },
    Error,
    ‘不符合预期的错误类型’
    );

    // 例二、抛出错误的提示信息符合正则表达式
    assert.throws(
    function() {

    throw new Error("Wrong value");
    

    },
    /value/,
    ‘不符合预期的错误类型’
    );

    // 例三、抛出的错误符合自定义函数的校验
    assert.throws(
    function() {

    throw new Error("Wrong value");
    

    },
    function(err) {

    if ( (err instanceof Error) && /value/.test(err) ) {
      return true;
    }
    

    },
    ‘不符合预期的错误类型’
    );

  6. assert. doesNotThrow()
    doesNotThrow方法与throws方法正好相反,预期某个代码块不抛出错误。

    // 格式
    assert.doesNotThrow(block, [message])

    // 用法
    assert.doesNotThrow(
    function() {

    console.log("Nothing to see here");
    

    },
    ‘预期不抛出错误’
    );

  7. assert. ifError()
    ifError方法断言某个表达式是否false,如果该表达式对应的布尔值等于true,就抛出一个错误。它对于验证回调函数的第一个参数十分有用,如果该参数为true,就表示有错误。

    // 格式
    assert.ifError(value)

    // 用法
    function sayHello(name, callback) {
    var error = false;
    var str = “Hello “+name;
    callback(error, str);
    }

    // use the function
    sayHello(‘World’, function(err, value){
    assert.ifError(err);
    // …
    })

  8. assert. fail()
    fail方法用于抛出一个错误。

    // 格式
    assert.fail(actual, expected, message, operator)

    // 例子
    var assert = require(‘assert’);

    assert.fail(21, 42, ‘Test Failed’, ‘###’)
    // AssertionError: Test Failed
    assert.fail(21, 21, ‘Test Failed’, ‘###’)
    // AssertionError: Test Failed
    assert.fail(21, 42, undefined, ‘###’)
    // AssertionError: 21 ### 42

该方法共有四个参数,但是不管参数是什么值,它总是抛出一个错误。如果message参数对应的布尔值不为false,抛出的错误信息就是message,否则错误信息就是“实际值 + 分隔符 + 预期值”

问题:node之buffer
Buffer对象是Node处理二进制数据的一个接口。它是Node原生提供的全局对象,可以直接使用,不需要require(‘buffer’)。
JavaScript比较擅长处理字符串,对于处理二进制数据(比如TCP数据流),就不太擅长。Buffer对象就是为了解决这个问题而设计的。它是一个构造函数,生成的实例代表了V8引擎分配的一段内存,是一个类似数组的对象,成员都为0到255的整数值,即一个8位的字节。
http://www.qdfuns.com/notes/15571/0f382a1878c9716eabc486ac853a8cba.html

问题:webpack
细说 webpack 之流程篇
目前,几乎所有业务的开发构建都会用到 webpack 。的确,作为模块加载和打包神器,只需配置几个文件,加载各种 loader 就可以享受无痛流程化开发。
Webpack 是一个前端资源加载/打包工具,React 是一个Facebook 和Instagram 用来创建用户界面的JavaScript 库。两个都是非常有用的技术,如果同时使用他们,前端开发会更加有趣。
问题:node之node-schedule
node-schedule实现定时任务

问题;node之nodemailer
nodemailer发送邮件

问题:node之Events
Events模块是Node对“发布/订阅”模式(publish/subscribe)的实现。一个对象通过这个模块,向另一个对象传递消息。
http://www.qdfuns.com/notes/15571/ab98cf32de6e4258b75204b2027942f9.html

问题:node之mime
概述
先说说什么是mime类型? 然后介绍两种设置mime类型的方法,第一种是通过后缀名判断文件类型,从而进行响应;第二种是使用第三方mime模块进行响应。
MIME(Multipurpose Internet Mail Extensions)多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。(百度百科)
简单点说,mime是一个互联网标准,通过设定它就可以设定文件在浏览器的打开方式。
http://www.qdfuns.com/notes/15571/042cfeed2e04a4158d630bfcabb0cb13.html

问题:node之child Process
child_process模块用于新建子进程。子进程的运行结果储存在系统缓存之中(最大200KB),等到子进程运行结束以后,主进程再用回调函数读取子进程的运行结果
通过child_process模块可以创建子进程,从而实现多进程模式,更好地利用CPU多核计算资源。该模块提供了四种方法创建子进程,分别是child_process.spawn()、child_process.exec()、child_process.execFile(),child_process.fork(),这四个方法都返回一个childProcess对象,该对象实现了EventEmitter的接口,带有stdout,stdin,stderr的对象。
http://www.qdfuns.com/notes/15571/bcdd087c62122c9f9923a1967889a2c1.html

问题:node之cluster
. 概述
1.1 基本用法
Node.js默认单进程运行,对于32位系统最高可以使用512MB内存,对于64位最高可以使用1GB内存。对于多核CPU的计算机来说,这样做效率很低,因为只有一个核在运行,其他核都在闲置。cluster模块就是为了解决这个问题而提出的。
cluster模块允许设立一个主进程和若干个worker进程,由主进程监控和协调worker进程的运行。worker之间采用进程间通信交换消息,cluster模块内置一个负载均衡器,采用Round-robin算法协调各个worker进程之间的负载。运行时,所有新建立的链接都由主进程完成,然后主进程再把TCP连接分配给指定的worker进程。
Node.js是单线程运行的,不管你的机器有多少个内核,只能用到其中的一个,为了能利用多核计算资源,需要使用多进程来处理应用。cluster模块让我们可以很容易地创建一个负载均衡的集群,自动分配CPU多核资源。
var cluster = require(‘cluster’);
var os = require(‘os’);

if (cluster.isMaster){
  for (var i = 0, n = os.cpus().length; i < n; i += 1){
    cluster.fork();
  }
} else {
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

上面代码先判断当前进程是否为主进程(cluster.isMaster),如果是的,就按照CPU的核数,新建若干个worker进程;如果不是,说明当前进程是worker进程,则在该进程启动一个服务器程序。
上面这段代码有一个缺点,就是一旦work进程挂了,主进程无法知道。为了解决这个问题,可以在主进程部署online事件和exit事件的监听函数。

var cluster = require('cluster');

if(cluster.isMaster) {
  var numWorkers = require('os').cpus().length;
  console.log('Master cluster setting up ' + numWorkers + ' workers...');

  for(var i = 0; i < numWorkers; i++) {
    cluster.fork();
  }

  cluster.on('online', function(worker) {
    console.log('Worker ' + worker.process.pid + ' is online');
  });

  cluster.on('exit', function(worker, code, signal) {
    console.log('Worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal);
    console.log('Starting a new worker');
    cluster.fork();
  });
}

上面代码中,主进程一旦监听到worker进程的exit事件,就会重启一个worker进程。worker进程一旦启动成功,可以正常运行了,就会发出online事件。
1.2 worker对象
worker对象是cluster.fork()的返回值,代表一个worker进程。
它的属性和方法如下。
(1)worker.id
worker.id返回当前worker的独一无二的进程编号。这个编号也是cluster.workers中指向当前进程的索引值。

(2)worker.process
所有的worker进程都是用child_process.fork()生成的。child_process.fork()返回的对象,就被保存在worker.process之中。通过这个属性,可以获取worker所在的进程对象。
(3)worker.send()
该方法用于在主进程中,向子进程发送信息。

if (cluster.isMaster) {
  var worker = cluster.fork();
  worker.send('hi there');
} else if (cluster.isWorker) {
  process.on('message', function(msg) {
    process.send(msg);
  });
}

上面代码的作用是,worker进程对主进程发出的每个消息,都做回声。
在worker进程中,要向主进程发送消息,使用process.send(message);要监听主进程发出的消息,使用下面的代码。

process.on('message', function(message) {
  console.log(message);
});    

发出的消息可以字符串,也可以是JSON对象。下面是一个发送JSON对象的例子。

worker.send({
  type: 'task 1',
  from: 'master',
  data: {
    // the data that you want to transfer
  }
});    

1.3 cluster.workers对象
该对象只有主进程才有,包含了所有worker进程。每个成员的键值就是一个worker进程对象,键名就是该worker进程的worker.id属性。

function eachWorker(callback) {
  for (var id in cluster.workers) {
    callback(cluster.workers[id]);
  }
}
eachWorker(function(worker) {
  worker.send('big announcement to all workers');
});

上面代码用来遍历所有worker进程。
当前socket的data事件,也可以用id属性识别worker进程。

socket.on('data', function(id) {
  var worker = cluster.workers[id];
});
  1. cluster模块的属性与方法
    2.1 isMaster,isWorker
    isMaster属性返回一个布尔值,表示当前进程是否为主进程。这个属性由process.env.NODE_UNIQUE_ID决定,如果process.env.NODE_UNIQUE_ID为未定义,就表示该进程是主进程。

isWorker属性返回一个布尔值,表示当前进程是否为work进程。它与isMaster属性的值正好相反。

2.2 fork
fork方法用于新建一个worker进程,上下文都复制主进程。只有主进程才能调用这个方法。

该方法返回一个worker对象。

2.3 kill
kill方法用于终止worker进程。它可以接受一个参数,表示系统信号。

如果当前是主进程,就会终止与worker.process的联络,然后将系统信号法发向worker进程。如果当前是worker进程,就会终止与主进程的通信,然后退出,返回0。

在以前的版本中,该方法也叫做 worker.destroy() 。

2.4 listening事件
worker进程调用listening方法以后,“listening”事件就传向该进程的服务器,然后传向主进程。

该事件的回调函数接受两个参数,一个是当前worker对象,另一个是地址对象,包含网址、端口、地址类型(IPv4、IPv6、Unix socket、UDP)等信息。这对于那些服务多个网址的Node应用程序非常有用。

cluster.on('listening', function (worker, address) {
  console.log("A worker is now connected to " + address.address + ":" + address.port);
});    
  1. 不中断地重启Node服务
    3.1 思路
    重启服务需要关闭后再启动,利用cluster模块,可以做到先启动一个worker进程,再把原有的所有work进程关闭。这样就能实现不中断地重启Node服务。

首先,主进程向worker进程发出重启信号。

workers[wid].send({type: ‘shutdown’, from: ‘master’});

worker进程监听message事件,一旦发现内容是shutdown,就退出。

process.on(‘message’, function(message) {
if(message.type === ‘shutdown’) {
process.exit(0);
}
});

下面是一个关闭所有worker进程的函数。

function restartWorkers() {
  var wid, workerIds = [];
  for(wid in cluster.workers) {
    workerIds.push(wid);
  }

  workerIds.forEach(function(wid) {
    cluster.workers[wid].send({
      text: 'shutdown',
      from: 'master'
     });
    setTimeout(function() {
      if(cluster.workers[wid]) {
        cluster.workers[wid].kill('SIGKILL');
      }
    }, 5000);
  });
};    

3.2 实例
下面是一个完整的实例,先是主进程的代码master.js。

var cluster = require(‘cluster’);

console.log(‘started master with ‘ + process.pid);

// 新建一个worker进程
cluster.fork();

process.on(‘SIGHUP’, function () {
console.log(‘Reloading…’);
var new_worker = cluster.fork();
new_worker.once(‘listening’, function () {
// 关闭所有其他worker进程
for(var id in cluster.workers) {
if (id === new_worker.id.toString()) continue;
cluster.workers[id].kill(‘SIGTERM’);
}
});
});

上面代码中,主进程监听SIGHUP事件,如果发生该事件就关闭其他所有worker进程。之所以是SIGHUP事件,是因为nginx服务器监听到这个信号,会创造一个新的worker进程,重新加载配置文件。另外,关闭worker进程时,主进程发送SIGTERM信号,这是因为Node允许多个worker进程监听同一个端口。

下面是worker进程的代码server.js。

var cluster = require('cluster');

if (cluster.isMaster) {
  require('./master');
  return;
}

var express = require('express');
var http = require('http');
var app = express();

app.get('/', function (req, res) {
  res.send('ha fsdgfds gfds gfd!');
});

http.createServer(app).listen(8080, function () {
  console.log('http://localhost:8080');
});

使用时代码如下。

$ node server.js
started master with 10538
http://localhost:8080      

然后,向主进程连续发出两次SIGHUP信号。

$ kill -SIGHUP 10538
$ kill -SIGHUP 10538    

主进程会连续两次新建一个worker进程,然后关闭所有其他worker进程,显示如下。

Reloading...
http://localhost:8080
Reloading...
http://localhost:8080    

最后,向主进程发出SIGTERM信号,关闭主进程。

$ kill 10538
  1. PM2模块
    PM2模块是cluster模块的一个包装层。它的作用是尽量将cluster模块抽象掉,让用户像使用单进程一样,部署多进程Node应用。

    // app.js
    var http = require(‘http’);

    http.createServer(function(req, res) {
    res.writeHead(200);
    res.end(“hello world”);
    }).listen(8080);

上面代码是标准的Node架设Web服务器的方式,然后用PM2从命令行启动这段代码。

$ pm2 start app.js -i 4    

上面代码的i参数告诉PM2,这段代码应该在cluster_mode启动,且新建worker进程的数量是4个。如果i参数的值是0,那么当前机器有几个CPU内核,PM2就会启动几个worker进程。
如果一个worker进程由于某种原因挂掉了,会立刻重启该worker进程。

# 重启所有worker进程
$ pm2 reload all

每个worker进程都有一个id,可以用下面的命令查看单个worker进程的详情。

$ pm2 show <worker id>    

正确情况下,PM2采用fork模式新建worker进程,即主进程fork自身,产生一个worker进程。pm2 reload命令则会用spawn方式启动,即一个接一个启动worker进程,一个新的worker启动成功,再杀死一个旧的worker进程。采用这种方式,重新部署新版本时,服务器就不会中断服务。

$ pm2 reload <脚本文件名>    

关闭worker进程的时候,可以部署下面的代码,让worker进程监听shutdown消息。一旦收到这个消息,进行完毕收尾清理工作再关闭。

process.on('message', function(msg) {
  if (msg === 'shutdown') {
    close_all_connections();
    delete_logs();
    server.close();
    process.exit(0);
  }
});    

问题:gulp.spritesmith
使用gulp.spritesmith插件制作雪碧图
注意:插件是gulp.spritesmith,不是gulp-spritesmith
插件地址:https://github.com/twolfson/gulp.spritesmith

//javascript代码
const gulp = require('gulp');
const spritesmith = require('gulp.spritesmith');
const dirname = __dirname;

gulp.task('spritesmith', function(){
    gulp.src(dirname + '/src/*.png')
        .pipe(spritesmith({
            imgName: 'icons.png',     // 生成的图片
            cssName: 'icons.sass',    // 生成的sass文件
            padding: 20,              // 图标之间的距离
            algorithm: 'binary-tree', // 图标的排序方式
            cssTemplate: './handlebarsInheritance.sass.handlebars // 模板
        }))
        .pipe(gulp.dest(dirname + '/build'));
});

gulp.task('default', ['spritesmith']);    

//sass模板
{
// Default options
'functions': true,
'variableNameTransforms': ['dasherize']
}

{{#extend "sass"}}
    {{#content "sprites"}}
.syin
  display: inline-block
$imageurl: ''
        {{#each sprites}}
${{strings.name}}: ({{px.x}}, {{px.y}}, {{px.offset_x}}, {{px.offset_y}}, {{px.width}}, {{px.height}}, {{px.total_width}}, {{px.total_height}}, ($imageurl + '{{{escaped_image}}}'), 'syin-{{name}}')
        {{/each}}
    {{/content}}
    {{#content "spritesheet"}}
${{spritesheet_info.strings.name_sprites}}: ({{#each sprites}}${{strings.name}}, {{/each}})
    {{/content}}
{{/extend}}

问题:forever(守护进程)
forever作为node的守护进程而存在的,因为node服务端很容易遇到异常就崩溃,在用这个的时候最好给系统添加 异常报警,异常记录之类的措施,不然用了forver也没有起到任何真正的作用, 不能因为有了forever就忽略了try catch,代码里应该多加异常捕捉,防止程序经常出现崩溃的情况。让程序更加健壮。
1.下载地址
https://github.com/foreverjs/forever
2.安装

npm install forever -gd

3.操作

forever --help  //帮助
forever start app.js //启动
forever stop app.js  //停止
forever list  //列表
forever stopall //关闭所有
forever restartall //重启所有    

4.日志输出

//指定app.js中的日志信息和错误日志输出文件,  
//  -o 就是console.log输出的信息,-e 就是console.error输出的信息
forever start -o out.log -e err.log app.js 

// 追加日志,forever默认是不能覆盖上次的启动日志,  
//  所以如果第二次启动不加-a,则会不让运行  
forever start -l forever.log -a app.js     

5.查看进程

ps -ef|grep node    
很惭愧<br><br>只做了一点微小的工作<br>谢谢大家