《深入显出Node.js》学习笔记——(二)模块机制

《深入浅出Node.js》学习笔记——(二)模块机制

《深入显出Node.js》学习笔记——(二)模块机制

                                 JavaScript的变迁

 

2.1 CommonJS规范

希望JavaScript能够在任何地方运行

2.1.1 CommonJS的出发点

针对JavaScript自身的缺陷:

①没有模块系统

②标准库较少

③没有标准接口

④缺乏包管理系统

 

希望不仅可以利用JavaScript开发富客户端应用还可以编写:

①服务器端JavaScript应用程序

②命令行工具

③桌面图形界面应用程序

④混合应用(TitaniumAdobe AIR等形式的应用)

 

CommonJS规范涵盖了模块、二进制、Buffer、字符集编码、I/O流、进程环境、文件系统、套接字、单元测试、Web服务器网关接口、包管理

 

《深入显出Node.js》学习笔记——(二)模块机制

 

2.1.2 CommonJS的模块规范

CommonJS对模块的定义分为模块引用、模块定义和模块标识

  1. 模块引用

模块引用的示例代码如下:

var math=require('math');

  1. 模块定义

exports用于导出,是module的属性。将方法挂载在exports对象上作为属性即可定义导出的方式:

//math.js

exports.add=function(){

   var sum=0,

   i=0,

   args=arguments,

   l=args.length;

   while(i<1){

sum+=args[i++];

}

Return sum;

};

在另一个文件中调用:

//program.js

Var math=require('math');

Exports.increment=function(val){

Return math.add(val,1);

};

  1. 模块标识

模块标识即传递给require()方法的参数

模块的意义在于将类聚的方法和变量等限定在私有的作用域中,同时支持引入和导出功能以顺畅地连接上下游依赖。

 

2.2 Node的模块实现

Node中引入模块需要经历的步骤:

①路径分析

②文件定位

③编译执行

 

模块分为两类:①核心模块 ②文件模块

 

2.2.1 优先从缓存加载

Node对引入过的模块会进行缓存,缓存的是编译和执行后的对象。核心模块的缓存检查先于文件模块的缓存检查。

 

2.2.2 路径分析和文件定位

1.模块标识符分析

模块标识符在Node中主要分为以下几类:

①核心模块

②相对路径文件模块

③绝对路径文件模块

④非路径形式的文件模块,如自定义的connect模块

2.文件定位

①文件扩展名分析

Node按照.js.json.node的次序补足扩展名

②目录分析和包

 

2.2.3模块编译

.js文件。通过fs模块同步读取文件后编译执行。

.node文件。用C/C++编写的扩展文件,通过dlopen()方法加载最后编译生成的文件。

.json文件。通过fs模块同步读取文件后,用JSON.parse()解析返回结果。

④其余扩展名文件。它们都被当做.js文件载入。

1.JavaScript模块的编译

           每个模块文件之间进行作用域隔离,执行后返回一个具体function对象。

           exportsmodule.exports

           2.C/C++模块编译

           Node调用process.dlopen()方法进行加载和执行,使用libuv兼容层封装。

           .node模块文件不需要编译。

           3.JSON文件的编译

           Node利用fs模块同步读取JSON文件的内容之后,调用JSON.parse()方法得到对象。

           如果作为配置,就不必调用fs模块去异步读取和解析,直接require()引入即可。

 

2.3 核心模块

核心模块分为C/C++JavaScript编写的两部分,其中C/C++文件放在src目录下,JavaScript文件存放在lib目录下。

2.3.1 JavaScript核心模块的编译过程

1、转存为C/C++代码

使用V8附带的js2c.py工具,将内置的JS代码转换成C++里的数组,生成node_natives.h头文件。

namespacenode {

constchar node_native[] = { 47, 47, ..};

constchar dgram_native[] = { 47, 47, ..};

constchar console_native[] = { 47, 47, ..};

constchar buffer_native[] = { 47, 47, ..};

constchar querystring_native[] = { 47, 47, ..};

constchar punycode_native[] = { 47, 42, ..};

...

struct_native {

constchar* name;

constchar* source;

size_tsource_len;

};

staticconst struct _native natives[] = {

{"node", node_native, sizeof(node_native)-1 },

{"dgram", dgram_native, sizeof(dgram_native)-1 },

...

};

}

