转: 模块化开发框架seajs简介
JavaScript模块化开发库之SeaJS
SeaJS由国内的牛人lifesinger开发。目前版本是1.1.1,源码不到1500行,压缩后才4k,质量极高。
这篇会讲述SeaJS的一些基本用法,不会面面俱到,但会就个人的理解讲述官方文档没有提到的一些细节。
一、SeaJS的全局接口
SeaJS向全局公开了两个标识符: seajs 和 define。
如果你的项目中已经用了标识符seajs,又不想改。这时SeaJS可以让出全局的seajs。如
var boot = seajs.noConflict();
这时boot就相当于先前的seajs。
如果你的项目中连标识符define也用到了,也不想改。SeaJS是很宽容的,它的define也可以让出。如
var boot = seajs.noConflict(true);
较上面仅多传了一个true。这时全局的define也没了。这时需要用boot.define来代替之前的define。
用过jQuery的同学应该很熟悉$.noConflict方法,SeaJS的noConflict与之类似。
二、SeaJS的模块写法
SeaJS默认使用全局的define函数写模块(可把define当成语法关键字),define定义了三个形参id, deps, factory。
define(id?, deps?, factory);
这个define很容易让你想起AMD的唯一API:define函数。 或者说让人费解,导致搞不懂SeaJS和 RequireJS define的区别。
它们都有个全局的define,形参都是三个,且对应的形参名也一样,会误认为SeaJS也是AMD的实现。
事实上SeaJS和RequireJS的define前两个参数的确一样。
id都为字符串,都遵循 Module Identifiers。deps都是指依赖模块,类型都为数组。区别仅在于第三个参数factory,虽然类型也都是函数,但factory的参数意义却不同。
RequireJS中factory的参数有两种情况
a、和deps(数组)元素一一对应。即deps有几个,factory的实参就有几个。
define(['a', 'b'], function(a, b){
// todo
});
b、固定为require,exports, module(modules/wrappings格式)。
define(function(require, exports, module){
// todo
});
这种方式是RequireJS后期向 Modules/Wrappings 的妥协,即兼容了它。而SeaJS的define仅支持RequireJS的第二种写法:Modules/Wrappings。
注意:SeaJS遵循的是 Modules/Wrappings 和 Modules/1.1.1。这两个规范中都没有提到define关键字,Modules/Wrapping中要求定义模块使用module.declare而非define。而恰恰只有AMD规范中有define的定义。即虽然SeaJS不是AMD的实现,但它却采用了让人极容易误解的标识符define。
说了这么多,还没开始写一个模块。下面我们从最简单的开始
1、简单模块
define({
addEvent: function(el, type, fn){},
removeEvent: function(el, type, fn){},
fireEvent: function(el, type){}
});
这样就写了一个事件模块,这和写一个单例没有区别。更多的时候用该方式定义纯数据模块。它类似于
var E = {
addEvent: function(el, type, fn){},
removeEvent: function(el, type, fn){},
fireEvent: function(el, type){}
};
2、简单的包装模块
define(function() {
// 一些内部辅助函数
// ...
function addEvent() {
// ..
}
function removeEvent() {
// ..
}
function fireEvent() {
// ..
}
return {
addEvent: addEvent,
removeEvent: removeEvent,
fireEvent: fireEvent
};
});
您懂的,在这个匿名函数中可以做很多事情。最后只需公开必要的接口。它类似于
var E = function() {
// 一些内部辅助函数
// ...
function addEvent() {
// ..
}
function removeEvent() {
// ..
}
function fireEvent() {
// ..
}
return {
addEvent: addEvent,
removeEvent: removeEvent,
fireEvent: fireEvent
};
}();
3、NodeJS风格的包装模块
上面两种写法看不到一丝NodeJS风格(Modules/1.1.1),改写下与“方式2”等价的。
define(function(require, exports) {
// 一些内部辅助函数
// ...
function addEvent() {
// ..
}
function removeEvent() {
// ..
}
function fireEvent() {
// ..
}
// 使用exports导出模块接口,而非返回一个对象
exports.addEvent = addEvent;
exports.addEvent = removeEvent;
exports.addEvent = fireEvent;
});
可以看到与“方式2”区别在于:
1:匿名函数有两个参数require、exports。
2:导出接口不是return一个对象而是使用exports。
而exports不正是NodeJS的风格吗? 细心的同学可能发现这个示例中require参数没有用到,这正是下面要讲的。
4、有依赖的模块
SeaJS中“依赖”都需要使用require函数去获取,虽然SeaJS的define的第二个参数deps也有“依赖”的意思,但它是提供打包工具(SPM)用的。此外,SeaJS的require是作为参数传入匿名函数内的,RequireJS的require则是全局变量。 www.2cto.com
上面定义的是一个没有依赖的模块,以下是有依赖的模块。
define(function(require, exports) {
var cache = require('cache');
// ...
exports.bind = bind;
exports.unbind = unbind;
exports.trigger = trigger;
});
该事件模块依赖于cache模块,函数有两个形参require和exports。抛开外层的匿名函数及define,它就是标准的NodeJS格式:使用require函数取依赖模块,使用exports导出现有模块接口。
实际上在SeaJS中具有依赖的模块必须按“方式4”写,即必须是包装模块,且匿名函数的第一个参数必须是标识符 “require”。即可以把require当初语法关键字来使用,虽然它不是全局的。
下面我们看看匿名函数的参数require和exports的一些有趣现象
a、如果写的不是require,改成req会是什么结果。
define(function(req, exports) {
var cache = req('cache');
// ...
exports.bind = bind;
exports.unbind = unbind;
exports.trigger = trigger;
});
Firebug网络请求如下
会看到依赖的“cache”没有被加载,当然JS肯定会报错了。
b、只把匿名函数的形参改成req,函数内部仍然使用require。
define(function(req, exports) {
var cache = require('cache');
// ...
exports.bind = bind;
exports.unbind = unbind;
exports.trigger = trigger;
});
看网络请求
这次“cache”模块竟然请求下来了。
仔细看上面的匿名函数代码中require没声明,且形参req而非require。那
?
1 var cache = require('cache');
中的require从何而来?
看SeaJS源码可知,它的define函数中会取该匿名函数的toString,使用正则匹配解析出其中的“cache”(私有的parseDependencies函数)。
我们也看到,虽然cache请求下来了,却仍然报错,因为在执行阶段require是未定义的。因此写依赖模块时匿名函数的第一个参数必须为require且不能更改。
正因为使用factory.toString和正则解析依赖,因此require的参数不能是表达式,如
// require的参数不能是表达式运算
require("ui-" + "dialog");
也不能使用require的别名,如
// 不能将require赋值给另外一个变量
var req = require;
req("ui-dialog");
c、修改exports为expo
define(function(require, expo) {
var cache = require('cache');
// ...
expo.bind = bind;
expo.unbind = unbind;
expo.trigger = trigger;
});
运行是没有问题的。即第二个参数“exports”是可以自定义的。显然SeaJS不赞成改“exports”为其它,这样明显破坏了NodeJS风格(Modules/1.1.1)的模块规范---它们正是使用“exports”导出模块接口。但这点在SeaJS中却无法被强制执行,只能是人为约定。
5、混合写法的模块
上面已经介绍了各种情形中的模块写法。为了与NodeJS风格保持一致:使用require获取“依赖”,使用exports导出“接口”。SeaJS在获取依赖这一块做了限制,即必须使用require。但导出则不一定非得使用exports,即exports可以改为其它。甚至还可以直接使用 “返回值”。
define(function(require) {
var cache = require('cache');
// ...
// 使用返回值导出接口
return {
bind: function() {},
unbind: function() {},
fire: function() {}
};
});
我们知道在NodeJS中模块只能是一个对象。即总是往exports上挂方法。SeaJS中如果使用exports导出接口,那么也一样,模块也只能是JS对象。如果使用“返回值”导出接口的话,那么模块可以是任意的JS类型。如下将返回一个函数类型的模块。
define(function(require) {
var cache = require('cache');
function ad() {
//...
}
// 函数类型的模块
return ad;
});
三、SeaJS的加载方式
虽然它提供各种方式(同步异步)加载,最简单的莫过于直接在页面中写script标签。引入SeaJS后,入门多数时候就是seajs.use方法。
seajs.use有两个参数,第一个参数可以为字符串(模块名)或数组(多个模块)。第二个参数是回调函数。模块加载后的回调。回调函数的参数与第一个参数一一对应。
seajs.use('dom', function(dom) {
// todo with dom
});
如下将在回调函数中使用dom模块。当然它也提供了快捷方式data-main(同RequireJS)。
摘自 Snandy
转: 模块化开发框架seajs简介的更多相关文章
- SeaJS简介一:由来,特点以及优势
由来: 在软件开发过程中,模块化编程思想已经习以为常了,模块化编程不仅仅给开发团队带来效率方面上的好处,还能够让开发的项目或者产品维护成本大大降低. 那么,在WEB开发过程中JS脚本语言已经不可或缺了 ...
- .NETCore 千星项目模块化开发框架 SimplCommerce 详解
SimplCommerce 是 github 上过千星的.netcore 商城示例项目,本文详解他的模块化框架现实思路,其业务(如产品.订单)不作介绍.因作者文笔水平很差,它又很值得学习和推荐,就算不 ...
- 基于 Cocos2d-x-lua 的游戏开发框架 Dorothy 简介
基于 Cocos2d-x-lua 的游戏开发框架 Dorothy 简介 概述 Dorothy 是一个在 Cocos2d-x-lua 基础上发展起来的分支, 它去掉 Cocos2d-x-lua 那些过多 ...
- Prism for WPF 搭建一个简单的模块化开发框架 (一个节点)
原文:Prism for WPF 搭建一个简单的模块化开发框架 (一个节点) 这里我就只贴图不贴代码了,看看这个节点之前的效果 觉得做的好的地方可以范之前的文章看看 有好的建议也可以说说 填充数据 ...
- Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单、导航
原文:Prism for WPF 搭建一个简单的模块化开发框架(六)隐藏菜单.导航 这个实际上是在聊天之前做的,一起写了,也不分先后了 看一下效果图,上面是模块主导航,左侧是模块内菜单,现在加一下隐藏 ...
- Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务、WCF消息头添加安全验证Token
原文:Prism for WPF 搭建一个简单的模块化开发框架(四)异步调用WCF服务.WCF消息头添加安全验证Token 为什么选择wcf? 因为好像wcf和wpf就是哥俩,,, 为什么选择异步 ...
- Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天、消息模块
原文:Prism for WPF 搭建一个简单的模块化开发框架(五)添加聊天.消息模块 中秋节假期没事继续搞了搞 做了各聊天的模块,需要继续优化 第一步画页面 页面参考https://github.c ...
- Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单
原文:Prism for WPF 搭建一个简单的模块化开发框架(三) 给TreeView加样式做成菜单 昨天晚上把TreeView的样式做了一下,今天给TreeView绑了数据,实现了切换页面功能 上 ...
- Prism for WPF 搭建一个简单的模块化开发框架(二)
原文:Prism for WPF 搭建一个简单的模块化开发框架(二) 今天又有时间了,再改改,加了一些控件全局的样式 样式代码 <ResourceDictionary xmlns="h ...
随机推荐
- 跨浏览器resize事件分析
resize事件 原生事件分析 window一次resize事件: IE7 触发3次, IE8 触发2次, IE9 触发1次, IE10 触发1次 Chrome 触发1次 FF 触发2次 Opera ...
- linux学习历程
1.linux初步介绍:2.linux的第一次接触:3.linux用户管理4.linux常用命令(3600+个).5.linux下所有者,所在组和其他组的介绍6.linux下文件和目录权限机制 lin ...
- ios学习Day3
bool 数据类型 #define TRUE 1// #define FALAE 0 #define BOOL int Bool flag=1; bool型 实质上是 int型 c89没有提供 c99 ...
- Node.js模块os
OS 操作系统模块 os.hostname() 操作系统的主机名. os.type() 操作系统的名称 os.release() 操作系统的发行版本 os.uptime() 当前系统的时间 以秒为 o ...
- ongl 表达式
struts.xml简单配置 <!-- (默认false)设置ognl表达式是否支持静态方法 --> <constant name="struts.ognl.allowSt ...
- La=LaULb (单链表)
#include<stdio.h> typedef struct LNode { int data; struct LNode *next; }LNode,*LinkList; void ...
- Spring--依赖注入
Spring 能有效地组织J2EE应用各层的对象.不管是控 制层的Action对象,还是业务层的Service对象,还是持久层的DAO对象,都可在Spring的 管理下有机地协调.运行.Spring将 ...
- python初探-collections容器数据类型
collections容器数据类型是对基本数据类型的补充,简单介绍下计数器.有序字典.默认字典.可命名元祖.队列. 计数器(Counter) Counter是对字典类型的补充,用于追踪值得出现次数 c ...
- python自学笔记(十一)关于函数及书写格式
1.函数是抽象的第一步 1.1 有关高压锅 1.2 函数是抽象出来的结构,是总结,是方法 1.3 多用函数 2.如何定义函数 2.1 def是关键词, ...
- [转]IE和Firefox兼容性问题及解决方法
今天测试代码时,发现不少IE可以运行的ajax,但在FF中报错.IE和Firefox(火狐)在JavaScript方面的不兼容及统一方法总结如下: 1.兼容firefox的 outerHTML,FF中 ...