Node.js有了高速的提升,然则对于Node的意见你也许供给有个别时光去适应

图片 1

图片 2

目录

 

目录

 

前言

  随着有个别大商厦如沃尔玛,PayPal等开首利用Node.js,在过去的几年里,Node.js有了迅速的抓牢。越多的人起初接纳Node并公布modules到NPM,其发展的进程远超其它耗费语言。不过对于Node的眼光你或者需求一些时光去适应,尤其是这一个刚从别的编制程序语言转型过来的开发职员。

  在本文中自小编将谈一谈Node开发者们最常范的有些指鹿为马以及哪些来防止那么些错误。有关示例的源代码,你能够从github上赢获得。

 

前言

  随着有个别大公司如沃尔玛(Walmart),PayPal等开首利用Node.js,在过去的几年里,Node.js有了迅猛的增长。越多的人开始接纳Node并宣布modules到NPM,其升高的进程远超其它开支语言。然则对于Node的见地你或然要求一些时日去适应,尤其是这些刚从别的编程语言转型过来的开发人员。

  在本文中本人将谈一谈Node开发者们最常范的有些破绽百出以及哪些来制止这一个错误。有关示例的源代码,你能够从github上赢获得。

 

1 不采用开发工具

  • 机动重启工具nodemon或supervisor
  • 浏览器内的live
    reload工具(当静态财富或views视图产生改变时自动reload页面)

  与其余编制程序语言如PHP或Ruby区别,当你改改了源代码后,Node须求再行启航才能使修改生效。在创制Web应用程序时还有一件事会使你放慢脚步,那正是当修改静态资源时刷新浏览器页面。当然你能够耐心地手动来做那个工作,可是那里会有一些更好的消除办法。

1 不应用开发工具

  • 机关重启工具nodemon或supervisor
  • 浏览器内的live
    reload工具(当静态能源或views视图产生变更时自动reload页面)

  与任何编制程序语言如PHP或Ruby分化,当你改改了源代码后,Node要求重新起动才能使修改生效。在开创Web应用程序时还有一件事会使您放慢脚步,那正是当修改静态财富时刷新浏览器页面。当然你能够耐心地手动来做这一个工作,不过那里会有部分更好的化解办法。

1.1 自动重启工具

  我们中的超越十三分之多个人或者都是这么编写和调节代码的,在编辑器中保存代码,然后在控制台按CT奇骏L+C键结束利用,随后通过向上键找到之前实施过的起步命令,按回车来重新起动应用。可是,通过利用上面这么些工具得以自动完成应用的重启并简化开发流程:

  这么些工具得以监视代码文件的改动并机关心重视启服务。上面以nodemon为例来说说哪些运用那些工具。首先通过npm进行全局安装:

npm i nodemon -g

  然后,在终极通过nodemon代替node命令来运行应用:

# node server.js

$ nodemon server.js
14 Nov 21:23:23 - [nodemon] v1.2.1
14 Nov 21:23:23 - [nodemon] to restart at any time, enter `rs`
14 Nov 21:23:23 - [nodemon] watching: *.*
14 Nov 21:23:23 - [nodemon] starting `node server.js`
14 Nov 21:24:14 - [nodemon] restarting due to changes...
14 Nov 21:24:14 - [nodemon] starting `node server.js`

  对nodemon或node-supervisor来说,在拥有已部分选用中,最牛逼的实在能够钦赐忽略的文件或文件夹。

1.1 自动重启工具

  大家中的大多数人恐怕都以那般编写和调剂代码的,在编辑器中保存代码,然后在决定台按CTCR-VL+C键甘休利用,随后经过向上键找到在此之前实施过的运转命令,按回车来重新起动应用。然而,通过选取下边那么些工具得以自行完毕应用的重启并简化开发流程:

  那一个工具得以监视代码文件的修改并活动重启服务。上边以nodemon为例来说说什么样利用那么些工具。首先通过npm举办全局安装:

npm i nodemon -g

  然后,在终点通过nodemon代替node命令来运转应用:

# node server.js

$ nodemon server.js
14 Nov 21:23:23 - [nodemon] v1.2.1
14 Nov 21:23:23 - [nodemon] to restart at any time, enter `rs`
14 Nov 21:23:23 - [nodemon] watching: *.*
14 Nov 21:23:23 - [nodemon] starting `node server.js`
14 Nov 21:24:14 - [nodemon] restarting due to changes...
14 Nov 21:24:14 - [nodemon] starting `node server.js`

  对nodemon或node-supervisor来说,在具有已有的采取中,最牛逼的其实能够钦定忽略的文书或文件夹。

1.2 浏览器自动刷新工具

  除了上边介绍的全自动重启工具外,还有任何的工具得以补助你加快web应用程序的付出。livereload工具允许浏览器在监测到程序变动后自动刷新页面,而不用手动进行刷新。

  其工作的基本原理和地点介绍的形似,只是它监测特定文件夹内的修改然后自动刷新浏览器,而不是重启整个服务。自动刷新供给借助于在页面中流入脚本也许通过浏览器插件来兑现。

  那里小编不去介绍如何利用livereload,相反,小编将介绍如何通过Node来创设三个一般的工具,它将全部下边这个效应:

  • 蹲点文件夹中的文件修改
  • 通过server-sent
    events
    向全部已接连的客户端发送新闻,并且
  • 接触多个page reload

  首先大家须要通过NPM来安装项目需求的具有信赖项:

  • express – 创造三个演示web应用程序
  • watch – 监视文件修改
  • sendevent – sever-sent events
    (SSE),也许也能够行使websockets来兑现
  • uglify-js – 用于压缩客户端JavaScript文件
  • ejs – 视图模板

  接下去自个儿将开创二个简短的Express server在前端页面中渲染home视图:

var express = require('express');
var app = express();
var ejs = require('ejs');
var path = require('path');

var PORT = process.env.PORT || 1337;

// view engine setup
app.engine('html', ejs.renderFile);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

// serve an empty page that just loads the browserify bundle
app.get('/', function(req, res) {
res.render('home');
});

app.listen(PORT);
console.log('server started on port %s', PORT);

  因为运用的是Express,所以大家能够将浏览器自动刷新工具做成2个Express的中间件。那些个中件会attach到SSE
endpoint,并会在客户端脚本中开创三个view
helper。中间件function的参数是Express的app,以及供给被监视的文本夹。于是,大家将下面包车型大巴代码加到server.js中,放到view
setup之前:

