引入SeaJS

作者:frank 发表日期:2015-08-31 12:07:06 更新日期:2016-08-15 19:57:45 分类:猿文色

摘要

在CVTOUCH开发中引入SeaJS

正文

  1. 前端开发中遇到的问题 以前如果要将某个功能抽象出来,比如Cvtouch商城的公共部分common.js,包括显示错误,打印错误,编码解码等,首先会自己找个名字,有意义并且不会冲突,可能是Common,然后将Common 关联到全局对象window.Common={},最后在Common下定义功能函数,Common.log=function(){},Common.encode=function(){}。这样做看似没什么问题,只要在HTML中引用common.js,那么所用的JS文件都可以通过Common使用公共函数log和encode。 但是真的没问题吗?起码现在有3个问题需要思考。

    • 命名冲突。假如某一天另一个项目组的小A看到我们的公共模块感觉还不错,想拿过去直接使用,而他的项目里面有个文件叫做Acommon.js,恰好也有个全局对象Common,即使小A在定义Common 时聪明的使用了window.Common=window.Common || {},但在HTML中引用文件时我们的common.js在Acommon.js之后,那么我们的定义方式window.Common={}就完全覆盖了小A的所有功能。即使我们也足够聪明,我们的log和encode也会覆盖小A的同名函数。
    • 文件依赖。common.js是依赖jQuery的,如果小A并没有引用jQuery,那就只能靠浏览器报错来手动解决依赖关系。小项目可能还比较容易解决,但是如果项目变大,依赖文件变多,那就麻烦了。
    • 都关联到window对象。浏览器唯一的JavaScript全局对象就是window,所以大家都把可抽象部分关联到window对象,导致window对象越来越大,极易产生命名冲突。
  2. 为什么要模块化? 为了解决上述问题。模块的封装可以很轻易的解决命名冲突,模块的引用可以很轻易的解决文件依赖。

  3. 主流的模块化工具 SeaJS和RequireJS,两者的主要区别:(参考 http://www.zhihu.com/question/20342350)

    • 两者定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为Rhino/Node等环境的模块加载器。SeaJS则专注于Web浏览器端,同时通过Node扩展的方式可以很方便跑在Node 服务器端。
    • 两者遵循的标准有差异。RequireJS 遵循的是 AMD(异步模 块定义)规范,SeaJS 遵循的是 CMD(通用模块定义)规范。规范的不同,导致了两者API的不同。SeaJS更简洁优雅,更贴近CommonJS Modules/1.1和Node Modules规范。
    • 两者社区理念有差异。RequireJS在尝试让第三方类库修改自身来支持RequireJS,目前只有少数社区采纳。SeaJS不强推,而采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
    • 两者代码质量有差异。RequireJS是没有明显的bug,SeaJS是明显没有bug。
    • 两者对调试等的支持有差异。SeaJS通过插件,可以实现Fiddler中自动映射的功能,还可以实现自动combo等功能,非常方便便捷。RequireJS无这方面的支持。
    • 两者的插件机制有差异。RequireJS采取的是在源码中预留接口的形式,源码中留有为插件而写的代码。SeaJS采取的 插件机制则与Node的方式一致:开放自身,让插件开发者可直接访问或修改,从而非常灵活,可以实现各种类型的插件。 综上所述,我们选择了SeaJS。
  4. 目前的状况 左图            右图 先看两张图片,左边是之前的JS文件结构图,右边是现在的JS文件结构图。可以看到,结构清晰了,用不用SeaJS其实都该这 样做。关键问题是SeaJS如何做到模块化的?与NodeJS使用的 Require非常类似,3个接口就可以搞定:

    • define:定义模块。
    • require:引用模块。
    • exports:导出模块接口。 我们看看右图中的modules下的Ajax.js是如何定义的:
      define(function(require, exports, module){ 
        //引入其他模块
        var $ = require(jquery);
        
        //定义模块
        var Ajax = {}; 
        
        //导出模块接口 
        module.exports = Ajax; 
        
        //模块接口定义
        Ajax.get = function(url, cb){
          $.get(url, cb); 
        };
        Ajax.post = function(uri, data, cb){ 
          $.post(url, data, cb);
        }; 
      });
      
      上述代码可以看到,在使用模块的时候只需要require一下,SeaJS会根据文件名查找相应的模块,然后赋值给指定变量,完全不需要担心命名冲突的问题,而使用require可以完整的解决文件依赖的问题,我们不需要在HTML中留意文件的依赖顺序,只需要在使用的时候在JS中require一下,SeaJS就会自动下载文件并执行。最后,完全与window全局对象没有任何关系。