blog icon indicating copy to clipboard operation
blog copied to clipboard

前端工程与模块化框架

Open fouber opened this issue 10 years ago • 95 comments

本文最先发表在 DIV.IO - 高质量前端社区,欢迎大家围观

不要再求验证码了,这个blog目前有800+人订阅,求验证没什么的很影响其他订阅者,可以在div.io上申请,定期会有同学发放的。。。


一直酝酿着写一篇关于模块化框架的文章,因为模块化框架是前端工程中的 最为核心的部分 。本来又想长篇大论的写一篇完整且严肃的paper,但看了 @糖饼DIV.IO 的一篇文章 《再谈 SeaJS 与 RequireJS 的差异》觉得可以借着这篇继续谈一下,加上最近spm3发布,在seajs的官网上又引来了一场 口水战 ,我并不想参与到这场论战中,各有所爱的事情不好评论什么,但我想从工程的角度来阐述一下已知的模块化框架相关的问题,并给出一些新的思路,~~其实也不新啦,都实践了2多年了~~。

前端模块化框架肩负着 模块管理资源加载 两项重要的功能,这两项功能与工具、性能、业务、部署等工程环节都有着非常紧密的联系。因此,模块化框架的设计应该最高优先级考虑工程需要。

基于 @糖饼 的文章 《再谈 SeaJS 与 RequireJS 的差异》,我这里还要补充一些模块化框架在工程方面的缺点:

  1. requirejs和seajs二者在加载上都有缺陷,就是模块的依赖要等到模块加载完成后,通过静态分析(seajs)或者deps参数(requirejs)来获取,这就为 合并请求按需加载 带来了实现上的矛盾:

    • 要么放弃按需加载,把所有js合成一个文件,从而满足请求合并(两个框架的官方demo都有这样的例子);
    • 要么放弃请求合并,请求独立的模块文件,从而满足按需加载。
  2. AMD规范在执行callback的时候,要初始化所有依赖的模块,而CMD只有执行到require的时候才初始化模块。所以用AMD实现某种if-else逻辑分支加载不同的模块的时候,就会比较麻烦了。考虑这种情况:

    //AMD for SPA
    require(['page/index', 'page/detail'], function(index, detail){
        //在执行回调之前,index和detail模块的factory均执行过了
        switch(location.hash){
            case '#index':
                index();
            break;
            case '#detail':
                detail();
            break;
        }
    });
    

    在执行回调之前,已经同时执行了index和detail模块的factory,而CMD只有执行到require才会调用对应模块的factory。这种差别带来的不仅仅是性能上的差异,也可能为开发增加一点小麻烦,比如不方便实现换肤功能,factory注意不要直接操作dom等。当然,我们可以多层嵌套require来解决这个问题,但又会引起模块请求串行的问题。


结论:以纯前端方式实现模块化框架 不能 同时满足 按需加载请求合并依赖管理 三个需求。

导致这个问题的根本原因是 纯前端方式只能在运行时分析依赖关系

解决模块化管理的新思路

由于根本问题出在 运行时分析依赖,因此新思路的策略很简单:不在运行时分析依赖。这就要借助 构建工具 做线下分析了,其基本原理就是:

利用构建工具在线下进行 模块依赖分析,然后把依赖关系数据写入到构建结果中,并调用模块化框架的 依赖关系声明接口 ,实现模块管理、请求合并以及按需加载等功能。

举个例子,假设我们有一个这样的工程:

project
  ├ lib
  │  └ xmd.js    #模块化框架
  ├ mods         #模块目录
  │  ├ a.js
  │  ├ b.js
  │  ├ c.js
  │  ├ d.js
  │  └ e.js
  └ index.html   #入口页面

工程中,index.html 的源码内容为:

<!doctype html>
...
<script src="lib/xmd.js"></script>   <!-- 模块化框架 -->
<script>
    //等待构建工具生成数据替换 `__FRAMEWORK_CONFIG__' 变量
    require.config(__FRAMEWORK_CONFIG__);
