seajs学习一天后的总结归纳

  公司项目最近需要将js文件迁移到seajs来进行模块化管理,由于我以前主要接触模块化开发是接触的AMD规范的requireJS,没有接触过CMD规范,而且在实际项目中还没有用过类似技术。于是,我非常兴奋的开始了seajs的学习,正好对模块化开发仰慕已久,终于有机会大展身手了!

  一开始总是有点曲折的,我照着玉伯的github上的教程一步步来,然后发现在我引入jquery的时候,require到的jquery竟然是undefined,经过一番摸索,我发现原来jquery是基于amd规范的,seajs官网的例子之所以能够成功引入jquery,是因为其jquery是采用cmd规范模块化之后的产物,而显然我从百度cdn上拿下来的jquery是没有经过该处理的。百度了一下,发现jquery想改造成seajs能引入的模块,有至少两种方法

 

  1.在jquery源码外层套上define,将其以cmd的规范模块化

  2.找到jquery最后几行的&&amd的条件,可以使用ctrl+f搜索amd即可找到,删掉该条件

 

  显然,第二种方法更为方便,改造之后,果然jquery能够正确引入了,不过据其他部门同事的反馈,改造后的jquery模块在某些情况下会出现莫名其妙的bug,据他说在ios上有bug,由于暂时没遇到bug,因此暂且还是先将jquery也封装为模块

  seajs的基本配置还是比较简单的,首先,在最前面先引入seajs本身,这应该是毋庸置疑的,然后开始配置seajs,这里我遇到一个坑,网上部分教程指出config可以使用data-config来引入,写在单独文件里,我愉快的这样做了,然后一直报错,发现路径指向错误,纠结了我半天,最后突然看到原来不知道从哪个新版本开始移除了这个属性,我的天,太逗了

废话少说,正式开始配置

1 seajs.config({
2    base:"../sea-modules",
3    alias:{
4         "jquery":"jquery/jquery.min.js"
5    }
6 });

以下即是seajs常用属性的用法:

base提供基础路径

alias是别名,用于将较长的路径简化 以上两个参数是最基本的参数,实际上还有以下几个参数

paths:用来统一路径前缀,适用于较长的外网前缀

preload:预加载部分模块,貌似已移除

debug:设置为true开启调试模式,在控制台输出一些错误警告

map:将某个路径映射到另一个,常用于在线调试,比如spm构建得到的一般有xx.js和xx-debug.js,此时可用map将.js映射为-debug.js,方便在线调试

vars:设置seajs自带变量,可用{变量名}来获取,常用于模块路径一开始不确定的情况,例如中文或英文,zh-cn或en,该变量是加在路径上的,src="../{变量名}/main.js"

charset:引用script文件时的charset属性,默认为utf-8,该属性可以为函数,具体值为函数返回的值

另外seajs.config函数可以运行多次,多次运行会自动合并配置的参数 配置结束后,开始写入口函数

seajs.use("../static/main"); 

 

这是最基本的入口,use方法用来引入模块,此处我引入了main.js,也就是入口js 由于seajs是异步加载模块的,所以这里还可以加入回调函数,传入一个形参,即可获取到main模块,接下来就可以调用main暴露出来的方法 配置完,写完入口,接下来就是重头戏,写模块了 seajs遵循cmd规范,要求每个模块需要按该规范风格书写 即

define(id,dependencies,function(require,exports,module){
   //前面两个参数,一个是当前模块唯一标志,一个是当前模块依赖的模块。正常情况下不必指定这两个参数,seajs会帮我们自动获取,第三个factory函数是模块的工厂函数
   //require用于获取其他模块,如:
   var a = require("moduleA");  //此处require内写的名称可以是具体路径,也可以是alias里定义的别名,一般写的是别名  
   //通过require语句执行了对应的模块函数,并返回该模块的module.exports对象
   //注意,除了返回对象外,也执行了该函数,比如,该模块里如果有一句alert(1)不在exports暴露的方法里,会在require调用的同时直接执行。
   a.fn();//获取到a模块后,即可调用a模块暴露出来的方法
})

以下是a模块,同样使用cmd规范

define(function(require,exports,module){
  var bb="no bb";
  exports.fn=function(){
  console.log(bb);
};
});

