推荐一款非常好用的chrome番茄插件“红杏出墙”

由于天朝的GFW,使得网络工作者(尤其是我们程序猿)的创造力受到很大限制,不能与世界各地的同行交流。而免费的goagent对于不愿意折腾的人来说,显得心有余而力不足。况且,goagent的证书问题一直让人困扰。推荐一款简单易用叫做“红杏出墙”的chrome插件,打开通往精彩世界的大门!

QQ20150306-1

这个是我的邀请链接,通过下面这个链接注册并充值,你我各自都会增加10天会员时间哦。

http://honx.in/i/VL9sA0Q-2m1WoJrm

这名字取的还真形象~

我的前端MVC之路

大约十几个月前,了解到时下前端MVC之火爆,同事推荐我了解一下angular。当时也不是特别在意,只是稍稍阅读了一遍官方文档,并尝试了文档上的例子。其实当时也颇有震惊之感的,原来代码还可以这么写!看完之后,很兴奋,也确实想在实际项目中运用一下,但可能我对它的理解不够深入,真到了项目里不知如何下手。后来也就不了了之了。又过了不久,另一个同事推荐我了解一下backbone,我找了一份中文文档来阅读。在当时看来,我更喜欢backbone的代码组织风格,有条不紊,比较好理解。同样也手痒,想要找个实际项目试试手。但由于当时基本都是基于老代码做更新维护,没有实践的机会。于是我的mvc之路便暂时搁浅了。

直到2014年3月换到了现在的公司,一切都可以重新来过了。新公司头一件事是进一步研究seajs+spm,之前仅仅是用seajs,没有配合其使用打包工具spm。

从2014年3月到4月底,我疯狂地在网上查资料,学习的过程也是很坎坷,官方的文档不够全面,网上的资料还穿插着历史的各种版本,很容易搞混API。由于跟官方的构建方案不大一样,所以官方的教程行不通。后来我甚至直接通过旺旺联系官方支付宝的spm维护人员,好消息是,他点到了其中一个关键配置,使得整个构建行的通,我之前的博客里也有提到他。再之后的一系列摸索,都顺利的多了,非常感谢。后来通过与backbone配合,真正实现了我所设想的构建方案,并发布到了github上。

2014年4月底,来了一个电商类的新项目,正好是个机会拿我刚提炼出来的spm构建方案试试手。没有经过实践的东西始终还是会有缺陷的呀,进入到了实际项目,有许多问题就暴露出来了,经过不断的摸索,基本都逐个击破了。

电商项目告一段落后,另外一个平台类的项目接踵而至。这个项目的重量级更高,需要更加重视。此时我又开始考虑是否要使用Angular,查了很多资料,想起之前对Angular的了解,有一个阻断我选择它的观点是,“Anagular不能与其他库并存?!”(后来发现这是错误的观点!有可能当时误解了)那岂不是所有组件都要自己写?!但是一方面又对很酷的编程体验很是向往。最终考虑到浏览器兼容,放弃了Angular(对IE8及以下支持不好),选择了Backbone。网上说,其实backbone提供的东西并不多,只是在代码结构组织方面有一定积极的作用。在数据绑定,页面渲染等方面都需要开发者自己搞定。于是在网上找是否有合适的基于backbone的框架可用。后来找到了marionette,跟着教程做了一个页面,发现有各种layout,感觉用起来好累,也许是当初用错了方法。

后来想起了arale组件库,当初在支付宝的时候,用过一些组件,感觉蛮方便,而且arale也是以seajs为模块加载器。我把widget进步一包装作为我们的基类按需扩展,派生出了很多常用组件,而backbone仅仅作为业务单元的路由工具使用(想想有点对不起它- -)。在技术选型确定以后,以这套方案开发了几个月后,越发觉得arale的基类很强大,兼具继承,混入和AOP特性,而且代码里发现很多backbone的影子。