</script>
<script>
    //用户代码,异步加载模块
    require.async(['a', 'e'], function(a, e){
        //do something with a and e.
    });
</script>
...

工程中,mods/a.js 的源码内容为(采用类似CMD的书写规范):

define('a', function(require, exports, module){
    console.log('a.init');
    var b = require('b');
    var c = require('c');
    exports.run = function(){
        //do something with b and c.
        console.log('a.run');
    };
});

具体实现过程

  1. 用工具在下线对工程文件进行扫描,得到依赖关系表:

    {
        "a" : [ "b", "c" ],
        "b" : [ "d" ]
    }
    
  2. 工具把依赖表构建到页面或者脚本中,并调用模块化框架的配置接口,index.html的构建结果为:

    <!doctype html>
    ...
    <script src="lib/xmd.js"></script>   <!-- 模块化框架 -->
    <script>
        //构建工具生成的依赖数据
        require.config({
            "deps" : {
                "a" : [ "b", "c" ],
                "b" : [ "d" ]
            }
        });
    </script>
    <script>
        //用户代码,异步加载模块
        require.async(['a', 'e'], function(a, e){
            //do something with a and e.
        });
    </script>
    
  3. 模块化框架根据依赖表加载资源,比如上述例子,入口需要加载a、e两个模块,查表得知完整依赖关系,配合combo服务,可以发起一个合并后的请求:

    http://www.example.com/??d.js,b.js,c.js,a.js,e.js

先来看一下这种方案的优点

  1. 采用类似CMD的书写规范(同步require函数声明依赖),可以在执行到require语句的时候才调用模块的factory。
  2. 虽然采用CMD书写规范,但放弃了运行时分析依赖,改成工具输出依赖表,因此 依赖分析完成后可以压缩掉require关键字
  3. 框架并没有严格依赖工具,它只是约定了一种数据结构。不使用工具,人工维护 require.config({...}) 相关的数据也是可以的。对于小项目,文件全部合并的情况,更加不需要deps表了,只要在入口的require.async调用之前加载所有模块化的文件,依赖关系无需额外维护
  4. 构建工具设计非常简单,而且可靠。工作就是扫描模块文件目录,得到依赖表,JSON序列化之后插入到构建代码中
  5. 由于框架预先知道所有模块的依赖关系,因此可以借助combo服务实现请求合并,而不用等到一级模块加载完成才能知道后续的依赖关系。
  6. 如果构建工具可以自动包装define函数,那么整个系统开发起来会感觉跟nodejs非常接近,比较舒服。

再来讨论一下这种方案的缺点:

由于采用require函数作为依赖标记,因此如果需要变量方式require,需要额外声明,这个时候可以实现兼容AMD规范写法,比如

define('a', ['b', 'c'], function(require, exports, module){
    console.log('a.init');
    var name = isIE ? 'b' : 'c';
    var mod = require(name);
    exports.run = function(){
        //do something with mod.
        console.log('a.run');
    };
})

只要工具把define函数中的 deps 参数,或者factory内的require都作为依赖声明标记来识别,这样工程性就比较完备了。

但不管怎样, 线下分析始终依靠了字面量信息,所以开发上可能会有一定的局限性,但总的来说瑕不掩瑜。

希望本文能为前端模块化框架的作者带来一些新的思路。没有必要争论规范,工程问题才是最根本的问题。

fouber avatar Jun 14 '14 08:06 fouber

这个方式其实和 spm2 的打包基本一致,只是更进了一步,把完整的依赖关系都提取好了。

目前 spm2 以及 Arale 的方式是,a 模块依赖 b ,b 依赖 c 的话,打包 a 时,会把 b 和 c 的依赖都放到 a 的依赖数组里去,这样就不需要下载到 b 时才知道 b 的依赖关系了。

最终使用的都是下面的 Transported CMD 模块:

