Mod 与 RequireJS/SeaJS 的那些事

本文的目的是为了能大让家更好的认识 Mod,之因此引入 RequireJS/SeaJS 的对比主要是应你们要求更清晰的对比应用场景,并非为了比较出孰胜孰劣,RequireJS 和 SeaJS 都是模块化漫漫之路的先驱者,向他们致敬!css

为工程化为生的Mod

模块化是一种处理复杂系统分解成为更好的可管理模块的方式,它能够把系统代码划分为一系列职责单一,高度解耦且可替换的模块,采用模块化可让系统的可维护性更加简单易得。html

JavaScript 并无为开发者们提供以一种简洁、有条理地的方式来管理模块的方法。从出发点来看,Mod和 RequireJS/SeaJS 是一致的,为开发者提供一套 JavaScript 模块化开发方案,让 JavaScript 的模块化开发变得更简单天然。可是在实现的过程当中却存在巨大着的差别。前端

Mod严格上来说并非一个独立的模块化框架,它是被设计用作前端工程化模块化方案的 JavaScript 支持,须要和自动化工具、后端框架配合来使用,目的在于但愿给工程师提供一个相似 nodeJS 同样的开发体验,同时具有很好的线上性能。node

RequireJS 和 SeaJS 的定位主要是 Web 浏览器端的模块加载器,依靠 JavaScript 运行时来支持模块定义、依赖分析和加载等功能。jquery

类 CommonJS 的开发体验

RequireJS 遵照的是 AMD 规范,SeaJS 遵照的是 CMD 的规范。AMD/CMD 规范使用的是“异步模块定义”的方式,这种方式给开发带来了极大的不便,全部的同步代码都须要修改成异步的方式,咱们是否能够在前端开发中使用“ CommonJS ”的方式,开发者可使用天然、容易理解的模块定义和调用方式,不须要关注模块是否异步,不须要改变开发者的开发行为。答案固然是确定的,Mod并不彻底遵照 AMD/CMD 规范,也正是为了为开发者提供更简单天然的开发体验。git

模块定义

Mod 使用 define 来定义一个模块:github

define (id, factory) 

factory 提供了 3 个参数:require, exports, module ,用于模块的引用和导出。json

在日常开发中,咱们无需关注模块定义,工具会自动对 JS 进行 define 包装处理:后端

JS 源码前端工程化

//common/widget/menu/menu.js var $ = require('common:widget/jquery/jquery.js'); exports.init = function() { $('.menu-ui ul li a').click(function(event) { var self = this; $('.menu-ui ul li a.active').removeClass('active'); $(self).addClass('active'); event.preventDefault(); }); }; 

编译后代码

define('common:widget/menu/menu.js', function(require, exports, module){ var $ = require('common:widget/jquery/jquery.js'); exports.init = function() { $('.menu-ui ul li a').click(function(event) { var self = this; $('.menu-ui ul li a.active').removeClass('active'); $(self).addClass('active'); event.preventDefault(); }); }; }); 

模块调用

Mod会在模块初始化以前自动加载相关依赖。所以当咱们须要一个模块时,只需提供一个模块名便可获取:

require (id) 

由于所需的模块都已预先加载,所以 require 能够当即(同步)返回该模块引用。不管在页面的 script 仍是模块内部,工程师均可以放心经过 require 来加载模块,不须要考虑什么时候该使用同步接口什么时候调用异步接口。

避免模块化引来的性能问题

RequireJS/SeaJS 经过过 JavaScript 运行时来支持“匿名闭包”、“依赖分析”和“模块加载”等功能,“依赖分析”须要在 JavaScript 运行时经过正则匹配到模块的依赖关系,而后顺着依赖链(也就是顺着模块声明的依赖层层进入,直到没有依赖为止)把全部须要加载的模块按顺序一一加载完毕, 当模块不少、依赖关系复杂的状况下会严重影响页面性能。Mod经过如下设计避免了如上问题:

  • 经过工具自动添加 define 闭包,线上不须要支持匿名闭包
  • 经过工具自动处理依赖,线上不须要动态处理依赖
  • 经过后端模板自动插入 script,线上不须要经过前端框架进行模块加载

经过以上设计,Mod极其精简,整个文件只有 100 多行,相比下 RequireJS 有 2000 多行,SeaJS 有将近 1000 行。

避免模块化为打包部署带来的极大不便

经过 RequireJS/SeaJS 进行模块化开发后,合并静态资源(打包)将变得十分不方便和晦涩难懂,每一个文件里只能有一个模块,不管是“ combo 插件”仍是“ flush 插件”,都须要咱们修改模块化调用的代码,这无疑是雪上加霜,工程师不只须要在开发的时候关注模块定义,在调用的时候还须要关注在一个请求里面加载哪些模 块比较合适,模块化的初衷是为了提升开发效率、下降维护成本,但咱们发现这样的模块化方案实际上并无下降维护成本,某种程度上来讲使得整个项目更加复杂 了。而使用 Mod,工程师只须要在配置文件配置合并策略便可,并不须要关注其余细节,Mod会自动处理好依赖以及合并信息并在模块初始化以前将模块的静态资源以及所依赖的模块加载并准备好。

自适应的性能优化

整个Mod模块化流程以下:

framework

经过自动化工具对模块进行编译处理,包括对对 JavaScript 模块添加闭包、记录每一个静态资源的部署路径以及依赖关系并生成资源表(resource map),以下所示,

{ "res": { "demo.js": { "uri": "/static/js/demo_33c5143.js", "type": "js", "deps": [ "demo.css" ], "pkg": "p0" }, "index.html": { "uri": "/index.html", "type": "html", "deps": [ "demo.js", "demo.css" ] }, "script.js": { "uri": "/static/js/script_32300bf.js", "type": "js", "pkg": "p0" } }, "pkg": { "p0": { "uri": "/static/pkg/aio_5bb04ef.js", "type": "js", "has": [ "demo.js", "script.js" ], "deps": [ "demo.css" ] } } } 

全部被打包的资源会有一个 pkg 属性 指向该表中的资源,而这个资源,正是咱们配置的打包策略。有了这些信息,咱们能够经过 Mod 框架(Mod 和后端框架)来管理和控制模块的加载。Mod的模块化能够十分灵活的适应各类性能优化场景,咱们还能够经过监控模块的调用状况,自动生成最优的打包配置,让网站能够自适应优化。

总结

Mod提供的是一体化的模块化解决方案,更多的是从工程化、自动化的角度去考虑,RequireJS/SeaJS 更独立灵活。

参考:http://fex.baidu.com/blog/2014/04/fis-modjs-requirejs-seajs/?utm_source=tuicool&utm_medium=referral

http://www.tuicool.com/articles/yuY36n

相关文章
相关标签/搜索