RingoJS的模块管理

模块化管理越来越有意思,刚开始看到RingoJS中有JSGI,我还猜想是否是OSGi的一个实现,后来才知道不是。RingoJS的模块化管理是CommonJS的一个实现,很Young,很Simple。

 

Modules in Ringo

 

Ringo实现了CommonJS Modules1.1规范,在Javascript世界中,存在多个可用Module patterns,但是在Ringo的世界里,我们只用这个:

  • 每一个文件就是一个模块,不需要特别的语法申明
  • 任何function或者其他属性,只要你在module(文件)中,attatch到exports对象上,他们就被导出(让外部可见),成为这个模块的导出属性
  • require(‘foobar’)会返回一个拥有模块所有exposed属性的对象,代表foobar模块

 

剖析Module

 

In Ringo,每一个javascript文件都被当做是一个module,Ringo提供导入、导出的实现。

一个简单的例子会让事情变得更明白。下面我们实现一个foobar.js模块(module),我们希望这个模块提供add方法,而不是其内部私有方法adder:

// foobar.js

varadder = function(a, b) {

return a + b;

};

 

exports.add = function(a, b) {

return adder(a, b );

};

 

这样,我们就完成了一个module的实现,我们现在可以在ringo环境中,通过require引入这个模块,然后调用这个模块对外开放的add方法(以下在Ringo命令行中执行, >>是Ringo命令行提示符):

>> varfoobar = require(‘foobar’);

>>foobar.add(3, 4);

7

而模块(module)中的私有(没有export)的方法,我们不能调用:

>> foobar.adder(2, 2);

TypeError: Cannot find function adder in object [object Object].

 

Ringo为每一个模块(module)对象提供了一个module对象,这个module对象包含如下属性:

  • id - 模块的标识id
  • path-模块的文件路劲
  • uri-模块的URL
  • exports-assigning to this property allows modules to replace the default exports object(如何使用??)

 

模块在私有区域执行,在模块中,除了显式定义为exported的对象属性,其他都是私有不被外部环境或者其他模块可见的——这个解决了JavaScript编程中常见的统一Namespace中,属性混乱、冲突的问题。

 

Module IDs: module文件名称,去除.js扩展名,相对ID,绝对ID——类似于相对路径、绝对路径的概念。

Module Path: 一些列的路径位置(多个!),Ringo会在其中寻在module,可以通过如下途径设置:

  • RINGO_MODULE_PATH 环境变量
  • ringo.modulepath Java System Property
  • -m 或者 --modules ——ringo的命令行启动参数
  • 使用module-path servlet 初始化参数——JsgiServlet(?? 这是啥,OSGi的美丽实现么??)
  • 在Ringo环境中,变更require.paths属性(Array-Like)

从Ringo 0.8版本开始,默认的module路劲(module path)包含安装目录下的modules和packages文件夹。Ringo 0.8也可以不用配置的从Java Classpath中装载modules。——0.8之前的版本?不提了。

 

Packages

Package是组合几个相关的模块(module)以及相关资源的单位。

Package是包含package.json(Package描述文件)的文件夹。下面的package.json能够被Ringo的模块装载机制(Module Loader)识别:

{

“main”: “/lib/main.js”

}

如果我们在使用require(或者其他通过module id引入模块的机制)引入模块时,module id指向package目录,并且package.json文件中定义了main属性,Ringo将尝试装载制定资源。main的属性中的文件路劲必须是针对package root的相对路径。

如果module id指向一个目录,目录中不存在package.json(不是Package),或者存在package.json但是其中没有定义main属性,Ring将尝试装载该目录下的index.js文件

如果module id的一部分为package目录,Ringo将剩余的module id部分在Package 目录的lib目录中解析、寻早;在package.json文件中可以定义directories.lib属性,覆盖默认的lib文件夹:

{

“directories”: {

“lib”: “lib”

}

}

Ringo也可以通过ringo-admin工具,进行简单的package管理工作。

 

Caching & Reloading

每一个模块(module)在第一次装载后会被缓存(caching),Ringo根据module id决定一个模块是第一次装载,还是可以直接从缓存中装载;避免模块被装载超过一次,——但是在一些特定环境下,模块仍然可能被重复装载。

默认情况下,Ringo 模块装载机制会在模块被required(这事嘛意思? require调用,还是require之后,每一次针对模块导出方法的调用??——经不严格测试,应该是做require调用时)时,自动检查此模块以及其依赖的模块是否被修改,被修改了的模块会被重新装载。

以产品模式运行ringo可以避免模块的修改检查。 -p --production 或者在servlet环境中配置。

 

Ringo 模块扩展

CommonJS模块管理规范设计的比较简洁,Ringo在此基础上,做了一些精巧、细微的扩展——这些扩展主要是针对export、import功能。对于“标准、规范”的随意扩展,会导致将来代码不能迁移,还好Ringo提供了工具,可以将这些代码转换为pure CommonJS规范代码。

include就是Ringo提供的一个扩展,其用户类似于require。但是include相当于把模块代码直接copy到本地,而不是仅仅引入模块中export的属性。

在Ringo中,我们可以结合Javascript 1.8的destructuring assignment新特性,进一步增强原有的require功能:

var{foo, bar} = require(“some/module”);

在模块中,使用:

export(“foo”, “bar”, “baz”);

 

Destructuring Assignment是个挺有意思的新功能,后续在Ringo环境中,我们会尝试去学习使用它。

Leave Comment