var reloadify = require('./lib/reloadify');
reloadify(app, __dirname + '/views');

  现在/views文件夹中的文件被监视。整个中间件看起来像上面那样:

  var sendevent = require('sendevent');
  var watch = require('watch');
  var uglify = require('uglify-js');
  var fs = require('fs');
  var ENV = process.env.NODE_ENV || 'development';

  // create && minify static JS code to be included in the page
  var polyfill = fs.readFileSync(__dirname + '/assets/eventsource-polyfill.js', 'utf8');
  var clientScript = fs.readFileSync(__dirname + '/assets/client-script.js', 'utf8');
  var script = uglify.minify(polyfill + clientScript, { fromString: true }).code;

  function reloadify(app, dir) {
    if (ENV !== 'development') {
      app.locals.watchScript = '';
      return;
    }

    // create a middlware that handles requests to `/eventstream`
    var events = sendevent('/eventstream');

    app.use(events);

    watch.watchTree(dir, function (f, curr, prev) {
      events.broadcast({ msg: 'reload' });
    });

    // assign the script to a local var so it's accessible in the view
    app.locals.watchScript = '<script>' + script + '</script>';
  }

  module.exports = reloadify;

  你大概已经注意到了,假使运维条件尚未被设置成’development’,那么那在那之中间件什么也不会做。那意味着大家只可以在成品环境上校该代码删掉。

  前端JS脚本文件相当简单,它只承担监听SSE的消息并在急需的时候再一次加载页面:

  (function() {

    function subscribe(url, callback) {
      var source = new window.EventSource(url);

      source.onmessage = function(e) {
        callback(e.data);
      };

      source.onerror = function(e) {
        if (source.readyState == window.EventSource.CLOSED) return;

        console.log('sse error', e);
      };

      return source.close.bind(source);
    };

    subscribe('/eventstream', function(data) {
      if (data && /reload/.test(data)) {
        window.location.reload();
      }
    });

  }());

  文件eventsourfe-polyfill.js可以从Remy Sharp’s polyfill for
SSE
找到。最终这一个关键的有些是将转变的剧本通过上边包车型地铁办法丰盛到前者页面/views/homt.html中:

  ...
  <%- watchScript %>
  ...

  以后,当你每一次对home.html页面做修改时,浏览器都将从服务珍视新加载该页面(http://localhost:1337/)。

 

1.2 浏览器自动刷新工具

  除了上面介绍的自发性重启工具外,还有其余的工具得以支持你加速web应用程序的支出。livereload工具允许浏览器在监测到程序变动后活动刷新页面,而不用手动举行刷新。

  其行事的基本原理和方面介绍的貌似,只是它监测特定文件夹内的修改然后自行刷新浏览器,而不是重启整个服务。自动刷新要求依靠于在页面中注入脚本或然经过浏览器插件来促成。

  那里小编不去介绍怎么样接纳livereload,相反,小编将介绍怎么样通过Node来创制2个一般的工具,它将全部下边这么些职能:

  • 蹲点文件夹中的文件修改
  • 通过server-sent
    events
    向具有已接连的客户端发送音讯,并且
  • 接触3个page reload

  首先大家需求通过NPM来安装项目供给的持有依赖项:

  • express – 创设1个演示web应用程序
  • watch – 监视文件修改
  • sendevent – sever-sent events
    (SSE),可能也得以行使websockets来落实
  • uglify-js – 用于压缩客户端JavaScript文件
  • ejs – 视图模板

  接下去自身将创立三个不难易行的Express server在前端页面中渲染home视图:

var express = require('express');
var app = express();
var ejs = require('ejs');
var path = require('path');

var PORT = process.env.PORT || 1337;

// view engine setup
app.engine('html', ejs.renderFile);
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'html');

// serve an empty page that just loads the browserify bundle
app.get('/', function(req, res) {
res.render('home');
});

app.listen(PORT);
console.log('server started on port %s', PORT);

  因为运用的是Express,所以大家得以将浏览器自动刷新工具做成叁个Express的中间件。那些当中件会attach到SSE
endpoint,并会在客户端脚本中开创贰个view
helper。中间件function的参数是Express的app,以及要求被监视的文书夹。于是,大家将上边包车型大巴代码加到server.js中,放到view
setup在此之前:

var reloadify = require('./lib/reloadify');
reloadify(app, __dirname + '/views');

  将来/views文件夹中的文件被监视。整个中间件看起来像下边那样:

  var sendevent = require('sendevent');
  var watch = require('watch');
  var uglify = require('uglify-js');
  var fs = require('fs');
  var ENV = process.env.NODE_ENV || 'development';

  // create && minify static JS code to be included in the page
  var polyfill = fs.readFileSync(__dirname + '/assets/eventsource-polyfill.js', 'utf8');
  var clientScript = fs.readFileSync(__dirname + '/assets/client-script.js', 'utf8');
  var script = uglify.minify(polyfill + clientScript, { fromString: true }).code;

  function reloadify(app, dir) {
    if (ENV !== 'development') {
      app.locals.watchScript = '';
      return;
    }

    // create a middlware that handles requests to `/eventstream`
    var events = sendevent('/eventstream');

    app.use(events);

    watch.watchTree(dir, function (f, curr, prev) {
      events.broadcast({ msg: 'reload' });
    });

    // assign the script to a local var so it's accessible in the view
    app.locals.watchScript = '<script>' + script + '</script>';
  }

  module.exports = reloadify;

  你可能已经注意到了,假若运维环境尚未被设置成’development’,那么那其中间件什么也不会做。那象征大家只能在产品环境准将该代码删掉。

  前端JS脚本文件相当不难,它只承担监听SSE的音讯并在急需的时候重新加载页面:

  (function() {

    function subscribe(url, callback) {
      var source = new window.EventSource(url);

      source.onmessage = function(e) {
        callback(e.data);
      };

      source.onerror = function(e) {
        if (source.readyState == window.EventSource.CLOSED) return;

        console.log('sse error', e);
      };

      return source.close.bind(source);
    };

    subscribe('/eventstream', function(data) {
      if (data && /reload/.test(data)) {
        window.location.reload();
      }
    });

  }());

  文件eventsourfe-polyfill.js可以从Remy Sharp’s polyfill for
