欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaScript模块化之使用requireJS按需加载

程序员文章站 2022-05-26 11:21:09
模块加载器的概念可能稍微接触过前端开发的童鞋都不会陌生,通过模块加载器可以有效的解决这些问题: js文件的依赖关系。 通过异步加载优化script标签引起的...

模块加载器的概念可能稍微接触过前端开发的童鞋都不会陌生,通过模块加载器可以有效的解决这些问题:

  1. js文件的依赖关系。
  2. 通过异步加载优化script标签引起的阻塞问题
  3. 可以简单的以文件为单位将功能模块化并实现复用

主流的js模块加载器有requirejs,seajs等,加载器之间可能会因为遵循的规范不同有微妙的差别,从纯用户的角度出发,之所以选requirejs而不是seajs主要是因为:

功能实现上两者相差无几,没有明显的性能差异或重大问题。

文档丰富程度上,requirejs远远好于seajs,就拿最简单的加载jquery和jquery插件这回事,虽然两者的实现方法相差无几,但requirejs就有可以直接拿来用的demo,seajs还要读文档自己慢慢折腾。一些问题的解决上,requirejs为关键词也更容易找到答案。

requirejs 加载jquery + jquery插件

可能对于一般web app来说,引入jquery及相关插件的概率是最大的,requirejs也亲切的给出了相应的解决方案及动态加载jquery及插件的文档及。

在最新的jquery1.9.x中,jquery已经在最后直接将自己注册为一个amd模块,即是说可以直接被requirejs作为模块加载。如果是加载旧版的jquery有两种方法:

1. 让jquery先于requirejs加载

2. 对jquery代码稍做一点处理,在jquery代码包裹一句:

define(["jquery"], function($) { 
  // $ is guaranteed to be jquery now */ 
}); 

requirejs的示例中,直接将requirejs与jquery合并为一个文件,如果是采用jquery作为核心库的话推荐这种做法。

同样对于jquery插件来说也有两种方法

1. 在插件外包裹代码

define(["jquery"], function($){ 
   // put here the plugin code. 
}); 

2. 在使用reuqirejs代码加载前注册插件(比如在main.js)中

requirejs.config({ 
  "shim": { 
    "jquery-cookie" : ["jquery"] 
  } 
}); 

requirejs加载第三方类库

在实例的app中还用到了jquery以外的第三方类库,如果类库不是一个标准的amd模块而又不想更改这些类库的代码,同样需要提前进行定义:

require.config({ 
   paths: { 
      'underscore': 'vendor/underscore' 
   }, 
   shim: { 
     underscore: { 
       exports: '_' 
     } 
   } 
}); 

css文件的模块化处理

在requirejs中,模块的概念仅限于js文件,如果需要加载图片、json等非js文件,requirejs实现了一系列加载插件。

但是遗憾的是requirejs官方没有对css进行模块化处理,而我们在实际项目中却往往能遇到一些场景,比如一个轮播的图片展示栏,比如高级编辑器等等。几乎所有的富ui组件都会由js与css两部分构成,而css之间也存在着模块的概念以及依赖关系。

为了更好的与requirejs整合,这里采用require-css来解决css的模块化与依赖问题。

require-css是一个requirejs插件,下载后将css.js与normalize.js放于main.js同级即可默认被加载,比如在我们的项目中需要加载jquery mobile的css文件,那么可以直接这样调用:

require(['jquery', 'css!../css/jquery.mobile-1.3.0.min.css'], function($) { 
}); 

不过由于这个css本质上是属于jquery mobile模块的一部分,更好的做法是将这个css文件的定义放在jquery mobile的依赖关系中,最终我们的requirejs定义部分为:

require.config({ 
   paths: { 
      'jquerymobile': 'vendor/jquery.mobile-1.3.0', 
      'jstorage' : 'vendor/jstorage', 
      'underscore': 'vendor/underscore' 
   }, 
   shim: { 
     jquerymobile : { 
      deps: [ 
        'css!../css/jquery.mobile-1.3.0.min.css' 
      ] 
     }, 
     underscore: { 
       exports: '_' 
     } 
   } 
}); 

在使用模块时,只需要:

require(['jquery', 'underscore', 'jquerymobile', 'jstorage'], function($, _) { 
}); 

jquery mobile的css文件就会被自动加载,这样css与js就被整合为一个模块了。同理其他有复杂依赖关系的模块也可以做类似处理,requirejs会解决依赖关系的逻辑。

数据源的加载与等待

web app一般都会动态加载后端的数据,数据格式一般可以是json、jsonp也可以直接是一个js变量。这里以js变量为例:

var restaurants = [ 
  { 
    "name": "kfc" 
  }, 
  { 
    "name": "7-11" 
  }, 
  { 
    "name": "成都小吃" 
  } 
] 

载入这段数据:

$.getscript('data/restaurants.json', function(e){ 
  var data = window.restaurants; 
  alert(data[0].name); //kfc 
}); 

单一的数据源确实很简单,但是往往一个应用中会有多个数据源,比如在这个实例app中ui就需要载入用户信息、餐厅信息、订餐信息三种数据后才能工作。如果仅仅靠多层嵌套回调函数的话,可能代码的耦合就非常重了。

为了解决多个数据加载的问题,我习惯的解决方法是构造一个dataready事件响应机制。

var foodorder = { 
 
  //数据载入后要执行的函数暂存在这里 
  datareadyfunc : [] 
 
  //数据源url及载入状态 
  , datasource : [ 
    { url : 'data/restaurants.json', ready : false, data : null }, 
    { url : 'data/users.json', ready : false, data : null }, 
    { url : 'data/foods.json', ready : false, data : null } 
  ] 
 
  //检查数据源是否全部载入完毕 
  , isready : function(){ 
    var isready = true; 
    for(var key in this.datasource){ 
      if(this.datasource[key].ready !== true){ 
        isready = false; 
      } 
    } 
    return isready; 
  } 
 
  //数据源全部加载完毕,则逐一运行datareadyfunc中存放的函数 
  , callready : function(){ 
    if(true === this.isready()){ 
      for(var key in this.datareadyfunc){ 
        this.datareadyfunc[key](); 
      } 
    } 
  } 
 
  //供外部调用,会将外部输入的函数暂存在datareadyfunc中 
  , dataready : function(func){ 
    if (typeof func !== 'function') { 
      return false; 
    } 
    this.datareadyfunc.push(func); 
  } 
 
  , init : function(){ 
    var self = this; 
    var _initelement = function(key, url){ 
      $.getscript(url, function(e){ 
        //每次载入数据后,将数据存放于datasource中,将ready状态置为true,并调用callready 
        self.datasource[key].data = window[key]; 
        self.datasource[key].ready = true; 
        self.callready(); 
      }); 
    } 
    for(var key in this.datasource){ 
      _initelement(key, this.datasource[key].url); 
    } 
  } 
} 

用法为:

foodorder.dataready(function(){ 
  alert(1);   
}); 
foodorder.init(); 

dataready内的alert将会在所有数据载入完毕后开始执行。

这段处理的逻辑并不复杂,将所有要执行的方法通过dataready暂存起来,等待数据全部加载完毕后再执行,更加复杂的场景此方法仍然通用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。