Node实战:5 模块化

本文深入探讨了Node.js中的模块系统,包括如何创建和使用模块、Node包管理器npm的使用方法、解决异步编程中回调地狱的问题以及利用async模块进行串行和并行任务处理的技术。

    Node附带了许多内置模块,这些模块都被打包在系统的node可执行文件中。如果从node.js网站上下载Node源代码,就可以查看它们的源文件,它们都在lib/子文件夹下。

5.1 编写简单模块

    exports对象是一个特殊的对象,在每个我们创建的文件中由Node模块系统创建,当引入这个模块中,会作为require函数的值返回。它被封装在每个模块的module对象中,用来暴露函数、变量或者类。

    工厂模式:为了获取ABC对象的实例,需要调用创建函数(或是工厂函数)来创建并返回这个类的实例。

function ABC () {
   this.language = lang;
   this.greet() = function () {
   }
}
exports.create_ABC = function (lang) {
    return new ABC(lang);
}
     构造函数模式:完全把模块的exports对象替换成想让别人使用的类:

function ABC () {
   this.language = lang;
   this.greet() = function () {
   }
}
module.exports = ABC;
      通常会使用工厂模式。
5.2 npm:Node包管理器

     $ npm update

5.3 使用模块

     require

     模块缓存:当模块从指定的文件或者目录上加载之后,Node.js会将它缓存。之后所有的require调用将会从相同的地址加载相同的模块---而这些模块已经初始化或者做过其他工作。这相当有趣,因为有时候两个文件中都尝试加载一个指定的模块,但得到的可能并不是同一个。

      这是Node.js模块系统最强大和神奇的特性之一。许多其他的系统、模块、widget或动态库都集中存储在一个位置,当请求的包本身也需要请求其他不同版本的模块时,版本控制就成了梦魇。而在Node中,可以自由地引入其他不同版本的模块,Node的命名空间和模块规则意味着它们之间不会互相干扰!独立的模块和项目部分内容都可以自由地引入、更新或修改引入的模块,因为它们只能看到自己那部分而不影响系统其他部分。

     循环

5.4 编写模块

   每个文件都是一个含有module和exports对象的模块。

   基本格式

  • 1)创建一个文件夹来存放模块内容
  • 2)添加名为package.json的文件到文件夹中。文件至少包含当前模块的名字和一个主要的JavaScript文件---用来一开始加载该模块。
  • 3)如果Node没有找到package.json文件或者没有指定主JavaScript文件,那它就会查找index.js(或者是编译后的附加模块index.node)。

   创建模块

|____test
|____lib
| |____album.js
| |____albums.js
|____package.json
|____Readme.md

package.json

{ "name": "album-manager",
  "version": "1.0.0",
  "main": "./lib/albums.js",
}
   使用模块进行开发:如果想在多个项目中使用它,可以把它拷贝到其他项目的node_modules/文件夹下,但这样会遇到一个问题:当想要对相册模块做一些修改时究竟会发生什么?

   发布模块:npm publish

5.5 应当内置的通用模块

    常见问题:回调函数中嵌套回调函数。这会导致代码可读性降低,不知道变量在哪里被使用,很难理解函数调用和返回的流程。

    解决方案:可以使用一个叫做async的模块。

    串行执行代码:有两种,分别是通过waterfall函数和series函数。


   waterfall函数接收一个函数数组作为参数并一次一个地执行它们,然后把每个函数的结果传给下一个函数。结束时,结果函数会接收函数数组中最后一个函数的返回结果作为参数并执行。

var fs = require('fs');
var async = require('async');

function load_file_contents(path, callback) {
	async.waterfall([
		function (callback) {
			fs.open(path, 'r', callback);
		},
		function (f, callback) {
			fs.fstat(f, function (err, stats) {
			});
		},
		function (f, stats, callback) {
			if (stats.isFile()) {
				fs.read(f, b, 0, 10000, null, funciont () {} );
			}
		},
		function (f, contents, callback) {
			fs.close(f, function(err) {} );
		}
	]
		function (err, file_contents) {
			callback(err, file_contents);
		}
	);
}
   async.series函数和async.waterfall有两个关键的不同点

  • 来自一个函数的结果不是传到下一个函数,而是收集到一个数组中,这个数组作为“结果”(第二个)参数传给最后的结果函数。依次调用的每一步都会变成结果数组中的一个元素。
  • 我们可以传给async.series一个对象,它会枚举每个key并执行每个key对应的函数。在这种方式下,结果不是作为一个数组传入,而是作为拥有相同key的对象被函数调用。

var async = require('async');
async.series({
	   numbers: function (callback) {
	   	 setTimeout(function () {
	   	 	callback(null, [ 1, 2, 3 ]);
	   	 }, 1500);
	   },
	   strings: function (callback) {
	   	 setTimeout(function () {
	   	 	callback(null, [ 'a', 'b', 'c' ]);
	   	 }, 2000);
	   }
   },
   function (err, results) {
   	 console.log(results);
   }
);
   输出结果:

  { numbers: [ 1, 2, 3 ],  strings: [ 'a', 'b', 'c' ] }

   并行执行:上述函数,可用async.parallel函数并行执行。

   串行和并行组合起来使用:使用async.auto,

    异步循环:async.forEachSeries会遍历我们提供的数组中的每个元素,为每个元素调用我们给定的函数。不仅如此,它会在调用序列中的下一个元素之前等待前一个执行完毕:

async.forEachSeries(
   arr,
   function (element, callback) {
       callback(null);
   },
   function (err) {
   }
);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值