之后来了一个手机端项目,相对简单。正好大漠的《用AngularJS开发下一代web应用》看的差不多了,想来正好是个实践的机会。试用了一下yo官网的generator-angular,很给力!这次尝试给了我前所未有的编程体验,一个字~爽!

generator-angular是一个用于生成angular工程脚手架的工具,包括初始化工程,和在开发过程中新建指定的模块,比如可以根据指定名称生成directive,controller,filter等。另外它还提供了一个完备的gruntfile构建流程,基本能够应对很多项目类型,不过开发者可以根据自己需要进行更改,我就大大小小增删改了很多流程,这个gruntfile对其他项目也具有很大的借鉴意义。

之后又有一个之前已经开发好的后台管理系统,我们决定把它用angular重构一遍。这个项目就复杂一些了,不过我还是继续使用generator-angular

generator-angular引入的angular模块都是angular官方的,很可能满足不了业务需求,这时候需要我们自己找第三方的模块,或者自己开发组件。我找到了angular-bootstrap作为基本组件库,很好用,而且通过阅读它的源代码,我也了解了很多angular的原理。之后花了一些时间开了form组件和datagrid表格组件,过程确实比较艰难坎坷,指令的确是angular最难的部分。

随着此项目的进行,业务需求越来越复杂,form组件和datagrid组件也越来越庞大臃肿,这还可以忍受。我看到controller里一大段一大段的配置对象代码(比如200行的代码,有120行是form组件和datagrid组件的配置代码),着实有些不爽,而且对有些需求真的很难扩展。这是,倒是对当初不用组件开发的方式有点留恋了,优势是思路简单,复杂需求可以较方便的实现。但是手写那么庞大的视图(尤其是form),而且经常是重复或近似的代码也挺无力。于是我终于下定决心想写一个自己的generator,用“代码写代码”,完成乏味的重复劳动,实有一种“返璞归真”的赶脚!

花了一个多星期的时间,参照generator-angular,各种踩坑与摸索后,酝酿出了generator-ngstone(以我自己的外号命名- -),算是实现了我之前的想法,尤其是根据配置文件生成form代码与datagrid代码(包括view与controller)。下面这个是我的项目地址:

https://github.com/stoneChen/generator-ngstone

欢迎使用,欢迎关注,更欢迎提出宝贵意见!

这一路走来,也有一年的时间了,感觉自己还是有一番提升的,这一年没有白过~
未来,HTML5应用会更加火爆,特别是微信SDK的发布,或许应该在这方面下点功夫了。
另外对iOS也是有一番兴趣,尚未深入了解,不知以后的路会怎样。

–从cnblogs搬来,写于 2015-01-25 12:38

三个css3趣玩小试

演示地址:

http://jsbin.com/semeh/8

请使用chrome打开

1.类似于网易新闻客户端的loading效果,左边的圆圈

2.发散式心跳效果,右边的圆圈

3.youtub上,搜索进度条效果,点击start,顶部的进度条宽度达到30%~70%区间时(随机),js设置至100%,然后reload页面

–从cnblogs搬来,写于2014-04-28 22:08

seajs的require.async方法的transport问题

这个方法是用于在模块中异步加载其他模块的,类似于在页面上的seajs.use.

比如需要在特定条件下才去加载a模块,不必每次都加载,类似于下面这样的代码

1
2
3
4
5
if({{some_condition}}){
require.async('a',function(A){
//do something
})
}

但是这里有个问题,并非是async本身的问题.

spm打包的时候,并不替换这个方法里的模块别名参数,这样就不利于统一去管理了.

在官方githunb上提了issue,并没有给回应,看来有需要找时间研究下spm源码了.

从维护角度上考虑,还是暂时不用这个异步方法了,姑且牺牲一点性能

另外spm还有几个令我有点不满意的地方,希望有一天能通过自己的研究把它解决了

 

–从cnblogs搬来,写于2014-04-27 15:06

seajs模块加载与执行原理小记

本文仅讨论具名模块的情况,即通过spm打包出来的模块.

想起ID与路径统一原则,详见https://github.com/seajs/seajs/issues/930

