这篇文章是由克里斯托弗·波耶 ( Christoph Pojer )撰写的。 要了解有关Christoph的更多信息, 请单击此处 。
这篇博客文章旨在为使用MooTools开发ServerSide JavaScript(SSJS)提供一个起点。 它专注于Node.js( http://nodejs.org ),并试图解释主要概念以及与客户端开发之间的区别。 它仅基于我目前的经验,当前的工作以及到目前为止为我自己定义的最佳实践-尽管其中大多数受到了MooTools团队其他人的严重影响。
如何设置Node.js
- 从http://nodejs.org/#download下载
- 通过./configure && make和&sudo make install构建
再简单不过了。
MooTools和SSJS的现状
当前的MooTools Core 1.2和1.3beta2版本无法与Node.js一起使用。 Node.js以及其他服务器端JavaScript实现都采用了CommonJS标准,其中包括一个模块系统。 您创建的每个模块都可以通过“导出”对象导出对象。 您可以使用“ require('path / to / module')”来包含文件,该文件使您可以访问模块的导出变量:
math.js
exports.add = function(a, b){
return a + b;
};
myApplication.js
var math = require('./math');
var sys = require('sys'); // System module
sys.puts(math.add(13, 37)); // Outputs "50"
您可以通过命令行上的“ node myApplication.js”执行此脚本。
您可以在CommonJS Wiki上找到有关此的更多信息: http ://wiki.commonjs.org/wiki/Modules/1.1。
CommonJS中指定的模块系统与普通客户端脚本标签之间的主要区别在于它们不共享相同的(全局)作用域。 这意味着在模块中通过“ var foo”创建变量不会自动使其在全局对象上可用。 客户端上的全局对象是“窗口”对象,通常在服务器端不可用。 在Node.js中,全局对象称为GLOBAL,而其他一些实现仅使用名称“ global”或在模块内部将其引用为“ this”。 CommonJS没有定义它,因此每种环境都以不同的方式解决它。
尽管在仅围绕一个对象JavaScript库中添加对模块的支持相对容易,但MooTools提供了一些全局变量,例如Class,Events,Type(Native)等。此外,该新标准还很年轻,如果我们曾经直接在MooTools中实现对CommonJS的支持,我们希望定义可以推荐给整个社区的最佳实践。
注意:CommonJS实际上不是标准,而是服务器端JavaScript实施可以(或应该)遵循的一组规范,以统一各种环境,并使创建无需修改即可在所有平台上工作的模块成为可能。
获取在Node.js上运行的MooTools
在过去的几个月中,MooTools团队的一些成员提出了各种使MooTools CommonJS兼容的方法。 我现在在GitHub上创建了一个存储库,可帮助创建MooTools的构建版本。 这主要是基于@keeto和我的工作。 我们将使用MooTools Core的进行中版本,它是1.3之前的版本。 如果您没有安装git或不想输入一些命令,则可以跳到下一部分,而只需下载MooTools的预构建版本: MooTools.js (基于此提交)。
获取MooTools 1.3wip(命令行)
git clone git://github.com/cpojer/mootools-core.git
获取打包程序(需要安装php-cli)
git clone http://github.com/kamicane/packager.git
获取MooTools CommonJS加载程序
git clone git://github.com/cpojer/mootools-loader.git
建立自定义的MooTools版本
cd packager # Switch into the Packager folder
./packager register /path/to/mootools-core
./packager register /path/to/mootools-loader
./packager build Loader/Prefix Core/Class Core/Class.Extras Loader/Loader -blocks 1.2compat > MooTools.js
您应该看到类似以下的输出:
Build using: Core, Loader
Included Files/Components:
- Loader/Prefix: [Prefix]
- Core/Core: [Core, MooTools, Type, typeOf, instanceOf]
- Core/Array: [Array]
- Core/String: [String]
- Core/Function: [Function]
- Core/Number: [Number]
- Core/Class: [Class]
- Core/Class.Extras: [Class.Extras, Chain, Events, Options]
- Core/Object: [Object, Hash]
- Loader/Loader: [Loader]
并且应该已经准备好使用文件“ MooTools.js”。
在Node.js中使用MooTools
require.paths.push('path/to/mootoolsjs/');
require('MooTools').apply(GLOBAL);
var sys = require('sys');
var MyClass = new Class({
initialize: function(){
sys.puts('It works!');
}
});
new MyClass;
在命令行上再次使用“ node”命令运行此命令,您应该看到“它有效!” 作为输出。
请注意,这是实验性的,您会发现其功能可能会发生更改,并且可能包含错误。 到目前为止,上述解决方案仅在Node.js上进行了测试,但它也应该在其他SSJS实现上也可以使用
应用程序和模块之间的差异
我要强调的一件事是,我坚信模块不应创建全局变量。 但是,上述解决方案将MooTools提供的所有功能都放在了全局范围内。 虽然在开发应用程序时可以做到这一点是合理的,并且可以控制它的各个方面,但是我不认为在创建使用MooTools的模块(插件)时这样做是个好主意。 这就是为什么我想出的解决方案还有另一种使用它的方法,请考虑以下示例。
MyPlugin.js
var Moo = require('MooTools'),
Class = Moo.Class,
Options = Moo.Options,
typeOf = Moo.typeOf;
exports.MyPlugin = new Class({
Implements: [Options],
options: {
name: ''
},
initialize: function(options){
if (!options) options = {};
if (typeOf(options.name) != 'string')
throw new Error("Ohmy!");
this.setOptions(options);
}
});
MyApplication.js
// Add path to MooTools module so every module can just require "MooTools" without specifying the exact path
require.paths.push('path/to/mootoolsjs/');
var MyPlugin = require('path/to/MyPlugin').MyPlugin;
new MyPlugin({name: 'Kid Rock'});
// We can still add all the MooTools objects to the global scope without breaking anything
require('MooTools').apply(GLOBAL);
new Class(..);
现在,您可以与其他人共享MyPlugin-Class,即使他们没有将MooTools对象放在全局范围内,它也将对他们有用。
注意:即使您没有将MooTools放在全局范围内,它仍会向本机类型添加扩展,例如String,Array和Function。 一旦执行“ require('MooTools')”,所有扩展都可以在任何范围内使用。 请注意,至少目前,所有模块都共享完全相同的数据类型。 没有沙盒数据类型。 如果在JavaScript中扩展本机类型与您的编码风格不符,则可能不应该使用MooTools(或JavaScript,因为它是该语言的核心功能)。 MooTools项目的目标之一是提供一种感觉自然且不会在库的核心语言和功能之间进行区分的框架。
为什么要“事件”? 异步吧?
JavaScript作为一种主要在客户端使用的语言,具有强大的异步功能。 这意味着大多数情况下,您会在应用程序中定义某些功能,当用户(一个真实的人)与网站内容进行交互时,该功能就会执行。 通常,您可以通过向DOM元素上的某些事件添加侦听器来实现此目的:
myElement.addEvent('click', function(){
// This function gets executed upon interaction
});
您调用addEvent方法并向其中添加一个函数,程序流程将正常继续,并且每当用户单击该函数时,都会异步调用该函数。 无论如何,您都不希望在执行此事件之前等待其他任何代码的执行。 您不希望click事件监听器被阻止。 这是Node.js的主要设计目标:不阻塞任何I / O操作,例如读取或写入文件,从数据库存储或检索数据等。这意味着您将在服务器端编写大多数代码传递函数(回调,事件侦听器),这些函数将在以后的时间(例如,操作结果准备就绪时)执行。 您可以在Node.js网站上找到一个简单的示例: http : //nodejs.org/
为了让您更好地理解,这是一些使用MooTools进行用户身份验证的示例代码:
var Moo = require('MooTools'),
Class = Moo.Class,
Db = require('Database').getDatabase(),
sha1 = require('Sha1');
exports.User = new Class({
initialize: function(name){
this.name = name;
},
authenticate: function(password, callback){
var user = this;
Db.open(function(db){
db.collection('users', function(err, collection){
if (err) return callback(err);
collection.findOne({name: user.name, password: sha1.hex(password)}, function(err, data){
if (err) return callback(err);
callback(null, data != null);
});
});
});
}
});
在您的应用程序中,您现在将创建一个新的User实例,并使用类似这样的回调调用authenticate方法
var User = require('User');
var sys = require('sys');
var instance = new User('david');
instance.authenticate('kidrock', function(err, result){
if (err) return; // handle database error
if (result) sys.puts('User authenticated');
else sys.puts('User does not exist');
});
sys.puts('Authenticating user');
此示例将打印出两条消息“正在验证用户”和验证的结果/成功。 该顺序取决于数据库的速度,可能会首先打印出“认证用户”。 您可以将其与setTimout示例进行比较
setTimeout(function(){
log('Bye');
}, 1);
log('Hello');
注意:以第一个参数为错误的回调样式与Node.js当前用于异步操作的方式保持一致。 在此之前,使用了“承诺”系统,但已将其删除。 高级实现可以从当前解决方案中抽象出来。 随时使用MooTools实施自己的回调/事件系统,并与社区共享:)
注意:CommonJS实际上将模块标识符指定为小写。 但是,我希望文件名以大写开头。 您将始终看到我在应用程序中执行“ require('User')”而不是“ user”。
为什么选择ServerSide MooTools?
存在JavaScript库的原因之一是缺少某些功能,尤其是在DOM级别,以及不同渲染引擎之间存在大量问题。 您在服务器端没有这些问题。 MooTools作为一个库,其工作水平比其他一些库低得多,因此它提供了有用的实用程序功能,我们认为这是JavaScript中所缺少的。 除此之外,即使有CommonJS规范,某些实现也与其他实现不同。 MooTools Core以及位于其之上的抽象层(如Deck( http://github.com/keeto/deck ))可以使您受益匪浅,并帮助您消除或减少在开发过程中可能遇到的低级问题。
除了MooTools提供的复杂而干净的类系统外,还可以编写可以在服务器端和客户端上都可以使用的抽象插件,而无需进行任何进一步的修改(例如,语言/内部化类,模式验证器等)。 )。 有关此方面的更多信息,请随时观看我在FOSDEM 2010上的演讲(25:00 +问答) 。
其他(非与MooTools相关的)东西
- 我曾经参与过的MongoDB驱动程序: http : //github.com/christkv/node-mongodb-native
- MooTools开发人员Guillermo Rauch的NodeJS WebSocket服务器: http : //github.com/LearnBoost/Socket.IO-node
- Nathan White和Guillermo Rauch在node-mongodb-driver上的ORM: http : //github.com/nw/mongoose
- 其他节点模块: http : //wiki.github.com/ry/node/modules
奖励:使用Node.js在TextMate中执行Node.js中的脚本
将此命令添加为脚本:
#!/usr/bin/env node
var sys = require('sys'),
spawn = require('child_process').spawn;
spawn('node', [process.ENV.TM_FILEPATH]).stdout.addListener('data', function(data) {
sys.puts(data);
});
像这样:
单击上面的图像查看大图。
关于克里斯托弗·波杰尔
Christoph是奥地利格拉茨科技大学的软件工程和工商管理专业的学生。 他是一位经验丰富的Web开发人员和MooTools JavaScript框架的核心开发人员。
Christoph的网站 • GitHub • Twitter • Forge
翻译自: https://davidwalsh.name/mootools-nodejs