记录:sea.js和require.js配置 与 性能对比

最近有点忙,很久无写博客,记录一下之前的配置require.js和sea.js的配置.(有误有望提出

require.js

文件目录  

/app(项目使用js)

/lib(require.js jq存放处)

/plugin(框架对应的功能插件)

require.js比较方便在于,不是amd的模块组件可以通过配置文件进行依赖配置.而且异步加载js 页面加载速度是杠杠的.当然会有点问题的,网速过慢的话,可能会出现js,timeout(可以通过配置解决.

config.js (配置文件)

// 配置信息
requirejs.config({
    // 模块的基准路径
    baseUrl: 'js/lib',
    // 映射不直接放在baseUrl下的模块名
    paths: {
    app: '../app',
    plugin: '../plugin',
    jquery: 'jquery-1.9.1.min',
    lazyload: '../plugin/jquery.lazyload.min',
    base: '../',
    controller: '../app/controller',
    model: '../app/model'
    },
    // 为没有使用define()的模块声明依赖关系
    shim: {
    lazyload:{
        deps: ['jquery'],
        exports: 'lazyload'
    },
    'plugin/placeholder':{
        deps: ['jquery'],
        exports: 'placeholder'
    }
    },
    // 设置请求超时时长,设为0时表示不限超时时间
    waitSeconds: 0
});

  官方的文档存在一个 indexMain.js 请求对应的首页js模块 index.js.(大概就以下的感觉)

require(['config'], function () {})

请求 ([依赖模块名称],回调());

require(['config'], function () {
  require(['app/index']);
});

  然后才在这个index.js模块上对各种模块进行操作,初始化等等操作

define(['jquery',], function($){});

定义模块 ([依赖模块名称],回调(模块返回参数变量));

define(['jquery',], function($){
    $(document).click(function() {
        alert($);
    });
});

  感觉有点多余,所以的采取的配置是这样一回事的直接一个index.js就 直接请求对应的模块为index服务了.

require(['config'], function(){
	require(['jquery','../common'], function($){
        $(document).click(function() {
          alert($);
        });
    });
});

  requirejs也支持CMD写法(js里面写require(**)请求js),忘记在哪里看了一篇文章,requirejs 是通过把CMD转换成require(['jquery','../common'])这种模式.所以就不在探究.

sea.js

  好奇心作怪,个人还是测试了sea.js在开发中的使用.一样是模块加载,由于是cmd,懒加载模式下,逻辑思路感觉比较合理?

个人觉得sea的文档比较混乱,require的就比较齐全了.具体自己体验...

文件目录

/app (项目使用js 控制器)

/sea

  /seajs (seajs seajs 配置文件)

  /jquery

    /plugins

  /plugins

针对上次require js挖的坑进行了一次优化目录路径 

旧版本seajs 可以通过<script src="./sea.js" data-main="./init"></script> 引进模块控制器,(过去了的旧demo会有这个)

现在是使用 seajs.use([],function(){})来调用模块控制器

seajs.的配置文件需要使用标签引进,<script src="./config.js"></script> 

config.js

/**
 * Created by moki on 2015-5-16.
 */
var seaUrl=location.protocol+'//'+location.host;
seajs.config({
    // Sea.js 的基础路径
    base: seaUrl+"/res/global/js/sea",
    // 路径配置
    paths: {
        'tuonews_reception': seaUrl+'/res/app/tuonews/reception/default/js',
        'tuonews_user': seaUrl+'/res/app/tuonews/user/default/js',
        'jq':'jquery',
        'jq_plugins': 'jquery/plugins',
        'plugins':'plugins'
    },
    // 别名配置
    alias: {
        'jquery': 'jq/jquery',
        '$': 'jq/jquery'
    },
    // 调试模式
    debug: true,
    // 文件编码
    charset: 'utf-8'
});

  在页面引进 sea.js 和config.js后 再调用对应的模块控制器,

  在页面中加入对应代码调用模块

  seajs.use(['jQuery','common'],function($){})

                   调用模块      回调

  官方建议回调和模块中不使用seajs.use来请求模块调用,而是使用require;

/**
 * Created by moki on 2015-5-12.
 */
define(function(require,exports,module){  //赋值变量 请求模块
    var $ = require('jquery');
    $(function (){   //根据节点判断是否需要加载对应的js插件 
   var banner = $('#banner');
       if(banner[0]) {
      require('banner');
      banner.banner();
    }
    });
});

  seajs的模块化插件 需要改变当前插件 

jq ,新版jq本身支持amd模式,在seajs下只需把

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

改为

if ( typeof define === "function") {define( "jquery", [], function () { return jQuery; } );}

至于低版本jq 需要定义jq

define('jquery',[],function(require, exports, module){
	//这里放jQuery源代码
	module.exports = jQuery;
});

制作cmd jq插件

define('jquery',[],function(require, exports, module){
        var $= require([jQuery]);
	//插件代码
        $.fn.a = function (){}
        //插件代码
}); 

 

优缺点

基本上seajs和require配置完毕;现在开发中我遇到的问题;

require由于项目运维方向问题,导致整个项目还未正式投入实战当中,估计还会存在坑;

在开发的逻辑思路来说,

seajs是采取 按需请求js, sea的思路会比清晰,(还未压缩js前),控制器不多的时候,通过一个js 来控制多个页面,优势在于可以缓存这个js文件,

requirejs,是先加载依赖文件再编译,所以要多个不同的页面 使用同一个js控制,会令到不同页面都会把大部分无用js加载过来,所以还是有必要拆分多个js.在requirejs下采取cmd模式加载模块,结果如何?(应该本身就不建议这样写,只是方便过渡,所以还是不在考虑当中);

这么一看好像seajs更适合开发?

大家常说requirejs比seajs要快?

seajs会在模块加载后再执行 模块中的require,然而requirejs 是在加载完毕后会执行对应的js

想深究 究竟如何的人 请务必细心看完引用文章

 

来源      http://www.zhihu.com/question/20342350

     http://qingbob.com/let-us-talk-about-resource-load/   让我们再聊聊浏览器资源加载优化

个人认为玉伯聊的比较抽象和宏观,比如标准,概念,机制,社区等。最近一直在做性能优化方面的工作,就自己的经验谈谈require.js和sea.js的异同。这两个加载器和标准没有优劣之分,这里指出的只是差别。具体还是要根据实际情况进行选择

在开始之前我已经假设你对requirejs与seajs语法已经基本熟悉了,如果还没有,请移步这里:

- CMD标准:
- AMD标准:


对比require.js与sea.js,某种意义上说就是对比AMD标准与CMD标准,个人认为两个类库在模块和factory的书写上其实无太大差异,差异在于

  • 模块的加载
  • factory函数的执行。

这里提前说一句,如果你的网站再上线前习惯把所有的模块打包压缩,其实requirejs和seajs并无太大差别。

## 加载差异

这一小节请允许我照搬一个帖子

玉伯转过的一个SeaJS与RequireJS最大的区别,这个帖子原始(不包括后记)的结论是

RequireJS你坑的我一滚啊, 这也就是为什么我不喜欢RequireJS的原因, 坑隐藏得太深了.
 
SeaJS是异步加载模块的没错, 但执行模块的顺序也是严格按照模块在代码中出现(require)的顺序, 这样才更符合逻辑吧! 你说呢, RequireJS?
 
而RequireJS会先尽早地执行(依赖)模块, 相当于所有的require都被提前了, 而且模块执行的顺序也不一定100%就是先mod1再mod2
因此你看到执行顺序和你预想的完全不一样! 颤抖吧~ RequireJS!

因为他认为他的测试代码

define(function(require, exports, module) {
    console.log('require module: main');

    var mod1 = require('./mod1');
    mod1.hello();
    var mod2 = require('./mod2');
    mod2.hello();

    return {
        hello: function() {
            console.log('hello main');
        }
    };
});


运行结果应该是顺序的(sea.js下的结果):

require module: main
require module: mod1
hello mod1
require module: mod2
hello mod2
helo main


而不应该是异步的require.js下:

require module: mod2
require module: mod1
require module: main
hello mod1
hello mod2
helo main

但问题是,为什么"执行模块的顺序"应该是"严格按照模块在代码中出现(require)的顺序"? 并且"这样才更符合逻辑吧"?

如果他以seajs的运行结果来要求requirejs,那requirejs肯定吃亏了。AMD标准从来都没有规定模块的加载顺序,它只是需要保证:
The dependencies must be resolved prior to the execution of the module factory function, and the resolved values should be passed as arguments to the factory function with argument positions corresponding to indexes in the dependencies array.

评论下方有人(jockchou)的回复更切中要害:
 
我个人感觉requirejs更科学,所有依赖的模块要先执行好。如果A模块依赖B。当执行A中的某个操doSomething()后,再去依赖执行B模块require('B');如果B模块出错了,doSomething的操作如何回滚? 
很多语言中的import, include, useing都是先将导入的类或者模块执行好。如果被导入的模块都有问题,有错误,执行当前模块有何意义?
 
楼主说requirejs是坑,是因为你还不太理解AMD“异步模块”的定义,被依赖的模块必须先于当前模块执行,而没有依赖关系的模块,可以没有先后。
 
想像一下factory是个模块工厂吧,而依赖dependencies是工厂的原材料,在工厂进行生产的时候,是先把原材料一次性都在它自己的工厂里加工好,还是把原材料的工厂搬到当前的factory来什么时候需要,什么时候加工,哪个整体时间效率更高?显然是requirejs,requirejs是加载即可用的。为了响应用户的某个操作,当前工厂正在进行生产,当发现需要某种原材料的时候,突然要停止生产,去启动原材料加工,这不是让当前工厂非常焦燥吗?
这样看来其实两者并无太大差别。



但考虑这样一种业务情况,考虑某一个功能只对登陆用户开放,这样的话requirejs提前把模块加载是否有必要?(因为来到你页面的用户到离开也不会登陆)。

这是非常实际的问题,一个页面可以有非常多的功能,比如登陆、分享、留言、收藏……但不一定每一个来到页面的用户都会使用这些功能,如果都作为页面模块的依赖提前加载的话,对页面一定是一个不小的负担。

但seajs可以即用即加载,比如代码可以这么写

define(function () {

    if (user_login) {
        require(login_feature_module)
    }    

    document.body.onclick = function () {
        require(show_module)
    }
})


我同意这句话:
很多语言中的import, include, useing都是先将导入的类或者模块执行好。如果被导入的模块都有问题,有错误,执行当前模块有何意义?
但个人觉得考虑到页面的性能,可以考虑将要导入模块的懒加载(Lazy load)。

你会不会觉得我上面说的懒加载是一种天方夜谭?

但然不是,你去看看现在的人人网个人主页看看



图中标注的“与我相关”、“相册”、“分享”都是在点击之后才加载对应的模块

Facebook的情况更为严重,不仅要考虑内部不同团队的功能模块,还要考虑第三方的功能模块。

早在09年,他们就是用了一套静态资源管理方案(Static Resource Management),用于管理一个功能所需要的js/css静态文件:



简单来说,就是由页面上有没有功能所需的html片段,来决定是否加载和打包功能所需的js与style。也就是说静态文件需要通过html“声明”之后才可用。

具体可以参考这里:Frontend Performance Engineering in Facebook : Velocity 2009


## 执行差异

为了增强对比,我们在定义依赖模块的时候,故意让它的factory函数要执行相当长的时间,比如1秒:
// dep_A.js定义如下,dep_B/dep_C定义同理

define(function(require, exports, module) {
    
    (function(second) {
        var start = +new Date();
        while (start + second * 1000 > +new Date()) {}
    })(window.EXE_TIME);

    // window.EXE_TIME = 1;此处会连续执行1s

    exports.foo = function() {
        console.log("A");
    }
})

// main中同时加载三个相同模块

//require.js:
require(["dep_A", "dep_B", "dep_C"], function(A, B, C) {

});


//sea.js:
define(function(require, exports, module) {

    var mod_A = require("dep_A");
    var mod_B = require("dep_B");
    var mod_C = require("dep_C");
});


requirejs加载的瀑布图:



seajs加载的瀑布图:



如果把一个模块的执行拆分为执行define和执行factory函数的话(对requirejs和seajs都适用),从上图可以看出:

  • requirejs:一个模块的factory函数执行是紧跟随在define(也就是Evaluate Script脚本模块文件)之后
  • seajs: 执行一个模块的factory函数需要等待所有模块define完毕。

重点不是这些,我想说的是我在seajs中看到一个闪光点。

在上面一节中我提到了懒加载模块,在加载模块的时候需要1. 临时请求模块文件 ; 2. 执行factory函数。

但如果我们在载入页面时仅仅是执行把懒加载的模块的define(从上面两个图可以看出define的代价是非常小的),而设法不执行factory函数。

那么在真正需要懒加载的时候只要执行factory函数即可。这样不是能够让模块响应更及时,更靠谱?

这是可以实现的。但技术细节就不赘述了。这可以作为优化模块加载的一种方案。

那么也看看上述引用文章的后记吧

http://www.douban.com/note/283566440/

注意我这里说的是执行(真正运行define中的代码)模块, 而非加载(load文件)模块.
模块的加载都是并行的, 没有区别, 区别在于执行模块的时机, 或者说是解析.

为了说明阻塞的问题, 翠花上图

SeaJS的懒执行

 

RequireJS的预执行



注意图中巨大的pinyin-dict.js模块, 取自pinyin.js, 复制了N次后以增加它的"重量", 增强演示效果, 大家有兴趣的话可以亲手试试.

可以很明显的看出RequireJS的做法是并行加载所有依赖的模块, 并完成解析后, 再开始执行其他代码, 因此执行结果只会"停顿"1次, 完成整个过程是会比SeaJS要快.

而SeaJS一样是并行加载所有依赖的模块, 但不会立即执行模块, 等到真正需要(require)的时候才开始解析, 这里耗费了时间, 因为这个特例中的模块巨大, 因此造成"停顿"2次的现象, 这就是我所说的SeaJS中的"懒执行".

最后感谢大家的各种意见建议, 我这里并没有说SeaJS与RequireJS哪个更好一些, 仅仅是为了说明下他们的区别, 各种取舍请大家根据实际情况来定, 希望能帮到大家.

 看到这里,估计都明白了,针对情景不一样,最后在于大家的取舍了.

posted @ 2015-07-03 01:58  锚机  阅读(762)  评论(0编辑  收藏  举报
Λ