今天又研究了下seajs源码,源码中并没有显式的判断id与路径相不相等,即没有类似如下的代码

1
2
3
4
5
if(id == uri){
mod.exec();
}

假定被加载的模块为a.js

step1:在加载a.js前,就创建并缓存了a.js的module实例A,key值为a.js的全路径,暂定为uriA

step2:定义好onload事件(这里ie又出来捣乱了),创建script标签插入head

step3:浏览器加载完a.js后,执行define方法跟踪代码到seajs内部,发现并没有做什么特别的事,只是调用了Module.save方法,id与路径的匹配即体现在这个save方法中.且看源码:

1
2
3
4
5
6
7
8
9
10
11
12
// Save meta data to cachedMods
Module.save = function(uri, meta) {
var mod = Module.get(uri)
// Do NOT override already saved modules
if (mod.status < STATUS.SAVED) {
mod.id = meta.id || uri
mod.dependencies = meta.deps || []
mod.factory = meta.factory
mod.status = STATUS.SAVED
}
}

第3行,获取缓存中的module,uri是模块中的id. 假如id与加载路径相等,那么这里可以获取到step1缓存的A,然后将factory等属性赋给A,结束 假如id与加载路径不相等,那么这里将获取不到A,会新创建一个新实例B step4:浏览器执行完a.js的代码,执行onload回调,进行一系列的属性操作(比如waiting和remain)和依赖模块的加载等等,这里有递归…需要花点时间才能看懂. step5:等step4的所有递归执行完,也即a.js及其所有依赖模块都已加载完,执行完,进入就绪状态,执行a.js的factory,这里的factory从A中获取,A通过uriA从缓存中获取. 如果step3中,a.js中的id与加载它的路径不一致,那么这里A中的factory将是undefined,所以你的factory方法就不执行. 以上是seajs处理模块的大致流程. 至于使id与路径一致,通常的做法是,use或require里的直接量字符串参数与模块里的id相等.比如

1
2
3
4
5
6
7
8
//html页面
seajs.use("app/start",function(){
// code
})
//start.js
define("app/start",["jquery/jquery/1.7.2/jquery"],function(require, exports, module){
//code
})

不过,这两个值通过seajs内部resolve过后能相等,也是可以的.

seajs之seajs-debug坑

最近遇到两个关于seajs-debug的坑

一个与preload有关,详情见https://github.com/seajs/seajs-debug/issues/15

一个与map时间戳有关,详情见https://github.com/seajs/seajs-debug/issues/16

截止本文章前,官方还未给出彻底解决方案.

本人经过试验,研究出一个勉强可行的办法

由于资源问题,没有启用combo服务器,所以本人将seajs-log,seajs-style合并到了sea.js中,追加在sea.js内容的末尾,且看作是一个”硬combo”.

一.preload问题:

 

解决方案是:

暂时避免配置preload,直到官方给出解决方案.

a.如果你的预加载模块”非预加载不可”,那么可以像seajs插件一样,将代码手动合并到seajs中,即追加拷贝代码

b.如果你的预加载模块只是一个页面公用模块,且不被其他模块依赖(其实这也谈不上预加载模块了囧),那么就把这个模块用seajs.use放在layout页面的底部就行了

ps:preload数组中的模块,要等第一次调用seajs.use才会被加载.

 

 

二.时间戳与source模块问题:

上代码,就全部清楚了

1
2
3
4
5
6
7
8
9
10
11
12
<script type="text/javascript">
var _ = '自定义变量';
</script>
<script type="text/javascript" src="underscore/underscore-min.js"></script>
<script type="text/javascript">
// Underscore对象
console.dir(_);
// 将Underscore对象重命名为us, 后面都通过us来访问和创建Underscore对象
var us = _.noConflict();
// 输出"自定义变量"
console.dir(_);
</script>