SSE
找到。最后特别关键的某个是将转变的本子通过下边包车型客车办法丰硕到前端页面/views/homt.html中:

  ...
  <%- watchScript %>
  ...

  未来,当你每一次对home.html页面做修改时,浏览器都将从服务珍视新加载该页面(http://localhost:1337/)。

 

2 阻塞event loop

  由于Node.js是单线程运营的,全部对event
loop的堵截都将使整个程序被卡住。那代表一旦你有3个上千个客户端访问的web
server,并且程序产生了event
loop阻塞,那么富有的客户端都将处于等候景况而无法获得服务器应答。

  这里有局部例证,你可能会在不在意中使用它们而发生event loop阻塞:

  难点是你会不检点中做了上述的作业,终究将三个具有15Mb左右轻重的内容输出并不会时常产生,对吧?那足以让攻击者发现并最后使你的任何服务器遭遇DDOS攻击而夭亡掉。

  幸运的是你能够透过监视event
loop的推移来检查和测试很是。大家得以由此有个别特定的消除方案例如StrongOps来落实,也许也得以因此某个开源的modules来促成,如blocked

  那么些工具的办事原理是规范地跟踪每一遍interval之间所消费的年月然后告诉。时间差是由此如此的方法来测算的:先记下下interval进程中A点和B点的可靠时间,然后用B点的日子减去A点的日子,再减去interval运营区间的时光。

  下边的例子丰裕表达了哪些来落实那或多或少,它是这么做的:

  • 获得当前光阴和以参数字传送入的岁月变量之间的高精度时间值(high-resolution)
  • 分明在例行状态下interval的event loop的延迟时间
  • 将延迟时间显示成浅绛红,假设跨越阀值则显得为樱草黄
  • 接下来看其实运作的情状,每300飞秒执行一遍大的运算

  上边是上述示范的源代码:

  var getHrDiffTime = function(time) {
    // ts = [seconds, nanoseconds]
    var ts = process.hrtime(time);
    // convert seconds to miliseconds and nanoseconds to miliseconds as well
    return (ts[0] * 1000) + (ts[1] / 1000000);
  };

  var outputDelay = function(interval, maxDelay) {
    maxDelay = maxDelay || 100;

    var before = process.hrtime();

    setTimeout(function() {
      var delay = getHrDiffTime(before) - interval;

      if (delay < maxDelay) {
        console.log('delay is %s', chalk.green(delay));
      } else {
        console.log('delay is %s', chalk.red(delay));
      }

      outputDelay(interval, maxDelay);
    }, interval);
  };

  outputDelay(300);

  // heavy stuff happening every 2 seconds here
  setInterval(function compute() {
    var sum = 0;

    for (var i = 0; i <= 999999999; i++) {
      sum += i * 2 - (i + 1);
    }
  }, 2000);

   运维方面包车型地铁代码须求设置chalk。运营之后您应该会在极端看到如下图所示的结果:

图片 3

  后边早已说过,开源modules也应用了一般的不二法门来落到实处对应的功用,因而得以放心使用它们:

  通过利用这种技术实行质量分析,你能够规范地找出代码中的哪一部分会导致延迟。

 

2 阻塞event loop

  由于Node.js是单线程运营的,全数对event
loop的鸿沟都将使任何程序被打断。那意味假设你有七个上千个客户端访问的web
server,并且程序发生了event
loop阻塞,那么具有的客户端都将远在等候状态而望洋兴叹赢得服务器应答。

  那里有一部分事例,你恐怕会在不理会中利用它们而发生event loop阻塞:

  难点是您会非常大心中做了上述的工作,毕竟将二个有所15Mb左右尺寸的内容输出并不会时时产生,对吧?那可以让攻击者发现并最终使您的全方位服务器遭到DDOS攻击而夭亡掉。

  幸运的是您能够由此监视event
loop的推移来检查和测试十分。大家能够通过有些特定的消除方案例如StrongOps来贯彻,只怕也得以通过某些开源的modules来促成,如blocked

  那个工具的劳作规律是准确地跟踪每一回interval之间所消费的时间然后告诉。时间差是通过那样的法子来计量的:先记下下interval进度中A点和B点的高精度时间,然后用B点的日子减去A点的光阴,再减去interval运营区间的时光。

  上边包车型大巴例子充足表达了什么来促成那点,它是那样做的:

  • 收获当前时间和以参数字传送入的时日变量之间的高精度时间值(high-resolution)
  • 分明在正规意况下interval的event loop的延迟时间
  • 将延迟时间显示成深红,借使超越阀值则显得为灰黄
  • 接下来看其实运作的气象,每300飞秒执行三遍大的运算

  上面是上述示范的源代码:

  var getHrDiffTime = function(time) {
    // ts = [seconds, nanoseconds]
    var ts = process.hrtime(time);
    // convert seconds to miliseconds and nanoseconds to miliseconds as well
    return (ts[0] * 1000) + (ts[1] / 1000000);
  };

  var outputDelay = function(interval, maxDelay) {
    maxDelay = maxDelay || 100;

    var before = process.hrtime();

    setTimeout(function() {
      var delay = getHrDiffTime(before) - interval;

      if (delay < maxDelay) {
        console.log('delay is %s', chalk.green(delay));
      } else {
        console.log('delay is %s', chalk.red(delay));
      }

      outputDelay(interval, maxDelay);
    }, interval);
  };

  outputDelay(300);

  // heavy stuff happening every 2 seconds here
  setInterval(function compute() {
    var sum = 0;

    for (var i = 0; i <= 999999999; i++) {
      sum += i * 2 - (i + 1);
    }
  }, 2000);

   运营方面包车型大巴代码供给设置chalk。运转之后您应当会在巅峰看到如下图所示的结果:

图片 4

  前面早已说过,开源modules也使用了相似的主意来贯彻对应的成效,因而得以放心使用它们:

  通过使用那种技术进行质量分析,你能够精确地找出代码中的哪部分会促成延迟。

 

3 频仍调用回调函数

  很多时候当你保存文件然后再次起动Node web
app时它就便捷地崩掉了。最有恐怕出现的来由就是调用了三回回调函数,那象征你很或然在率先次调用之后忘记return了。

  我们创制3个例证来重现一下那种处境。小编将开创3个简便的隐含基本申明功效的代理server。要采用它你需要安装request那么些依靠包,运转程序然后访问(如http://localhost:1337/?url=http://www.google.com/)。下面是这个例子的源代码:

  var request = require('request');
  var http = require('http');
  var url = require('url');
  var PORT = process.env.PORT || 1337;

  var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
  var isUrl = new RegExp(expression);

  var respond = function(err, params) {
    var res = params.res;
    var body = params.body;
    var proxyUrl = params.proxyUrl;

    res.setHeader('Content-type', 'text/html; charset=utf-8');

    if (err) {
      console.error(err);
      res.end('An error occured. Please make sure the domain exists.');
    } else {
      res.end(body);
    }
  };

  http.createServer(function(req, res) {
    var queryParams = url.parse(req.url, true).query;
    var proxyUrl = queryParams.url;

    if (!proxyUrl || (!isUrl.test(proxyUrl))) {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.write("Please provide a correct URL param. For ex: ");
      res.end("<a href='http://localhost:1337/?url=http://www.google.com/'>http://localhost:1337/?url=http://www.google.com/</a>");
    } else {
      // ------------------------
      // Proxying happens here
      // TO BE CONTINUED
      // ------------------------
    }
  }).listen(PORT);

  除代理本人外,上边的代码大约涵盖了颇具必要的有的。再仔细看看下边包车型地铁情节:

request(proxyUrl, function(err, r, body) {
if (err) {
    respond(err, {
    res: res,
    proxyUrl: proxyUrl
    });
}

respond(null, {
    res: res,
    body: body,
    proxyUrl: proxyUrl
});
});

  在回调函数中,大家有错误处理的逻辑,但是在调用respond函数后忘记甘休一切运营流程了。那意味着一旦我们走访一个不能host的站点,respond函数将会被调用三次,大家会在顶峰收到下边的错误音信:

  Error: Can't set headers after they are sent.
      at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
      at respond (/Users/alexandruvladutu/www/airpair-2/3-multi-callback/proxy-server.js:18:7)

This can be avoided either by using the `return` statement or by wrapping the 'success' callback in the `else` statement:

  request(.., function(..params) {
    if (err) {
      return respond(err, ..);
    }

    respond(..);
  });

  // OR:

  request(.., function(..params) {
    if (err) {
      respond(err, ..);
    } else {
      respond(..);
    }
  });

 

3 频繁调用回调函数

  很多时候当你保存文件然后重新启航Node web
app时它就飞速地崩掉了。最有或许现身的由来就是调用了四次回调函数,那表示你很也许在第一次调用之后忘记return了。

  大家创造三个例证来重现一下那种情景。作者将开创二个不难易行的隐含基本评释作用的代办server。要动用它你必要设置request这一个依靠包,运转程序然后访问(如http://localhost:1337/?url=http://www.google.com/)。下面是这个例子的源代码:

  var request = require('request');
  var http = require('http');
  var url = require('url');
  var PORT = process.env.PORT || 1337;

  var expression = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/gi;
  var isUrl = new RegExp(expression);

  var respond = function(err, params) {
    var res = params.res;
    var body = params.body;
    var proxyUrl = params.proxyUrl;

    res.setHeader('Content-type', 'text/html; charset=utf-8');

    if (err) {
      console.error(err);
      res.end('An error occured. Please make sure the domain exists.');
    } else {
      res.end(body);
    }
  };

  http.createServer(function(req, res) {
    var queryParams = url.parse(req.url, true).query;
    var proxyUrl = queryParams.url;

    if (!proxyUrl || (!isUrl.test(proxyUrl))) {
      res.writeHead(200, { 'Content-Type': 'text/html' });
      res.write("Please provide a correct URL param. For ex: ");
      res.end("<a href='http://localhost:1337/?url=http://www.google.com/'>http://localhost:1337/?url=http://www.google.com/</a>");
    } else {
      // ------------------------
      // Proxying happens here
      // TO BE CONTINUED
      // ------------------------
    }
  }).listen(PORT);

  除代理本身外,下面的代码大致涵盖了颇具需求的一部分。再精心看看下边包车型客车内容:

request(proxyUrl, function(err, r, body) {
if (err) {
    respond(err, {
    res: res,
    proxyUrl: proxyUrl
    });
}

respond(null, {
    res: res,
    body: body,
    proxyUrl: proxyUrl
});
});

  在回调函数中,大家有错误处理的逻辑,不过在调用respond函数后忘记甘休一切运营流程了。那意味一旦我们走访2个一点都不大概host的站点,respond函数将会被调用三次,大家会在极限收到上边包车型地铁错误音信:

  Error: Can't set headers after they are sent.
      at ServerResponse.OutgoingMessage.setHeader (http.js:691:11)
      at respond (/Users/alexandruvladutu/www/airpair-2/3-multi-callback/proxy-server.js:18:7)

This can be avoided either by using the `return` statement or by wrapping the 'success' callback in the `else` statement:

  request(.., function(..params) {
    if (err) {
      return respond(err, ..);
    }

    respond(..);
  });

  // OR:

  request(.., function(..params) {
    if (err) {
      respond(err, ..);
    } else {
      respond(..);
    }
  });

 

4 圣诞树结构的回调(回调的炼狱)

  有个外人总是拿鬼世界般的回调参数来抨击Node,认为在Node中回调嵌套是力不从心幸免的。但实则并非如此。这里有成千上万缓解措施,能够使你的代码看起来10分规整:

  大家来创设三个例子,然后重构它以利用async模块。那个app是四个大约的前端能源分析工具,它完结上边这几个干活儿:

  • 检查HTML代码中有稍许scripts,stylesheets,images的引用
  • 将检查的结果输出到极限
  • 检查每二个能源的content-length并将结果输出到终点

  除async模块外,你必要安装上边这么些npm包:

  • request – 读取页面数据(body,headers等)
  • cheerio – 后台的jQuery(DOM成分选取器)
  • once – 确认保障回调函数只被实施一回

    var URL = process.env.URL;
    var assert = require(‘assert’);
    var url = require(‘url’);
    var request = require(‘request’);
    var cheerio = require(‘cheerio’);
    var once = require(‘once’);
    var isUrl = new RegExp(/[-a-zA-Z0-9@:%+.~#?&//=]{2,256}.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%+.~#?&//=]*)?/gi);

    assert(isUrl.test(URL), ‘must provide a correct URL env variable’);

    request({ url: URL, gzip: true }, function(err, res, body) {

    if (err) { throw err; }
    
    if (res.statusCode !== 200) {
      return console.error('Bad server response', res.statusCode);
    }
    
    var $ = cheerio.load(body);
    var resources = [];
    
    $('script').each(function(index, el) {
      var src = $(this).attr('src');
      if (src) { resources.push(src); }
    });
    
    // .....
    // similar code for stylesheets and images
    // checkout the github repo for the full version
    
    var counter = resources.length;
    var next = once(function(err, result) {
      if (err) { throw err; }
    
      var size = (result.size / 1024 / 1024).toFixed(2);
    
      console.log('There are ~ %s resources with a size of %s Mb.', result.length, size);
    });
    
    var totalSize = 0;
    
    resources.forEach(function(relative) {
      var resourceUrl = url.resolve(URL, relative);
    
      request({ url: resourceUrl, gzip: true }, function(err, res, body) {
        if (err) { return next(err); }
    
        if (res.statusCode !== 200) {
          return next(new Error(resourceUrl + ' responded with a bad code ' + res.statusCode));
        }
    
        if (res.headers['content-length']) {
          totalSize += parseInt(res.headers['content-length'], 10);
        } else {
          totalSize += Buffer.byteLength(body, 'utf8');
        }
    
        if (!--counter) {
          next(null, {
            length: resources.length,
            size: totalSize
          });
        }
      });
    });
    

    });

  下面的代码看起来还不是特地倒霉,可是你还能嵌套更深的回调函数。从底层的代码中您应有能辨识出什么样是圣诞树结构了,其代码的缩进看起来像那一个样子:

        if (!--counter) {
          next(null, {
            length: resources.length,
            size: totalSize
          });
        }
      });
    });
  });

  要运营方面包车型地铁代码,在终点输入上面包车型大巴授命:

  $ URL=https://bbc.co.uk/ node before.js
  # Sample output:
  # There are ~ 24 resources with a size of 0.09 Mb.

  使用async举香港行政局地重构之后,大家的代码看起来像下边那样:

  var async = require('async');

  var rootHtml = '';
  var resources = [];
  var totalSize = 0;

  var handleBadResponse = function(err, url, statusCode, cb) {
    if (!err && (statusCode !== 200)) {
      err = new Error(URL + ' responded with a bad code ' + res.statusCode);
    }

    if (err) {
      cb(err);
      return true;
    }

    return false;
  };

  async.series([
    function getRootHtml(cb) {
      request({ url: URL, gzip: true }, function(err, res, body) {
        if (handleBadResponse(err, URL, res.statusCode, cb)) { return; }

        rootHtml = body;

        cb();
      });
    },
    function aggregateResources(cb) {
      var $ = cheerio.load(rootHtml);

      $('script').each(function(index, el) {
        var src = $(this).attr('src');
        if (src) { resources.push(src); }
      });

      // similar code for stylesheets && images; check the full source for more

      setImmediate(cb);
    },
    function calculateSize(cb) {
      async.each(resources, function(relativeUrl, next) {
        var resourceUrl = url.resolve(URL, relativeUrl);

        request({ url: resourceUrl, gzip: true }, function(err, res, body) {
          if (handleBadResponse(err, resourceUrl, res.statusCode, cb)) { return; }

          if (res.headers['content-length']) {
            totalSize += parseInt(res.headers['content-length'], 10);
          } else {
            totalSize += Buffer.byteLength(body, 'utf8');
          }

          next();
        });
      }, cb);
    }
  ], function(err) {
    if (err) { throw err; }

    var size = (totalSize / 1024 / 1024).toFixed(2);
    console.log('There are ~ %s resources with a size of %s Mb.', resources.length, size);
  });

 