define('a', ['b', 'c'], function(require, exports, module){
  require('b');
})

对于浏览器模块来说,提前打包目前来说是大势所趋,线上的实时依赖分析由于现阶段浏览器环境的限制,只保留在调试阶段比较合适。这也是 spm3 对于 CMD 规范的态度。

另外,线下分析是可以根据 AST 去拿到准确的依赖列表,而不只是require的字面量

afc163 avatar Jun 14 '14 09:06 afc163

@afc163

不应该提取到每个顶级模块里,因为逻辑可能在任何地方执行异步加载,提取到框架配置级别会更合理一些,也更灵活。

另外,线下分析,什么实现都可以的,我比较深刻的理解AST,但是放弃他是因为正则的“不严谨”性,使得可以适用于各种混合语言的代码中,比如模板

fouber avatar Jun 14 '14 09:06 fouber

全依赖提取到每个顶级模块,这种方式其实不完全合理和准确。但我们不想维护一个完整的依赖列表(YUI最早这么做)的原因是因为工程上的复杂、对编译环境的高要求、以及难于移植。但这样做简单可靠,其实我们方案中的很多决策都是在追求这一点。

afc163 avatar Jun 14 '14 09:06 afc163

感谢~

afc163 avatar Jun 14 '14 09:06 afc163

@afc163

fis的整个设计原则就是围绕着这个核心来实现的,工具的实现会更纯粹一些,如果时间允许,我可以给出一个完整的demo,应该经得起考验的。

fouber avatar Jun 14 '14 09:06 fouber

@afc163

对于特别大的工程,并不是一个完整的表,而是要有“命名空间”的概念,将表拆分成多个命名空间。工具负责维护和提取每个命名空间下的表信息。这条路可以继续探索下去,相信以表为媒介,连接框架和工具以及规范,是比较合理的一个出路。

fouber avatar Jun 14 '14 09:06 fouber

大家的方案都差不多,目前来看有以下几种:

  1. 大家误以为的 seajs 方式:什么都动态分析、加载。(这种方式其实我们只用在开发调试时)
  2. 用工具生成 config 配置表的方式。类似 YUI、KISSY 采用的方式,有利有弊,配置表的维护并非那么轻松,特别是在阿里目前的模板技术背景,以及考虑 cms 区域的情况下。
  3. 用工具提取信息到文件本身的方式,线下打包好。Arale 在支付宝的实践,通过 spm 来完成,目前实践下来还可以,但依旧存在一些小痛。
  4. 用工具提取信息到文件本身,然后通过服务端(CDN源服务器)来实现自动打包的方式。目前阿里国际站在尝试,YAHOO 后来有部分业务线也走向了这种方式。支付宝还在考察观望。

@fouber 你真应该来阿里看看呀,很多时候,场景决定方案。FIS 很适合百度的场景,但拿到阿里的场景下,依旧还有许多路要走。

lifesinger avatar Jun 14 '14 12:06 lifesinger

@fouber 我之前也对构建系统的看法也类似,但以这段时间在苏宁看到的情况来说,这个构建系统太难建成,主要是部门划分和模块划分的不一致性。命名空间式的构建,要求的是互相之间没有交叉,理想状况是一个树形下去,但一交叉就完蛋了,要不然是命名空间的粒度过小,要不然是命名空间横跨部门,给构建带来很大麻烦。

@lifesinger 阿里的细节情况我不清楚,但估计是很类似的,所以我才逐渐明白sea里面有些细节的用意。之前没想到这些情况,没能理解为什么非要这么搞。以我之前公司的情况来说,是有大部门,也有统一的架构组,而且大部分产品是纯AJAX交互,所以连非静态模板的问题都很少有。

但是在网购型系统里,很可能顶部的购物车、支付模块等,不是来自本系统,而是来自其他业务部门,这些东西却非要集成在一个页面里,它们的公共项就很难处理。所以我理解阿里把模块拆得这么碎,然后用看上去很怪异的方式,在nginx那边搞combiner来合并,然后也正是为此,可能js会有乱序,必须晚期依赖。