1
2
3
4
5
6
7
8
9
10
11
12
var pageDebug = (seajs.data.preload[0] == 'seajs-debug');//判断是否加载seajs-debug
seajs.config({
alias: {
"$": "jquery/jquery/1.7.2/jquery",
"$-debug": "jquery/jquery/1.7.2/jquery-debug",
"seajs-debug": "seajs/seajs-debug/1.1.1/seajs-debug",
"placeholders":"gallery/placeholders/3.0.2/placeholders"
},
map: [
pageDebug?function(){}:[ /^(.*\/app.*\/.*\.(?:css|js))(?:.*)$/i, '$1?t=20140421' ]//时间戳
]
})

这样,页面URL没有seajs-debug的时候,加载模块的url是带时间戳的,有seajs-debug的时候,source功能也正常了

 

–从cnblogs搬来,写于2014-04-21 18:15

构建seajs业务模块之grunt VS spm build

在最开始,我并不知道grunt可以构建CMD模块.(以下spm指代spm build)

当时正困惑于如何用spm方便的构建业务模块,后来看到@>twinstony (感谢@twinstony的分享)使用grunt-cmd-xxx插件构建了CMD模块,跟着demo自己做了测试,的确可以构建,但是有一个问题:

grunt 方式不能把依赖的外部css(比如非项目中的jquery插件css)打包进来,而spm可以.

其原因是,spm会根据别名配置依次(./sea-mpdules -> ~/.spm/cache -> -> 源)查找并下载依赖,最后将依赖的css依赖打包进来(通过seajs.imporStyle).

而grunt并没有做这件事情,也许可以通过另一个grunt插件完成.

另,include这个参数是针对js模块的,对于css 而言则是”all”

还有一个不是问题的问题:

用grunt方式构建,参数配置项太多了,有时候找问题不太好找.或者说是过于灵活了,有时使开发者们无所适从.

而spm把很多东西都封装好了,只需配置简单几个参数即可,符合seajs简单一致的设计原则.

其实spm底层也是调用了grunt-cmd-transport以及其他模块,但把一些繁杂的配置帮我们处理好了

之前对于spm的困惑是:

1.打包出的模块id只能是family/version/name么? –通常业务模块并不需要这类命名

2.打包出的模块路径只能是直接输出到dist目录下? –不能像grunt那样,构建出来的目录结构与源文件的目录结构一致么?

3.输出目录不能指定么,只能是dist么? –如果可以指定,就可以直接省略掉部署环节(本地部署)

经过摸索与咨询,得出一个可行的解决方案,感谢@popomore 的指点

示例代码下载链接:

https://github.com/stoneChen/seajs-backbone-test/tree/master

(以下所有spm命令均在sea-modules的上一层目录下,即js下执行,package.json设置在这里)

A.所有模块部署在项目中.

以下以一个简单的示例(需要对backbone有一定了解)演示.

上图是目录结构.

sea-modules是spm默认的模块安装目录名.

为了开发方便,不必每次查看效果都要spm build一番,在开发阶段不使用spm打包代码,除源上的已成熟的组件外(可通过spm install安装都本地),均使用匿名模块查看页面效果.

上图是我把seajs结合backbone做的一个demo.

等所有功能开发完毕后,现在页面引入模块文件是这样的

1
seajs.use('start.js');

接着,进入打包阶段.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
{
"family": "test",
"version": "0.0.1",
"name": "spmTest",
"spm": {
"idleading": "_output/",
"source": "sea-modules",
"output": [
"start.js",
"app/gettingStart.js",
"app/index.js",
"app/userlist.js",
"collection/C_userlist.js",
"model/M_gettingStart.js",
"model/M_index.js",
"model/M_user.js",
"view/V_gettingStart.js",
"view/V_index.js",
"view/V_user.js",
"view/V_userlist.js"
],
"alias": {
"$": "jquery/jquery/1.7.2/jquery",
"$-debug": "jquery/jquery/1.7.2/jquery-debug",
"jquery": "jquery/jquery/1.7.2/jquery",
"jquery-debug": "jquery/jquery/1.7.2/jquery-debug",
"select2": "jquery/select2/3.4.5/select2-zh-cn",
"select2-css": "jquery/select2/3.4.5/select2.css",
"select2-css-debug":"jquery/select2/3.4.5/select2-debug.css",
"underscore": "gallery/underscore/1.5.2/underscore",
"backbone": "gallery/backbone/1.1.0/backbone"
}
}
}