4 圣诞树结构的回调(回调的苦海)

  某些人连连拿鬼世界般的回调参数来攻击Node,认为在Node中回调嵌套是不可能制止的。但事实上并非如此。那里有诸多缓解方法,能够使你的代码看起来越发规整:

  我们来创设三个事例,然后重构它以应用async模块。那些app是3个简约的前端财富分析工具,它成功上面那个工作:

  • 自笔者批评HTML代码中有微微scripts,stylesheets,images的引用
  • 将检查的结果输出到终端
  • 自作者批评每三个能源的content-length并将结果输出到极限

  除async模块外,你要求设置上面那一个npm包:

  • request – 读取页面数据(body,headers等)
  • cheerio – 后台的jQuery(DOM成分选用器)
  • once – 确认保证回调函数只被执行贰遍

    var URL = process.env.URL;
    var assert = require(‘assert’);
    var url = require(‘url’);
    var request = require(‘request’);
    var cheerio = require(‘cheerio’);
    var once = require(‘once’);
    var isUrl = new RegExp(/[-a-zA-Z0-9@:%+.~#?&//=]{2,256}.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%+.~#?&//=]*)?/gi);

    assert(isUrl.test(URL), ‘must provide a correct URL env variable’);

    request({ url: URL, gzip: true }, function(err, res, body) {

    if (err) { throw err; }
    
    if (res.statusCode !== 200) {
      return console.error('Bad server response', res.statusCode);
    }
    
    var $ = cheerio.load(body);
    var resources = [];
    
    $('script').each(function(index, el) {
      var src = $(this).attr('src');
      if (src) { resources.push(src); }
    });
    
    // .....
    // similar code for stylesheets and images
    // checkout the github repo for the full version
    
    var counter = resources.length;
    var next = once(function(err, result) {
      if (err) { throw err; }
    
      var size = (result.size / 1024 / 1024).toFixed(2);
    
      console.log('There are ~ %s resources with a size of %s Mb.', result.length, size);
    });
    
    var totalSize = 0;
    
    resources.forEach(function(relative) {
      var resourceUrl = url.resolve(URL, relative);
    
      request({ url: resourceUrl, gzip: true }, function(err, res, body) {
        if (err) { return next(err); }
    
        if (res.statusCode !== 200) {
          return next(new Error(resourceUrl + ' responded with a bad code ' + res.statusCode));
        }
    
        if (res.headers['content-length']) {
          totalSize += parseInt(res.headers['content-length'], 10);
        } else {
          totalSize += Buffer.byteLength(body, 'utf8');
        }
    
        if (!--counter) {
          next(null, {
            length: resources.length,
            size: totalSize
          });
        }
      });
    });
    

    });

  下面的代码看起来还不是专程不佳,不过你还足以嵌套更深的回调函数。从底部的代码中你应当能识别出怎么着是圣诞树结构了,其代码的缩进看起来像那个样子:

        if (!--counter) {
          next(null, {
            length: resources.length,
            size: totalSize
          });
        }
      });
    });
  });

  要运转方面包车型客车代码,在极限输入上边包车型的士命令:

  $ URL=https://bbc.co.uk/ node before.js
  # Sample output:
  # There are ~ 24 resources with a size of 0.09 Mb.

  使用async实行部分重构之后,大家的代码看起来像上面这样:

  var async = require('async');

  var rootHtml = '';
  var resources = [];
  var totalSize = 0;

  var handleBadResponse = function(err, url, statusCode, cb) {
    if (!err && (statusCode !== 200)) {
      err = new Error(URL + ' responded with a bad code ' + res.statusCode);
    }

    if (err) {
      cb(err);
      return true;
    }

    return false;
  };

  async.series([
    function getRootHtml(cb) {
      request({ url: URL, gzip: true }, function(err, res, body) {
        if (handleBadResponse(err, URL, res.statusCode, cb)) { return; }

        rootHtml = body;

        cb();
      });
    },
    function aggregateResources(cb) {
      var $ = cheerio.load(rootHtml);

      $('script').each(function(index, el) {
        var src = $(this).attr('src');
        if (src) { resources.push(src); }
      });

      // similar code for stylesheets && images; check the full source for more

      setImmediate(cb);
    },
    function calculateSize(cb) {
      async.each(resources, function(relativeUrl, next) {
        var resourceUrl = url.resolve(URL, relativeUrl);

        request({ url: resourceUrl, gzip: true }, function(err, res, body) {
          if (handleBadResponse(err, resourceUrl, res.statusCode, cb)) { return; }

          if (res.headers['content-length']) {
            totalSize += parseInt(res.headers['content-length'], 10);
          } else {
            totalSize += Buffer.byteLength(body, 'utf8');
          }

          next();
        });
      }, cb);
    }
  ], function(err) {
    if (err) { throw err; }

    var size = (totalSize / 1024 / 1024).toFixed(2);
    console.log('There are ~ %s resources with a size of %s Mb.', resources.length, size);
  });

 