JS代码以字符串形式存在node命名空间中,不可直接执行。

启动Node进程时,JS代码直接加载进内存中。

2.编译JavaScript核心模块

同样经历头尾包装,执行和导出exports对象。

与文件模块有区别的地方在于:

①获取源代码的方式

②缓存执行结果的位置

 

2.3.2 C/C++核心模块的编译过程

1.内建模块的组织形式

structnode_module_struct {

intversion;

void*dso_handle;

constchar *filename;

void(*register_func) (v8::Handle<v8::Object> target);

constchar *modname;

};

内建模块定义后,通过NODE_MODULE宏将模块定义到node命名空间中,模块的具体初始化方法挂载为结构的register_func成员:

#defineNODE_MODULE(modname, regfunc)                                                             \

extern"C" {                                                                                                                          \

NODE_MODULE_EXPORTnode::node_module_struct modname ## _module =  \

{                                                                                                                                            \

NODE_STANDARD_MODULE_STUFF,                                                                            \

regfunc,                                                                                                                               \

NODE_STRINGIFY(modname)                                                                                         \

};                                                                                                                                           \

}

Node_extensions.h文件将内建模块放进了node_module_list数组中,

包括:

Node_buffer,node_crypto, node_evals, node_fs, node_http_parser, node_os, node_zlib,node_timer_wrap, node_tcp_wrap, node_udp_wrap, node_pipe_wrap, node_cares_wrap,node_tty_wrap, node_process_wrap, node_fs_event_wrap, node_signal_watcher

使用get_builtin_module()取出模块

内建模块的优势:

①性能优于脚本语言

②直接加载进内存执行

2.内建模块的导出

《深入显出Node.js》学习笔记——(二)模块机制

node在启动时,生成全局变量process,并提供binding()方法协助加载内建模块

Binding()的实现代码在src/node.cc

staticHandle<Value> Binding(const Arguments& args) {

HandleScopescope;

Local<String>module = args[0]->ToString();

String::Utf8Valuemodule_v(module);

node_module_struct*modp;

if(binding_cache.IsEmpty()) {

binding_cache= Persistent<Object>::New(Object::New());

}

Local<Object>exports;

if(binding_cache->Has(module)) {

exports= binding_cache->Get(module)->ToObject();

returnscope.Close(exports);

}

//Append a string to process.moduleLoadList

charbuf[1024];

snprintf(buf,1024, "Binding %s", *module_v);

uint32_tl = module_load_list->Length();

module_load_list->Set(l,String::New(buf));

if((modp = get_builtin_module(*module_v)) != NULL) {

exports= Object::New();

modp->register_func(exports);

binding_cache->Set(module,exports);

} elseif (!strcmp(*module_v, "constants")) {

exports= Object::New();

DefineConstants(exports);

binding_cache->Set(module,exports);

#ifdef__POSIX__

} elseif (!strcmp(*module_v, "io_watcher")) {

exports= Object::New();

IOWatcher::Initialize(exports);

binding_cache->Set(module,exports);

#endif

} elseif (!strcmp(*module_v, "natives")) {

exports= Object::New();

DefineJavaScript(exports);

binding_cache->Set(module,exports);

} else {

returnThrowException(Exception::Error(String::New("No such module")));

}

returnscope.Close(exports);

}

2.3.3核心模块的引入流程

《深入显出Node.js》学习笔记——(二)模块机制

 

2.3.4 编写核心模块

编写头文件,编写C++文件,更改src/node_extensions.h,更改node的项目生成文件,编译,安装

 

2.4 C/C++扩展模块

属于文件模块中的一类

《深入显出Node.js》学习笔记——(二)模块机制

 

2.4.1前提条件

GYP项目生成工具

V8引擎C++

libuv

Node内部库

⑤其他库

2.4.2 C/C++扩展模块的编写

无须将源代码写进node命名空间,也不需要提供头文件