以上为package.json配置.

然后终端执行

1
spm build -O sea-modules/_output

如上图,然后_output目录就会被创建,相应目录结构的模块就会依次生成好.

然后改变页面上引入模块的路径,只需添加一个前缀即可:

1
seajs.use('_output/start.js'); //开发阶段是seajs.use('start.js');

 

之前开发好的功能,现在一样能够跑通,而且执行效率会更高.

下面,讲一下其中的注意点

package.json重点是idleading,source,output的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
"idleading": "_output/",
"source": "sea-modules",
"output": [
"start.js",
"app/gettingStart.js",
"app/index.js",
"app/userlist.js",
"collection/C_userlist.js",
"model/M_gettingStart.js",
"model/M_index.js",
"model/M_user.js",
"view/V_gettingStart.js",
"view/V_index.js",
"view/V_user.js",
"view/V_userlist.js"
],

1.idleading如果不配置,那么生成出来的id就是family/version/name这种形式,这个值怎么配,需要与source,output配合,最终达到的效果就是最终加载这个模块的路径(require或use的参数)与生成的id值要一致,至于为什么,请参考https://github.com/seajs/seajs/issues/930

2.source指定从当前目录下的哪个目录”查找”需要被构建的模块,这个官方文档有说明

3.output指定具体要被构建的模块,这里很重要,数组里的每个路径怎么写,生成出来的目录结构就是怎么样的.我曾经试过,将他们写成

1
2
3
4
5
6
7
8
9
10
11
12
13
"output": [
"start.js",
"app/*.js",
"collection/*.js",
"model/*.js",
"view/*.js"
]

 

但是前面提到那个css打包问题又出现了,而且模板文件也不打包了,好像是合并操作都失效了的感觉.这个就不知为何了,曾经提过issue,说是不支持这种写法.

4.执行build命令时指定 -O 的值,指定输出目录, 这个除了这种方式,不知道怎么在配置文件中解决,也提过issue,貌似需要自己实现.

5.还有一个奇怪的警告:

为啥会去找sea-debug?(后补:transport任务会转换source下所有文件,而sea-debug当然不是cmd模块,但是压缩版的sea.js也不是啊,怎么不警告?)

还有一点,下面那个152 files,貌似是对souce下的所有文件都进行transport了,但目标并不是全部,也就是说,实际上output的配置在transport后才起作用,是不是有些不太合理?

这样打包好后,结合seajs-debug,seajs-debug用于测试环境,线上调试,以及map控制时间戳,就能很好的管理所有模块了

ps:关于seajs的默认base,还有这种情况在:

本demo中即属于这种情况.

构建输出目录在sea-modules/_output下

访问backbobe-test.html查看最终效果

个人建议将工程代码在webstorm(@7.0+)中打开,webstorm会自动启动一个web服务,或者其他能够启动web服务的方式.

seajs-backbone作为根目录

B.业务模块部署在项目中,其他模块包括seajs本身及其插件部署在静态资源服务器见附件,构建时,只需将idleading做适当转换即可,总之要保证require或use时的路径和id一致

构建输出目录在sea-modules/_output2下

访问backbobe-test-outer.html查看最终效果

这种情况下需要启动两个web服务,一个webstorm的,一个静态资源服务器的,个人建议使用nodejs的anywhere启动.

接下来附上几幅效果图

A1.开发阶段:

A2.构建好后:

A3.构建好后,使用seajs-debug调试:

B1.公用模块外部部署,开发阶段:

B2.公用模块外部部署,构建好后:

B3.公用模块外部部署,构建好后调试:

代码下载地址:

https://github.com/stoneChen/seajs-backbone-test/tree/master