5 创制三个大而全部的应用程序

  一些初入Node的开发人士往往会将其他语言的局地构思方式融入进来,从而写出分歧风格的代码。例如将装有的代码写到一个文书里,而不是将它们分散到祥和的模块中再公布到NPM等。

  就拿我们前边的例子来说,大家将持有的始末都置身二个文件里,那使得代码很难被测试和读懂。可是别担心,大家会重构代码使其看起来不错并且更为模块化。当然,那也将使得地防止回调鬼世界。

  借使大家将URL validator,response
handler,request功用块以及resource处理程序抽出来放到它们本人的模块中,大家的主程序看起来会像上边那样:

  // ...
  var handleBadResponse = require('./lib/bad-response-handler');
  var isValidUrl = require('./lib/url-validator');
  var extractResources = require('./lib/resource-extractor');
  var request = require('./lib/requester');

  // ...
  async.series([
    function getRootHtml(cb) {
      request(URL, function(err, data) {
        if (err) { return cb(err); }

        rootHtml = data.body;

        cb(null, 123);
      });
    },
    function aggregateResources(cb) {
      resources = extractResources(rootHtml);

      setImmediate(cb);
    },
    function calculateSize(cb) {
      async.each(resources, function(relativeUrl, next) {
        var resourceUrl = url.resolve(URL, relativeUrl);

        request(resourceUrl, function(err, data) {
          if (err) { return next(err); }

          if (data.res.headers['content-length']) {
            totalSize += parseInt(data.res.headers['content-length'], 10);
          } else {
            totalSize += Buffer.byteLength(data.body, 'utf8');
          }

          next();
        });
      }, cb);
    }
  ], function(err) {
    if (err) { throw err; }

    var size = (totalSize / 1024 / 1024).toFixed(2);
    console.log('\nThere are ~ %s resources with a size of %s Mb.', resources.length, size);
  });

  而request功效块则看起来像那样:

  var handleBadResponse = require('./bad-response-handler');
  var request = require('request');

  module.exports = function getSiteData(url, callback) {
    request({
      url: url,
      gzip: true,
      // lying a bit
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36'
      }
    }, function(err, res, body) {
      if (handleBadResponse(err, url, res && res.statusCode, callback)) { return; }

      callback(null, {
        body: body,
        res: res
      });
    });
  };

  完整的例子能够从github