xufei avatar Jun 14 '14 14:06 xufei

@xufei

在百度,对于大型系统,我们都不是整站构建的,而是按业务拆成了很多个子系统,每个产品会产生一张资源表,跨业务的依赖会引入对应产品库的表,每个业务子系统是独立构建上线的。举个例子:

site(站点)
  ├ common.git     #公共子系统模块
  ├ user.git       #用户子系统模块
  ├ message.git    #消息子系统模块
  ├ ...

每个子系统独立构建,并产生独立的表,线上部署的大致效果为:

www
  ├ map               #表目录
  │  ├ common.json    #公共子系统静态资源关系表
  │  ├ user.json      #用户子系统静态资源关系表
  │  ├ message.json   #消息子系统静态资源关系表
  │  ├ ...
  ├  template         #模板目录
  │  ├ common         #公共子系统模板
  │  ├ user           #用户子系统模板
  │  ├ message        #消息子系统模板
  │  ├ ...

每个子系统的静态资源id结构为: 系统名:资源id,比如common系统下的jquery代码,其id为 common:lib/jquery/jquery-2.0.2.js,所有的依赖关系可以记录在模板或模板所引用的js中的,模板中提供了静态资源管理和加载的接口,比如user子系统中希望使用message系统下的资源,其代码为(在user.git下的widget/user-info/user-info.php):

<?php import('common:lib/jquery/jquery-2.0.2.js');  ?>
<?php import('user:widget/user-info/user-info.js');  ?>
<?php import('user:widget/user-info/user-info.css');  ?>
blablabla

模板中的import函数,会在运行时读取资源表来实现静态资源按需,资源表中也记录了子系统内代码的合并情况,可以在模板运行期间计算静态资源的最优组合(带宽、请求数等)

每个系统独立构建,只有运行时的交叉引用,不会出现整站构建的情况

fouber avatar Jun 14 '14 15:06 fouber

@lifesinger

@fouber 你真应该来阿里看看呀,很多时候,场景决定方案。FIS 很适合百度的场景,但拿到阿里的场景下,依旧还有许多路要走。

那个,阿里好像刚收购了uc,你们已经在一家了……

xufei avatar Jun 14 '14 15:06 xufei

@lifesinger

我非常确信,支付宝在生产中使用的策略是更适合支付宝业务现状的解决方案。

写文章是想为中小企业应用模块化方案提出一些思路。现在比较堪忧的是中型团队对seajs的理解和应用现状。

以松鼠团队这边为例,之前使用seajs一直是放弃按需加载的,没有使用spm,因为规范不一致问题,所以自己实现了一套比较粗糙的打包方案,放弃了按需的能力,all-in-one.js。但面对移动端spa这种对按需、请求合并要求比较高的应用场景,会比较痛苦。

seajs作为业界广泛使用的模块化框架,可能还需要给出比较合理的生产应用的指导。

这是一项非常有难度的工作,因为即便是支付宝目前的解决方案,相信也不能推广给业界使用,因为它极有可能跟支付宝业务有着很深入的整合,包括提到的cms系统。

所以本文想阐述的就是这样一种适用于中小企业的模块化解决方案,确实不能涵盖全部使用场景,也没有关心具体哪种规范,均以生产需要为优先。

对于复杂体系的工程化改进真心是非常痛苦且充满挑战、并要承担巨大风险的工作。就规模而言,我觉得即便在熊掌公司所有经历过的工程化改造团队都没有过支付宝这么大规模的,所以确实不敢妄言,我非常赞同 场景决定方案,也坚信前端工程没有通用的解决方案这一事实。所以,除非亲身经历,亲手解决,否则每种方案总有 不适用的场景