欢迎大家指出不正确或不合理的地方~

–从cnblogs搬来,写于2014-04-07 14:14

seajs+spm之再研究

好久没有用seajs了,之前对spm也只是一知半解,这些天再次拿起来研究.谈谈我的认识与理解.

声明:本文不适合对seajs完全不了解的同学阅读.对于想知道seajs来龙去脉以及spm相关的同学”可能”有帮助.对于我自己也是个梳理的机会.

一.seajs部分

1.seajs由来:

传统web前端的js开发,主要基于script标签的引入,一个文件一个script标签,或者对他们进行简单的压缩与合并,以减少http请求.

没错,我们以前都是这么干的,甚至现在还有很多人这么干.

随着这些年的发展,前端越来越被重视,逻辑越来越复杂,前端代码的维护变得越来越难.

2009年,nodejs诞生(nodejs是什么东西,可以自行google),其模块化的编程思想,给攻城师牛人们(包括但不限于前端攻城师)带来了很大的启发.特别是它的模块化依赖管理系统.工程师们想把这个依赖管理机制移植到浏览器端.但是浏览器端目前还没有高度统一的规范,更别说依赖管理机制了.于是工程师们绞尽脑汁,日思夜想.

不久后,requireJS诞生了,也的确火了,但它所坚持的原则规范并不被前面那些的大部分工程师们接受.

这时,我国的一位前端大牛也逐渐参与到模块化开发的研究中来.经过不断的虚心学习,借鉴,摸索,seaJS诞生了.他的作者是玉伯(–仰视,尊敬).

以上历史介绍,只是我简单的整理与理解.详细的可以参考玉伯本人的描述https://github.com/seajs/seajs/issues/588

2.seajs是什么,能做什么

它是模块加载器(也即js文件加载器),所有模块都可以通过它加载,而且很重要的是,它可以帮你很好的管理模块依赖.当你的页面上有十几二十个js文件的时候,用了seajs,你就会知道它有多么美妙.

3.怎么用

这里只讲个大概,教程随便google就一打.

所有的模块几乎(下面会讲为什么不是全部)都被define(function(require,exports,module){})这个全局函数包围,require参数用于表明被依赖模块,exports和module用于输出接口,让别的模块可以调用.对于开发者,按照它的规范写代码,就能很好的管理依赖了.

现在来讲,上面那个”几乎”.其实也是后来看aralejs(aralejs是基于seajs的组件库,其开放程度可以用包罗万象来形容,具体可自行google)上的模块才发现的(只是个别模块这样做).

对于我们后来写成的模块,seajs官网是推荐上面那种方式的,而且也没有什么问题.之前随着requireJS的推出,一些开源的类库或者框架(比如jquery,backbone等)都纷纷加入了requireJS的行列,后续的新版本默认支持AMD模块规范.这些开源代码为了兼容页面上有无requireJS的情况,并没有将所有代码都包在define函数中,而是判断页面中是否定义了define函数进行模块定义,类似于下面这样

1
2
3
if ( typeof define === "function" &amp;&amp; define.amd &amp;&amp; define.amd.jQuery ) {
    define( "jquery", [], function () { return jQuery; } );
}

而arale或者说seajs官方的做法是,可能为了兼容AMD,将其改成了

1
2
3
4
5
if ( typeof define === "function" ) {
define("jquery/jquery/1.7.2/jquery-debug", [], function () { return jQuery; } );
}

可能有其他更特殊的原因吧,还有json的模块更简单,直接在最后追加

1
2
3
4
5
define("gallery/json/1.0.3/json-debug", [], function() {
return window.JSON;
});

而underscore和bockbone就是按照”标准”,在最外层包上define.不是特别清楚为什么有这么些个做法.可以再看看其他被包装过的模块的都是什么样子的.

4.seajs的原理

这个可以参考我之前的文章(差不多一年前了囧,现在才来写这篇) : http://www.cnblogs.com/webstone/articles/3043119.html

5.引出下一节