repo
中找到。

  将来就大概了,代码越发易读,大家也能够发轫为大家的app添加测试用例了。当然,大家还是能一而再重构代码将收获response长度的功能独立分离出来放到自个儿的模块中。

  好的一些是Node鼓励我们编写小的模块并揭露到NPM。在NPM中你能够找到各个各种的模块小到如在interval间生成随机数的模块。你应当大力使你的Node应用程序模块化,作用越不难越好。

 

5 创造三个大而完全的应用程序

  一些初入Node的开发职员往往会将别的语言的局地思索方式融入进来,从而写出差别风格的代码。例如将享有的代码写到三个文本里,而不是将它们分散到祥和的模块中再发表到NPM等。

  就拿我们事先的例证来说,我们将拥有的内容都坐落一个文书里,那使得代码很难被测试和读懂。然而别担心,大家会重构代码使其看起来能够并且越发模块化。当然,这也将实用地幸免回调鬼世界。

  即便大家将U奥德赛L validator,response
handler,request功效块以及resource处理程序抽出来放到它们本身的模块中,大家的主程序看起来会像下边那样:

  // ...
  var handleBadResponse = require('./lib/bad-response-handler');
  var isValidUrl = require('./lib/url-validator');
  var extractResources = require('./lib/resource-extractor');
  var request = require('./lib/requester');

  // ...
  async.series([
    function getRootHtml(cb) {
      request(URL, function(err, data) {
        if (err) { return cb(err); }

        rootHtml = data.body;

        cb(null, 123);
      });
    },
    function aggregateResources(cb) {
      resources = extractResources(rootHtml);

      setImmediate(cb);
    },
    function calculateSize(cb) {
      async.each(resources, function(relativeUrl, next) {
        var resourceUrl = url.resolve(URL, relativeUrl);

        request(resourceUrl, function(err, data) {
          if (err) { return next(err); }

          if (data.res.headers['content-length']) {
            totalSize += parseInt(data.res.headers['content-length'], 10);
          } else {
            totalSize += Buffer.byteLength(data.body, 'utf8');
          }

          next();
        });
      }, cb);
    }
  ], function(err) {
    if (err) { throw err; }

    var size = (totalSize / 1024 / 1024).toFixed(2);
    console.log('\nThere are ~ %s resources with a size of %s Mb.', resources.length, size);
  });

  而request作用块则看起来像这样:

  var handleBadResponse = require('./bad-response-handler');
  var request = require('request');

  module.exports = function getSiteData(url, callback) {
    request({
      url: url,
      gzip: true,
      // lying a bit
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36'
      }
    }, function(err, res, body) {
      if (handleBadResponse(err, url, res && res.statusCode, callback)) { return; }

      callback(null, {
        body: body,
        res: res
      });
    });
  };

  完整的例证能够从github
repo
中找到。

  未来就大致了,代码尤其易读,我们也可以起首为大家的app添加测试用例了。当然,大家还是能持续重构代码将赢得response长度的效率独立分离出来放到自个儿的模块中。

  好的有个别是Node鼓励大家编写小的模块并发表到NPM。在NPM中您能够找到各样各个的模块小到如在interval间生成随机数的模块。你应当着力使您的Node应用程序模块化,效用越简单越好。

 

6 缺点和失误日志

  很多Node教程都会来得示例代码,并在内部不一样的地点含有console.log,那给许多Node开发人士留下了多个影象,即console.log正是在Node代码中贯彻日志效用。

  在编辑Node