2.4.3 C/C++扩展模块的编译

编写.gyp项目文件

{

'targets':[

{

'target_name':'hello',

'sources':[

'src/hello.cc'

],

'conditions':[

['OS =="win"',

{

'libraries':['-lnode.lib']

}

]

]

}

]

}

然后调用

$node-gyp configure

会得到如下的输出结果:

gyp infoit worked if it ends with ok

gyp infousing node-gyp@0.8.3

gyp infousing node@0.8.14 | darwin | x64

gyp infospawn python

gyp infospawn args [ '/usr/local/lib/node_modules/node-gyp/gyp/gyp',

gyp infospawn args 'binding.gyp',

gyp infospawn args '-f',

gyp infospawn args 'make',

gyp infospawn args '-I',

gyp infospawn args'/Users/jacksontian/git/diveintonode/examples/02/addon/build/config.gypi',

gyp infospawn args '-I',

gyp infospawn args '/usr/local/lib/node_modules/node-gyp/addon.gypi',

gyp infospawn args '-I',

gyp infospawn args '/Users/jacksontian/.node-gyp/0.8.14/common.gypi',

gyp infospawn args '-Dlibrary=shared_library',

gyp infospawn args '-Dvisibility=default',

gyp infospawn args '-Dnode_root_dir=/Users/jacksontian/.node-gyp/0.8.14',

gyp infospawn args'-Dmodule_root_dir=/Users/jacksontian/git/diveintonode/examples/02/addon',

gyp infospawn args '--depth=.',

gyp infospawn args '--generator-output',

gyp infospawn args 'build',

gyp infospawn args '-Goutput_dir=.' ]

gyp infook

 

2.4.4 C/C++扩展模块的加载

Require()在引入.node文件的过程中,经历了四个层面的调用

加载.node文件的两个步骤:

①调用uv_dlopen()方法打开动态链接库

②调用uv_dlsym()找到动态链接库中通过NODE_MODULE宏定义的方法地址

两个过程都通过libuv库进行封装

《深入显出Node.js》学习笔记——(二)模块机制

 

2.5 模块调用栈

《深入显出Node.js》学习笔记——(二)模块机制

 

2.6 包与NPM

《深入显出Node.js》学习笔记——(二)模块机制

 

2.6.1 包结构

包目录包含的文件:package.json,bin, lib, doc, test

2.6.2 包描述文件与NPM

包描述文件是一个JSON格式的文件——package.json

CommonJSpackage.json文件定义了一些必需字段:

Name,description, version, keywords, maintainers, contributors, bugs, licenses,repositories, dependencies, homepage, os, cpu, engine, builtin, directories,implements, scripts

2.6.3 NPM常用功能

1.查看帮助 2.安装依赖包 3.NPM钩子命令 4.发布包 5.分析包

2.6.4 局域NPM

《深入显出Node.js》学习笔记——(二)模块机制

2.6.5 NPM潜在问题

包质量和安全问题

口碑效应

 

2.7前后端共用模块

2.7.1 模块的侧重点

Node模块引入同步,前端模块异步。

2.7.2 AMD规范

define明确定义一个模块,而在Node中是隐式包装的

用返回的方式实现导出

2.7.3 CMD规范

AMD区别于定义模块和依赖引入部分

2.7.4 兼容多种模块规范