另外,并不是fis适合熊掌公司的场景,而是基于fis实现的 fis-plus 适合,现在在松鼠团队同样基于fis做的 scrat 及其生态是适合松鼠团队业务形态的。

模块化框架,作为前端工程的 重中之重,是应该被反复锤炼和完善的,而且以我现在的认知来看,模块化框架非常有必要 每个团队根据自己的业务形态单独设计和实现一套。为cms模板服务的模块化框架、为spa服务的模块化框架,为小型项目服务的模块化框架、为大型系统服务的模块化框架,都有各自不同的问题域,实现上会有很大差异。由于模块管理本身逻辑很简洁,所以自己实现的收益是大的。

fouber avatar Jun 14 '14 15:06 fouber

楼主在前端工程化上的见解和造诣的确值得我长久学习,现实工作中的项目没有尝试的机会,很多进步的方案都是浅尝辄止甚至读读而已,希望将来有机会在前端工程化能有更多机会尝试。另外,楼主对Browserify这个解决方案怎么看?个人感觉,all in one简单粗暴,但未来可能有很大前景。

chuguixin avatar Jun 20 '14 13:06 chuguixin

@chuguixin

有大概扫过 Browserify ,没有在实际生产中应用过。这篇文章提到了,前端工程的核心是模块化框架,实践总结的是,模块化框架会关联工具、规范、部署等问题的,所以,原则上讲,选择了一种模块化框架,就要选择其配套的工具及规范,类似选了seajs,就要接受spm,接受了require.js,要接受它的r.js一样。当然也可以自己DIY工具,但有些规范基本上是天生定义好了的。

所以模块化系统设计上,我比较推崇自己diy,包括框架和工具,我在文章的末尾中也提到了,不同的场景会有不同的模块化需求,完全通用的可能性不大。

关于all in one,相信是因为不能同时做到按需、合并请求才不得已选择的结果,aio(all in one)的模块化体系,在demo层面看不出有什么问题,这是非常具有迷惑性的,等到项目用上了,达到一定规模了,才会发现这种方式的弊端:

  1. 对于传统PC,aio之后,跨页面之间共享缓存将失效。比如有A、B两个页面,A页面用了 a, b, c, d 四个模块,B页面用了 a, b, e, f,对每个页面进行aio,那么,用户在访问A页面之后再访问B页面时,重复下载了 a, b 这两个资源的内容,使得性能变差。
  2. 对于SPA应用,aio之后,首次渲染会性能变差,SPA有很多虚拟页面,不应该放到首次去加载,而是按需的,首页需要什么资源就加载什么,后续hash跳转加载页面才再做进一步的请求,这样比较合理
  3. aio还会引起资源本身的缓存失效率提升。比如一个aio中使用了 a, b, c, d 四个模块,每个模块因业务开发而需要修改的概率是p,那么aio这个文件本身被触发修改的概率就是 1- (1-p)⁴ ,假设p是20%,那么aio文件发生修改的概率将达到60%,每次修改,都会导致原来用户浏览器中的aio缓存失效,那么一个aio里合并的越多,发生修改的概率就越高,最后带宽浪费也就越多。

所以,根据实践总结,合理的打包方案应该是:

  1. 经常修改的文件极少修改的文件 分开打包,可有效提升缓存利用率
  2. 多页面共用的文件极少页面会用的文件 分开打包,可有效提升用户跨页面浏览的缓存命中率
  3. 支持按需加载的动态合并 可有效提升SPA应用的展现性能

这三条原则,本身也有一些矛盾的地方,最终确定的打包方案应该是根据业务权衡的。当然,我可以补充一条:

当且仅当业务规模很小,缓存命中、按需加载收益不明显时,aio的方式才因为没那么矬而不被察觉其劣势。

个人觉得,市面上这些模块化框架及其配套工具比较坑爹,demo把缺点隐藏的很好,上手很happy,吃亏的是后面大规模应用。

fouber avatar Jun 20 '14 14:06 fouber