apps代码时你应有利用一些比console.log更好的工具来完毕日志功效,因为这个工具:

  • 对一部分大而复杂的指标不供给采纳util.inspect
  • 嵌入类别化器,如对errors,request和response对象等展开体系化
  • 援助三种不相同的日志源
  • 可自动包罗hostname,process id,application name等
  • 支撑分歧级别的日记(如debug,info,error,fatal等)
  • 有的高档作用如日志文件自动滚动等

  那些意义都足避防费应用,你可以在生育环境中利用日志模块如bunyan。假设将模块安装到全局,你还能收获二个方便人民群众的CLI开发工具。

  让大家来看望它的以身作则程序以询问怎样行使它:

  var http = require('http');
  var bunyan = require('bunyan');

  var log = bunyan.createLogger({
    name: 'myserver',
    serializers: {
      req: bunyan.stdSerializers.req,
      res: bunyan.stdSerializers.res
    }
  });

  var server = http.createServer(function (req, res) {
    log.info({ req: req }, 'start request');  // <-- this is the guy we're testing
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
    log.info({ res: res }, 'done response');  // <-- this is the guy we're testing
  });

  server.listen(1337, '127.0.0.1', function() {
    log.info('server listening');

    var options = {
      port: 1337,
      hostname: '127.0.0.1',
      path: '/path?q=1#anchor',
      headers: {
        'X-Hi': 'Mom'
      }
    };

    var req = http.request(options, function(res) {
      res.resume();
      res.on('end', function() {
        process.exit();
      })
    });

    req.write('hi from the client');
    req.end();
  });

  在极端运转,你会看到上面包车型客车出口内容:

  $ node server.js
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"msg":"server listening","time":"2014-11-16T11:30:13.263Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","host":"127.0.0.1:1337","connection":"keep-alive"},"remoteAddress":"127.0.0.1","remotePort":61580},"msg":"start request","time":"2014-11-16T11:30:13.271Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"res":{"statusCode":200,"header":"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nDate: Sun, 16 Nov 2014 11:30:13 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n"},"msg":"done response","time":"2014-11-16T11:30:13.273Z","v":0}

  开发进度中可将它作为1个CLI工具来利用:

图片 5

  正如您所见到的,bunyan给您提供了关于当前经过的不在少数得力的新闻,这个音信在成品环境中都这个首要。此外3个利于的效劳是您能够将日志输出到三个或多少个流中。

 

6 缺乏日志

  很多Node教程都会展现示例代码,并在中间分化的地方含有console.log,那给广大Node开发人士留下了叁个印象,即console.log就是在Node代码中落实日志功效。

  在编写制定Node
apps代码时你应该选拔一些比console.log更好的工具来贯彻日志作用,因为那么些工具:

  • 对有个别大而复杂的靶子不须要选用util.inspect
  • 放置类别化器,如对errors,request和response对象等展开系列化
  • 援助多种分化的日志源
  • 可活动包蕴hostname,process id,application name等
  • 支持区别级别的日记(如debug,info,error,fatal等)
  • 局地尖端效率如日志文件自动滚动等

  那些职能都足避防费使用,你能够在生育条件中接纳日志模块如bunyan。假使将模块安装到全局,你还足以拿走一个利于的CLI开发工具。

  让我们来探视它的言传身教程序以询问哪些使用它:

  var http = require('http');
  var bunyan = require('bunyan');

  var log = bunyan.createLogger({
    name: 'myserver',
    serializers: {
      req: bunyan.stdSerializers.req,
      res: bunyan.stdSerializers.res
    }
  });

  var server = http.createServer(function (req, res) {
    log.info({ req: req }, 'start request');  // <-- this is the guy we're testing
    res.writeHead(200, { 'Content-Type': 'text/plain' });
    res.end('Hello World\n');
    log.info({ res: res }, 'done response');  // <-- this is the guy we're testing
  });

  server.listen(1337, '127.0.0.1', function() {
    log.info('server listening');

    var options = {
      port: 1337,
      hostname: '127.0.0.1',
      path: '/path?q=1#anchor',
      headers: {
        'X-Hi': 'Mom'
      }
    };

    var req = http.request(options, function(res) {
      res.resume();
      res.on('end', function() {
        process.exit();
      })
    });

    req.write('hi from the client');
    req.end();
  });

  在终极运转,你会看到下边的输出内容:

  $ node server.js
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"msg":"server listening","time":"2014-11-16T11:30:13.263Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"req":{"method":"GET","url":"/path?q=1#anchor","headers":{"x-hi":"Mom","host":"127.0.0.1:1337","connection":"keep-alive"},"remoteAddress":"127.0.0.1","remotePort":61580},"msg":"start request","time":"2014-11-16T11:30:13.271Z","v":0}
  {"name":"myserver","hostname":"MBP.local","pid":14304,"level":30,"res":{"statusCode":200,"header":"HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nDate: Sun, 16 Nov 2014 11:30:13 GMT\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n"},"msg":"done response","time":"2014-11-16T11:30:13.273Z","v":0}

  开发进度中可将它看做三个CLI工具来行使:

图片 6

  正如你所见到的,bunyan给你提供了有关当前历程的不在少数实用的音讯,那么些音讯在产品环境中都尤其主要。此外1个便利的功用是你能够将日志输出到三个或多少个流中。

 

