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


Posted in Javascript onApril 12, 2017

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

  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暂存起来,等待数据全部加载完毕后再执行,更加复杂的场景此方法仍然通用。

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

Javascript 相关文章推荐
用js判断页面是否加载完成实现代码
Dec 11 Javascript
jQuery选择器中含有空格的使用示例及注意事项
Aug 25 Javascript
JQUERY 设置SELECT选中项代码
Feb 07 Javascript
JavaScript截取指定长度字符串点击可以展开全部代码
Dec 04 Javascript
javascript中获取元素标签中间的内容的实现方法
Oct 08 Javascript
Vue.js系列之vue-router(上)(3)
Jan 03 Javascript
vuejs开发组件分享之H5图片上传、压缩及拍照旋转的问题处理
Mar 06 Javascript
JavaScript 程序错误Cannot use 'in' operator to search的解决方法
Jul 10 Javascript
vue实现商城购物车功能
Nov 27 Javascript
微信小程序项目总结之点赞 删除列表 分享功能
Jun 25 Javascript
解决layui的form里的元素进行动态生成,验证失效的问题
Sep 14 Javascript
JS函数式编程实现XDM一
Jun 16 Javascript
使用jQuery和ajax代替iframe的方法(详解)
Apr 12 #jQuery
微信小程序 仿美团分类菜单 swiper分类菜单
Apr 12 #Javascript
jQuery用noConflict代替$的实现方法
Apr 12 #jQuery
jQuery判断邮箱格式对错实例代码讲解
Apr 12 #jQuery
微信小程序 引入es6 promise
Apr 12 #Javascript
JavaScript队列的应用实例详解【经典数据结构】
Apr 12 #Javascript
Vue 实用分页paging实例代码
Apr 12 #Javascript
You might like
PHP插入排序实现代码
2013/04/04 PHP
yii实现CheckBox复选框在同一行显示的方法
2014/12/03 PHP
php自定义hash函数实例
2015/05/05 PHP
php实现表单多按钮提交action的处理方法
2015/10/24 PHP
js 实现无干扰阴影效果 简单好用(附文件下载)
2009/12/27 Javascript
js中匿名函数的N种写法
2010/09/08 Javascript
js函数调用常用方法详解
2012/12/03 Javascript
JS导出PDF插件的方法(支持中文、图片使用路径)
2016/07/12 Javascript
JavaScript String(字符串)对象的简单实例(推荐)
2016/08/31 Javascript
微信开发 JS-SDK 6.0.2 经常遇到问题总结
2016/12/08 Javascript
JavaScript基于DOM操作实现简单的数学运算功能示例
2017/01/16 Javascript
Koa日志中间件封装开发详解
2019/03/09 Javascript
layui实现左侧菜单点击右侧内容区显示
2019/07/26 Javascript
解决在Vue中使用axios用form表单出现的问题
2019/10/30 Javascript
聊聊Vue中provide/inject的应用详解
2019/11/10 Javascript
JS实现audio音频剪裁剪切复制播放与上传(步骤详解)
2020/07/28 Javascript
ant design 日期格式化的实现
2020/10/27 Javascript
[49:18]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 OG vs TNC
2018/04/01 DOTA
[02:21]十步杀一人,千里不留行——DOTA2全新英雄天涯墨客展示
2018/08/29 DOTA
Python实现类继承实例
2014/07/04 Python
python基于socket实现网络广播的方法
2015/04/29 Python
使用python实现生成用户信息
2017/03/20 Python
Python实现获取磁盘剩余空间的2种方法
2017/06/07 Python
pandas实现DataFrame显示最大行列,不省略显示实例
2019/12/26 Python
Python基于template实现字符串替换
2020/11/27 Python
台湾最大银发乐活百货:乐龄网
2018/05/21 全球购物
美国围栏公司:Walpole Outdoors
2019/11/19 全球购物
你常见到的runtime exception
2016/09/05 面试题
西部世纪.net笔试题面试题
2014/04/03 面试题
2013年研究生毕业感言
2014/02/06 职场文书
《黄山奇石》教学反思
2014/04/19 职场文书
人事经理岗位职责
2014/04/28 职场文书
应届大学生求职信
2014/07/20 职场文书
医生见习报告范文
2014/11/03 职场文书
2014年团总支工作总结
2014/11/21 职场文书
关于倡议书的范文
2015/04/29 职场文书