@fouber 非常对!aio是一条极端的路线,让多页面的站点缓存很鸡肋了,而让单页面的应用又失去了按需加载。从amd/cmd最开始的动态加载到后来按照规范实现服务器端合并再到现在browserify的一刀切,虽然实践不足,但是经过几个项目也很认可具体业务具体分析,现实往往比理想差,而且差很多。 与本文关系不大的是,browserify的想法貌似很大,尝试为很多node的包做浏览器的兼容,我感觉这个唯一的意义就是让node的包可以直接运行在浏览器,但是好像真正对于生产的意义还有待观察。拙见。

chuguixin avatar Jun 21 '14 09:06 chuguixin

@chuguixin

... 让node的包可以直接运行在浏览器 ...

问题是这样的包到底有哪些?好像实际业务中基本没有。曾经有一些框架说“让js在前后端都能跑”也是扯淡,看过某些所谓前后端能跑的js,其实都是这样的代码结构:

if(runAtServer){
    //do something in server
} else {
    //do something in browser
}

是的,输出到前端的代码携带了一坨不需要的逻辑分支,这对于要求低带宽的前端来说好矬逼,而且很容易暴露server端的敏感信息。

我在另外一篇blog中也回答过这句话,照搬过来。

fouber avatar Jun 21 '14 09:06 fouber

@fouber 我认为,这个与

if(runAtServer){
    //do something in server
} else {
    //do something in browser
}

还是不同的。browserify目标应该是形成一个小社区,提供一系列类似于shim的实现,这里,也就是我们将来的业务代码不需要在上面的判断,或者说我们已有的node代码不需要修改直接在浏览器可用。关于这个思路,我的一点看法是:

  1. 我们实际开发中会有这样的需求?比如,我们会在浏览器端需要stream吗?
  2. 即使我们有部分需求,但是部分模块根本无法做shim,比如fs,这也让这个思路显得有些坑坑洼洼。

边走边瞧吧。

chuguixin avatar Jun 21 '14 17:06 chuguixin

@fouber 还是有这样的模块的。比如md5、hmac等用到的crypto模块。

hax avatar Jun 22 '14 11:06 hax

@chuguixin fs模块是有的。还有模块可以把所有readFileSync的常量调用全部替换成直接内嵌文件。 另外,stream在浏览器里也会用的。比如websocket是可以传输二进制的。只不过从我的角度看,将来未必是统一在node api上,而是统一在 html5 api 上。比如未必是统一在 fs 模块,而是统一在 File API。

hax avatar Jun 22 '14 11:06 hax

@hax 嗯,首先,关于浏览器端的stream,只是提供类似的api吧?或者说,html5后续有类似的想法在浏览器端实现一个原生的stream?关于,fs模块,应该是我的失误,我没看到,就误以为没有了。 关于api的统一,我一直以为browserify目标是在浏览器端实现一套node的api。原因是看到browserify-handbook

browserify is a tool for compiling node-flavored commonjs modules for the browser.

这个是我个人的理解。如果是统一到html5的话,那应该是我理解错误了。如果是:

比如未必是统一在 fs 模块,而是统一在 File API。

这样的话,意思是node的fs的api会修改?应该也不会吧。难道是我直接就把人家的初衷理解错误了?

chuguixin avatar Jun 22 '14 14:06 chuguixin

@chuguixin See https://dvcs.w3.org/hg/streams-api/raw-file/tip/Overview.htm

browserify 的初衷确实是让你依赖node标准库的程序也能在浏览器上用,因此它提供了几乎所有node api在浏览器上的对应版本。

但是这并不表示你的程序必须用node的模块。因为现在 html5 的 API 已经非常丰富,特别是,大量API倒过来在node中有polyfill。举例来说,网络通讯我就不用node的http模块,而用 xmlhttprequest 模块,它与浏览器中的 XMLHttpRequest API 一致。