7 没有测试

  没有提供测试的顺序不是1个总体的先后。已经有诸如此类多的工具得以扶持大家来拓展测试,实在没有任何理由不编写测试用例了:

  作为NPM模块的约定,你须要在package.json中钦命测试命令,如:

  {
    "name": "express",
    ...
    "scripts": {
      "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
      ...
   }

  然后经过npm test来运转测试,你根本不用去管什么来调用测试框架。

  此外二个您必要考虑的是在付出代码以前必须使全体的测试用例都通过,那只必要通过一行不难的授命就能够成功:

npm i pre-commit --save-dev

  当然你也得以强制执行有个别特定的code
coverage级别的测试而不肯提交这一个不信守该级其余代码。pre-commit模块作为三个pre-commit的hook程序能够活动地运维npm
test。

  假使你不明确什么来编排测试,能够经过某些在线教程恐怕在Github中看看这个流行的Node项目它们是什么样做的:

 

7 没有测试

  没有提供测试的先后不是一个完全的主次。已经有如此多的工具得以扶助大家来展开测试,实在没有任何理由不编写测试用例了:

  作为NPM模块的预订,你必要在package.json中钦点测试命令,如:

  {
    "name": "express",
    ...
    "scripts": {
      "test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
      ...
   }

  然后通过npm test来运营测试,你根本不用去管如何来调用测试框架。

  其余三个您须求考虑的是在付出代码以前必须使拥有的测试用例都通过,那只须求经过一行简单的一声令下就可以完结:

npm i pre-commit --save-dev

  当然你也能够强制执行有些特定的code
coverage级别的测试而推辞提交那么些不遵从该级其他代码。pre-commit模块作为多少个pre-commit的hook程序能够自动地运转npm
test。

  要是你不明确如何来编排测试,能够经过一些在线教程或然在Github中看看那个流行的Node项目它们是哪些做的:

 

8 不行使静态分析工具

  为了不在生产条件中才发觉难点,最好的点子是在开发进度中采取静态分析工具立即就意识那一个标题。

  例如,ESLint工具得以协助大家缓解广大标题:

  • 或者的失实。如:禁止在条件表明式中选取赋值语句,禁止选取debugger
  • 强制最佳体验。如:禁止证明七个相同名称的变量,禁用arguments.calle
  • 找出秘密的安全题材,如应用eval()或不安全的正则说明式
  • 侦测出大概存在的性情难点
  • 履行同一的风骨

  有关ESLint越来越多的总体规则能够查阅官方文书档案。假使想在实际项目中选用ESLint,你还应该看看它的配备文书档案

  有关怎么着配置ESLint,这里能够找到一些例证。

  其余,那里还有一些相似的工具如JSLintJSHint

  若是您想解析AST(抽象源树或泛泛语法树)并团结成立静态分析工具,可以参考EsprimaAcorn

 

8 不应用静态分析工具

  为了不在生产环境中才察觉难题,最好的方式是在开发进度中动用静态分析工具马上就意识那么些题目。

  例如,ESLint工具得以扶助大家化解许多题目:

  • 只怕的不当。如:禁止在原则表达式中央银行使赋值语句,禁止行使debugger
  • 强制最佳体验。如:禁止申明五个一律名称的变量,禁止行使arguments.calle
  • 找出秘密的三门峡题材,如利用eval()或不安全的正则表明式
  • 侦测出大概存在的习性难题
  • 履行同样的品格

  有关ESLint更加多的欧洲经济共同体规则能够查看法定文书档案。若是想在实质上项目中应用ESLint,你还应当看看它的安排文书档案

  有关如何配置ESLint,这里能够找到一些事例。

  此外,那里还有一对形似的工具如JSLintJSHint

  若是你想解析AST(抽象源树或虚幻语法树)并自身创建静态分析工具,能够参见EsprimaAcorn

 

9 不曾监视与脾气分析

  如若Node应用程序没有监视与本性分析,你将对其运转意况一窍不通。一些很重庆大学的东西如event
loop延迟,CPU负载,系统负荷或内部存款和储蓄器使用量等您将不可能获悉。

  那里有一部分特定的服务可以援助到您,能够从New
Relic
,
StrongLoop以及Concurix,
AppDynamics等理解到。

  你也足以经过开源模块如look或结成差别的NPM包和谐来贯彻。不管选取哪个种类方式,你都要力保始终都能监测到你的次序的运行情况,不然你或者会在半夜接到各个奇异的对讲机告诉您程序又出新那样或那样的标题。

 

9 未曾监视与品质分析

  要是Node应用程序没有监视与质量分析,你将对其运作状态不详。一些很要紧的东西如event
loop延迟,CPU负载,系统负荷或内部存款和储蓄器使用量等您将不可能得知。

  那里有局地一定的劳务能够帮助到你,能够从New
Relic
,
StrongLoop以及Concurix,
AppDynamics等摸底到。

  你也得以透过开源模块如look或组合不一致的NPM包和谐来兑现。不管采纳哪一类方法,你都要保障始终都能监测到你的次序的周转情状,不然你大概会在半夜收受各样奇异的电话机告诉您程序又并发这么或那样的标题。

 

10 使用console.log来debug

  一旦程序现身谬误,你能够归纳地在代码中插入console.log来开始展览debug。难题一下子就解决了今后剔除console.log调节和测试语句再持续。

  难点是别的的开发人士(甚至是您本人)恐怕还会赶上同样的难点而再另行上边的操作。那正是干什么调节和测试模块如debug存在的缘故。你能够在代码中选取debug
function来代替console.log语句,而且在调节完事后并非删除它们。

  其余开发职员假如赶上难题须要调剂代码,只供给通过DEBUG环境变量来运营程序即可。

  这些小的module具有以下优点:

  • 除非你通过DEBUG环境变量运行程序,不然它不会在决定台出口任何内容。
  • 你能够有选用地对代码中的一有个别开始展览调剂(甚至可以通过通配符来钦赐内容)。
  • 终极的出口内容有各样分歧的颜色,看起来很舒畅女士。

  来探视官方给出的演示:

  // app.js
  var debug = require('debug')('http')
    , http = require('http')
    , name = 'My App';

  // fake app

  debug('booting %s', name);

  http.createServer(function(req, res){
    debug(req.method + ' ' + req.url);
    res.end('hello\n');
  }).listen(3000, function(){
    debug('listening');
  });

  // fake worker of some kind

  require('./worker');

<!--code lang=javascript linenums=true-->

  // worker.js
  var debug = require('debug')('worker');

  setInterval(function(){
    debug('doing some work');
  }, 1000);

  就算以node
app.js来运维程序,不会输出任何内容。然而一旦开发银行的时候带着DEBUG标记,那么:

图片 7

  除了在应用程序中选用它们,你还是能够在一部分小的modules中央银行使它并颁发到NPM。与任何一些复杂的logger分化,它只担负debugging而且还很好使。

 

初稿地址:https://www.airpair.com/node.js/posts/top-10-mistakes-node-developers-make

10 使用console.log来debug

  一旦程序出现错误,你能够省略地在代码中插入console.log来开始展览debug。难题一下子就解决了现在剔除console.log调节和测试语句再持续。

  难题是其他的开发职员(甚至是你协调)恐怕还会遇上同样的标题而再另行下面的操作。那便是怎么调节和测试模块如debug留存的案由。你能够在代码中使用debug
function来取代console.log语句,而且在调节和测试完之后不要删除它们。

  其余开发职员假设遭遇难点亟待调剂代码,只须要通过DEBUG环境变量来运行程序即可。

  那个小的module具有以下优点:

  • 只有您通过DEBUG环境变量运营程序,不然它不会在支配台出口任何内容。
  • 您能够有选取地对代码中的一片段实行调节(甚至足以经过通配符来内定内容)。
  • 极端的输出内容有各样分裂的水彩,看起来很舒服。

  来看看官方给出的示范:

  // app.js
  var debug = require('debug')('http')
    , http = require('http')
    , name = 'My App';

  // fake app

  debug('booting %s', name);

  http.createServer(function(req, res){
    debug(req.method + ' ' + req.url);
    res.end('hello\n');
  }).listen(3000, function(){
    debug('listening');
  });

  // fake worker of some kind

  require('./worker');

<!--code lang=javascript linenums=true-->

  // worker.js
  var debug = require('debug')('worker');

  setInterval(function(){
    debug('doing some work');
  }, 1000);

  要是以node
app.js来运行程序,不会输出任何内容。不过如若开发银行的时候带着DEBUG标记,那么:

图片 8

  除了在应用程序中使用它们,你还足以在局地小的modules中应用它并发布到NPM。与别的一些错综复杂的logger分化,它只负责debugging而且还很好使。

 

原稿地址:https://www.airpair.com/node.js/posts/top-10-mistakes-node-developers-make

相关文章