;(function(name, definition) {

// 检测上

文章评论

《深入显出Node.js》学习笔记——(二)模块机制
漫画:程序员的工作
《深入显出Node.js》学习笔记——(二)模块机制
老美怎么看待阿里赴美上市
《深入显出Node.js》学习笔记——(二)模块机制
60个开发者不容错过的免费资源库
《深入显出Node.js》学习笔记——(二)模块机制
程序员最害怕的5件事 你中招了吗?
《深入显出Node.js》学习笔记——(二)模块机制
程序员的鄙视链
《深入显出Node.js》学习笔记——(二)模块机制
亲爱的项目经理,我恨你
《深入显出Node.js》学习笔记——(二)模块机制
鲜为人知的编程真相
《深入显出Node.js》学习笔记——(二)模块机制
2013年美国开发者薪资调查报告
《深入显出Node.js》学习笔记——(二)模块机制
每天工作4小时的程序员
《深入显出Node.js》学习笔记——(二)模块机制
旅行,写作,编程
《深入显出Node.js》学习笔记——(二)模块机制
那些性感的让人尖叫的程序员
《深入显出Node.js》学习笔记——(二)模块机制
不懂技术不要对懂技术的人说这很容易实现
《深入显出Node.js》学习笔记——(二)模块机制
要嫁就嫁程序猿—钱多话少死的早
《深入显出Node.js》学习笔记——(二)模块机制
我是如何打败拖延症的
《深入显出Node.js》学习笔记——(二)模块机制
如何区分一个程序员是“老手“还是“新手“?
《深入显出Node.js》学习笔记——(二)模块机制
看13位CEO、创始人和高管如何提高工作效率
《深入显出Node.js》学习笔记——(二)模块机制
代码女神横空出世
《深入显出Node.js》学习笔记——(二)模块机制
我的丈夫是个程序员
《深入显出Node.js》学习笔记——(二)模块机制
程序员和编码员之间的区别
《深入显出Node.js》学习笔记——(二)模块机制
做程序猿的老婆应该注意的一些事情
《深入显出Node.js》学习笔记——(二)模块机制
程序员的样子
《深入显出Node.js》学习笔记——(二)模块机制
那些争议最大的编程观点
《深入显出Node.js》学习笔记——(二)模块机制
老程序员的下场
《深入显出Node.js》学习笔记——(二)模块机制
为什么程序员都是夜猫子
《深入显出Node.js》学习笔记——(二)模块机制
Java 与 .NET 的平台发展之争
《深入显出Node.js》学习笔记——(二)模块机制
科技史上最臭名昭著的13大罪犯
《深入显出Node.js》学习笔记——(二)模块机制
写给自己也写给你 自己到底该何去何从
《深入显出Node.js》学习笔记——(二)模块机制
10个帮程序员减压放松的网站
《深入显出Node.js》学习笔记——(二)模块机制
当下全球最炙手可热的八位少年创业者
《深入显出Node.js》学习笔记——(二)模块机制
程序员都该阅读的书
《深入显出Node.js》学习笔记——(二)模块机制
编程语言是女人
《深入显出Node.js》学习笔记——(二)模块机制
如何成为一名黑客
《深入显出Node.js》学习笔记——(二)模块机制
十大编程算法助程序员走上高手之路
《深入显出Node.js》学习笔记——(二)模块机制
“肮脏的”IT工作排行榜
《深入显出Node.js》学习笔记——(二)模块机制
聊聊HTTPS和SSL/TLS协议
《深入显出Node.js》学习笔记——(二)模块机制
为啥Android手机总会越用越慢?
《深入显出Node.js》学习笔记——(二)模块机制
Web开发人员为什么越来越懒了?
《深入显出Node.js》学习笔记——(二)模块机制
程序员应该关注的一些事儿
《深入显出Node.js》学习笔记——(二)模块机制
5款最佳正则表达式编辑调试器
《深入显出Node.js》学习笔记——(二)模块机制
10个调试和排错的小建议
《深入显出Node.js》学习笔记——(二)模块机制
我跳槽是因为他们的显示器更大
《深入显出Node.js》学习笔记——(二)模块机制
程序猿的崛起——Growth Hacker
《深入显出Node.js》学习笔记——(二)模块机制
2013年中国软件开发者薪资调查报告
《深入显出Node.js》学习笔记——(二)模块机制
初级 vs 高级开发者 哪个性价比更高?
《深入显出Node.js》学习笔记——(二)模块机制
一个程序员的时间管理
《深入显出Node.js》学习笔记——(二)模块机制
程序员的一天:一寸光阴一寸金
《深入显出Node.js》学习笔记——(二)模块机制
什么才是优秀的用户界面设计
《深入显出Node.js》学习笔记——(二)模块机制
程序员周末都喜欢做什么?
《深入显出Node.js》学习笔记——(二)模块机制
程序员必看的十大电影