显然,用脚也能想到控制台会打印 “no bb”,  cmd规范里比较重要的概念就是使用exports来暴露属性或方法, 例如exports.a=3,exports.fnn=function(){}, 这样其他模块用require关键字获取到对象的同时,就能使用这些暴露出来的属性或方法了。

可能有人已经注意到了,我们的factory函数里三个参数,前面两个已经用到了,require用来获取模块,exports用来暴露模块,那第三个参数呢,有什么作用?

问得好!其实一开始我也很纳闷,这个玩意到底是干嘛的,经过一番研究,我大体上了解了这个参数 ,原来require获取到的模块实际上最后返回的是module,而调用方法也是通过module.exports获取到的 exports是module.exports的一个引用,至于为什么要拐个弯,我个人猜测有两个原因(作为一个初学者大胆的猜测,如果有误欢迎指正)

1.名字短,写的爽。好吧,我开个玩笑。

2.避免随意改动模块对象,这个才是重点,前面也强调了exports只是一个引用,其指向了module.exports的内存地址 但是引用毕竟是引用,修改引用是不会改动被引用的对象的。

举个例子说明一下。

module.exports=5;

exports=3;

此时require后返回的值就会是5,而不会是3,这就是引用和本身最大的区别。

关于这两个的区别有一个新手使用常犯的错误。好吧,我没犯过(得意中~) 某些场景里,我们频繁使用exports向外提供接口,可能写了多个exports.xxx=xxx 。

这个时候,初学者可能会想我可以这样写

exports={ 
  a:xxx,
  b:xxx,
  c:xxx
}

想想就激动啊,这样写多专业,就好像js面向对象里,给构造函数的prototype拓展方法和属性时,也会用到这种写法 。

然后,很不幸的告诉你,这种写法是错误的,至于原因嘛,还是刚刚提到的知识点,exports仅仅是module.exports的一个引用,改变exports的值并不会影响到module.exports。 所以你费尽千辛万苦简化的代码并没有什么卵用,最后require时引用的module.exports根本没有像你想的那样赋值 。

当然,这种写法的方向是正确的,确实可以简化代码,如果需要这样写,这里一般有两种写法:

方法1:

module.exports={ .... }

这种方法直接给module.exports赋值,一了百了。

方法2:

return{ .... }

利用return返回的内容默认也相当于传入了module.exports 。

到此为止,我们已经能够基本使用require来获取模块,exports来暴露接口。

但是,还没结束呢,在我学习的过程中,可不止引入了一个模块,这个时候,引入多个模块会有一个小问题。

比如

var a=require("modulessA");

var b=require("modulesB");

我引入了两个模块,一个modulesA,一个modulesB,但由于粗心,我写错了第一个模块的名字,此时获取a模块那一行会报错,从而阻塞后续代码的运行 这样会造成很不好的影响,要知道seajs的初衷就是尽量0阻塞 。

此处,seajs提供了require.async方法来异步获取模块,

var a=require.async("modulesssssA");

 此时,虽然该行会报错,但不会影响后续代码的执行,这就是异步加载带来的好处,使用async时还可以传入回调函数来指定加载完之后执行的逻辑 。

谈到异步与同步,突然又想起一个需要注意的地方 ,那就是module.exports的赋值必须是同步完成,而不能放在回调函数里,。

例如

setTimeout(function(){
  module.exports=...
},0);

  此时module.exports会变为 undefined 。

  在文章的最后,还提醒大家注意到一个性能问题,那就是seajs模块化项目之后,如果功能较多,大量的模块js加载会造成大量请求,这显然对项目性能是有影响的  。

  玉伯本人是推荐是用spm工具来压缩合并这些模块,这样所有的模块会合并到一个js里,适合项目上线使用 至于spm的具体配置,本人就不详细讲解了,百度一下,你就知道。

  听我啰嗦了半天,希望初学者能对seajs有充分的了解,我本人也是初次接触,这是我学习了一天之后总结出来的一些基础知识,适用于刚刚上手的朋友,至于大牛们,还请批评指正,毕竟我的理解也比较浅薄,难免有疏漏和表达不当的地方。

  最后还啰嗦一句! seajs和requirejs感觉最大的不同在于seajs是按需加载,用到的时候再加载,而requirejs是提前加载,提前就将用到的模块写在一个数组里一开始就加载好, 至于孰优孰劣,在下也不好评价。

 
posted @ 2015-09-02 09:12  饶总真坏  阅读(2022)  评论(4编辑  收藏  举报