node的fs模块自然不会改,但是不代表你必须用它啊。你完全可以倒过来用File API,用DOM storage,它们都有node上的实现。所以,browserify虽然初衷是让你可以用 node api 写程序跑在浏览器中,但是它只是一个工具,我们也完全可以反其道而行之,利用它让我们写出使用 html5 api 的程序,同时可以跑在浏览器和node中。

hax avatar Jun 22 '14 18:06 hax

@hax 看到这个Draft了,谢谢,感叹一句真是跟不上节奏啊。 我理解你的意思是说,我们将来使用browserify的方式很可能是反向的?也就是node可能有一些开源的包去兼容w3c的标准化api?

chuguixin avatar Jun 23 '14 02:06 chuguixin

@hax @chuguixin

我也学习到了,多谢。确实感觉nodejs的包向html5的api靠拢更合理一些。在浏览器中使用nodejs的api感觉略牵强,但在nodejs提供一致的html5的API,就很舒服了。

fouber avatar Jun 23 '14 03:06 fouber

不仅是感觉舒服,而且我觉得也是合理的。因为API统一作为抽象层总是要付出代价的,关键这代价放在哪里。浏览器端与服务器端(node)比,显然是浏览器端对任何成本更加敏感——比如看看npm的树策略和bower的平面策略就可以看到不同的选择方向。服务器端多数情况下其实无所谓你包了几层——反正node本来的强项也是io密集应用,而API统一的成本是间接调用也就主要是cpu消耗,相比较而言对性能影响估计可忽略不计。至于多的代码占用空间啥的,更是如此。

hax avatar Jun 23 '14 05:06 hax

@hax 能说明一下browser端程序跑在server端的需求情况么 browser端的程序大多与dom相关并且有用户参与交互,跑在server端的意义何在呢。 如果只是单纯为了使用html5的api,感觉有点为了统一而统一啊

sapjax avatar Jun 23 '14 05:06 sapjax

@sapjax 我举个工作中的例子。最近我们上了一个在线聊天的功能,使用的是某云服务提供的基于Web Socket的chatting service。他们暂时只有iOS和Android的sdk,所以用于网站的sdk是我们自己写的。在这个项目中我就选择尝试了前述用browerify但是统一于html5 api的方式。主要的原因是两个。第一是这强迫我们必须把界面(大量dom操作,只在浏览器中测试)和通讯和协议层(websocket、ajax以及少量dom storage部分,大部分在Node.js环境中测试,然后通过browerify打包后再在浏览器环境中测试)分离,负责sdk开发的可以专注于协议本身的问题,和第三方的接口也比较方便。第二是后续我们服务器端也需要直接调用云服务,这个时候就直接共用了相同的sdk。

所以这样的需求不见得很多,但是还真的是存在的。

其他的典型例子,比如经典的表单验证。如果能统一到 html5 的 validation API,会方便很多。

hax avatar Jun 23 '14 08:06 hax

总觉得到最后,用哪个模块化方式,也是要根据业务场景来定。amd和cmd都不是银弹,就算你说的模块化管理新思路,要维护字面量列表,在某些场景下,也是麻烦的。

wushanchao avatar Jun 25 '14 09:06 wushanchao

@wushanchao

维护列表是工具做的啊

fouber avatar Jun 25 '14 11:06 fouber

关于 维护框架级别 依赖表 我更多支持seajs方案,前端模块标准化已然是一个大趋势,在阿里系web page、web app、移动端应用中,模块化颗粒度越来越细,应用场景也越来越复杂多变,直接 ransported 模块 灵活度更高、稳定性强、成本也小!

shmnhdouble2013 avatar Jun 26 '14 15:06 shmnhdouble2013

@fouber 受益匪浅

kkdashu avatar Jul 01 '14 01:07 kkdashu

谢谢。忍不住想吐槽,什么时候Github的文字会变成emoji了。。

image

airyland avatar Jul 22 '14 08:07 airyland