其实说到这里,seajs以及它的模块都可以在浏览器里正常运行.那么为什么还要有spm呢? 且继续往下看

二.spm部分

1.spm由来:

各个模块开发测试完毕后,为了进一步提升页面性能,我们还要对代码进行压缩,合并操作.

于是问题产生了.

为什么压缩后,被依赖的模块不加载了?

为什么合并后的模块一个都不执行了?(新版本不执行任何操作,1.3.1及以前会只执行第一个模块)

对于第一个问题,这涉及到seajs模块依赖处理的原理,对于第一节中谈到的模块组织方式define(factory),seajs寻找依赖是依靠正则判断factory.toString()中的require关键字,如下代码片段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var REQUIRE_RE = /"(?:\\"|[^"])*"|'(?:\\'|[^'])*'|\/\*[\S\s]*?\*\/|\/(?:\\\/|[^\/\r\n])+\/(?=[^\/])|\/\/.*|\.\s*require|(?:^|[^$])\brequire\s*\(\s*(["'])(.+?)\1\s*\)/g;
...
function parseDependencies(code) {
var ret = []
code.replace(SLASH_RE, "")
.replace(REQUIRE_RE, function(m, m1, m2) {
if (m2) {
ret.push(m2)
}
})
return ret
}
// Parse dependencies according to the module factory code
if (!isArray(deps) &amp;&amp; isFunction(factory)) {
deps = parseDependencies(factory.toString())
}

而代码经过压缩后,require就不见了,自然就找不到依赖了.还有就是,这种依赖分析很消耗性能.

第二个问题,涉及到模块标识问题.其实上述的模块组织方式称为”匿名模块”.多个没有名字的模块合并到一起后,各自都返回了输出接口,那么对于这个”大模块”,应该返回哪一个接口呢?所以理想情况下每个模块都需要一个模块标识,这样就可以任何合并了.

其实seajs支持两种规范:

第一种就是第一节中的那种,叫做CMD规范,即define(factory)只传入一个callback.

第二种是给define传入三个参数define(id, deps, factory),id为模块标识,reps为依赖模块数组,第三个factory同上面那个factory.

这种模块属于Modules/Transport 规范 ,参考https://github.com/seajs/seajs/issues/242

第二种方式下,seajs的工作原理略有不同.它不再对factory进行正则依赖分析,直接加载deps中的依赖模块(数组中的字符串即模块标识,要与被依赖模块的id相匹配,或者与seajs.config别名中的映射后相匹配),这样一来,依赖处理的速度更快了,同时所有模块均可以任意压缩合并.

另外,路径与模块标识的问题可以参考这篇文章: https://github.com/seajs/seajs/issues/930

所以呢,为了使CMD模块(也即匿名模块),更加便于管理,包括模块的压缩,合并,打包,部署,甚至公用下载安装,spm就应运而生了.

2.spm是什么,能做什么

spm是CMD模块管理器,就像npm,其实spm本身就是nodejs的一个模块,负责CMD模块的打包发布删除等操作.当然对外输出的模块已经不是CMD模块了,是Modules/Transport 模块.

接着要说的是spm的源,就像npm有个代码集中管理的地方,甚至svn的代码仓库也有些类似.

当然,这个源可以是官方默认的https://spmjs.org/ ,也可以是私有源,搭建方法参考:https://github.com/spmjs/spm-yuan,然后需要通过修改配置文件中的soucre值来将spm的源地址改为私有源(默认为官方)

然后我们就可以开发我们的模块了.个人认为,最终生成的模块不一定要发布到源上.具有公共特性的组件类的模块才需要放到源上,业务模块放到项目中就好.只是build出来的id会看起来比较怪异,因为必须要与加载的路径一致.

3.spm的主要功能

nodejs和spm及其插件的安装这里就不说了,也是google一大把

我们从使用的角度,按开发顺序走下来

A.如果是开发一个公用组件,将来是要上传到spm源的模块,可以这么干(这种情况,除了源配置,我们暂时使用默认的配置信息):

1)spm init

打开终端,进入你将要开发组件的那个目录,执行

1
spm init

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{{newModuleName}}/ //这一层是你执行spm init前创建的
src/
{{newModuleName}}.js
{{newModuleName}}.css
examples/
index.md
tests/
{{newModuleName}}-spec.js
dist/ //这一层现在不会有,后面build完会出现,输出的文件在这一层下
README.md
HISTORY.md
package.json

然后你就可以在src下的js和css里开发组件了,这两个文件初始化了一小部分代码,在src下你可以自由创建文件夹与其他js,css甚至模板文件等,用于被依赖

2)spm build

组件开发完成后,在终端下,同样是在上一步的目录下,执行

1
spm build

然后,就生成了上一步看到的dist目录,下面有打包好的具名模块,其中如果有依赖,默认是会被打包进来的

3)接着开始测试模块

官网说,spm test以及spm totoro都是支付宝内部才能使用的插件,不推荐外部使用,我还没有深入研究过.

所以呢,我们只能暂时自己创建测试环境,不过也不太麻烦啦

4)spm publish

测试通过后,我们就可以把它发布到源上了,就像nodejs模块可以通过npm发布一样.

在终端,同样的目录下执行

1
spm login

按照提示,登陆你的源账号,如果没有账号则按照提示创建账号登陆

接着执行

1
spm publish

如果没有意外,就可以发布成功了

打开浏览器,访问你的源地址,就能看到你刚刚发布的模块了

其他同学就可以通过spm install /安装(即下载)你发布的模块了

B.如果是开发业务模块,按照我个人的观点,业务模块不需要被发布到源上,或者说不需要和公用组件放在一块

这里我想起一个误区,而且我之前就一直是这么认为的:

“源上的模块是可以被http访问到的”

走出误区:简单讲,源只是一个存储模块文件的地方,不提供模块的web服务.于是就有了”部署”这件事情.但是spm deploy也是支付宝内部插件,我也没深入研究.

通常大型项目,静态资源文件与应用服务是分离的,也即模块文件是存放在另一台服务器上的.

但是小型项目,放在一起也基本能够满足需求.

但好像没有看到有人将公用模块与业务模块分离的.经过个人简单试验,这样是行的通的,并打算应用在项目中.

之前一直在探索如何使用spm构建业务模块,但总找不到合适的资料,尤其是它要求的目录结构以及生成的id让我束手无策.后来看到有大牛使用grunt-cmd-transport构建模块,自己跟着试了一下,的确可行.但似乎有些问题,比如:依赖的css没有被打包进来.在咨询了官方的人员后,并没有给出我这些问题的答案,而是建议我用spm,并给出了一些方案.我又重新拿起spm开始尝试.终于研究出了可行的方案,相对公用组件的开发方式,只需要修改package.json中的一些配置即可,而grunt的配置实在太多,看的人眼花.再次感谢支付宝贯高(github @popomore)给我的建议

由于需要比较多的笔墨,我将在下一篇讲述如何使用spm构建业务模块.

–从cnblogs搬来,写于2014-04-02 10:44

关于IE文档模式

如果不设置DOCTYPE,IE会默认进入怪异模式

1
<input type=”hidden” value=”xxx” name=”action”/>
1
document.getElementById('action');

此句代码是可以生效的!即给document.getElementById传入name参数,查找元素

如果不设置DOCTYPE,设置了<META http-equiv=”X-UA-Compatible” content=”IE=edge” />的话,
文档模式会按照指定的变化

IE下有这种用法

1
form.elements('action1').value = 'xxxx';

其中form中有id或name为action1的input元素,即设置该input元素的值(若同时存在id和name为action1则value为undefined)

为干掉IE67举杯!!
用这个

1
<meta http-equiv=”X-UA-Compatible” content=”IE=edge,chrome=1” />

还有这个

1
2
3
if($.browser.msie && ($.browser.version < 8 || document.documentMode < 8)){
alert(‘get out!’)
}