引言:javascript
鸭子类型:css
面向对象的编程思想里,有一个有趣的概念,叫鸭子类型:
“一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那它就能够被当作鸭子。也就是说,它不关注对象的类型,而是关注对象具备的行为(方法)”----面向接口的编程html
编程思想还讲求单一原则,也就是要解耦,因此咱们但愿咱们编写程序功能的时候,具备单一职责、和面向接口的特色。前端
模块化其实也是这种思想,咱们赋予模块鲜明特色的功能(如jquery就是dom操做的能手),并把它们可以使用的方法属性(就是一种接口)暴露出来,固然,从这个角度来讲js模块化开发的引入是有点偏颇了,其实框架的出现都是为了解决一类问题产生的,java
在前端开发中,众多的script标签和他们之间的依赖关系复杂化,以及全局变量的增长,使得人工的维护变得困难起来,常犯错误。node
-----------------------------------------------jquery
模块写法的演进:git
多函数分散写法 --> 对象单体的写法 --> 匿名函数当即返回 --> 传入其余模块实现继承,var m1=(function(m1){ ...;return m1 })(window.module1||{})
(全局变量的污染) (暴露成员易被外部修改)github
-----------------------------------------------------ajax
规范:
commonJS
commonJS应用于服务端,服务端加载是和硬盘通讯,能够轻易实现同步,只需像require,而后直接使用,但浏览器则会形成长时间等待,故适合异步加载。
在客户端:
将多引入回调参数。
AMD,(Asynchronous Module Definition)客户端异步模块定义,是一种requireJS推广过程产生的规范。
CMD, (Common Module Definition)通用模块定义,是一种seaJS推广过程产生的规范。
---------------------------------------------------------------------------------------------------------------------------------------------
解决思路:
全局变量的消除 --- 按必定规则定义的模块 --- 把方法封入define的方法体内:define([id],dependencies,factory);
依赖关系的实现 --- 延迟和按需加载
具体实现:
<script src="js/require.js" data-main="js/main" defer async="true"></script>后两个属性能够实现放在头部可是在页面最后加载 这个main就是加载完require后首先加载和运行的文件,至关于c语言里的main函数
调用环节:
require.config({
baseUrl: "js/lib",//公共路径
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min",//支持在线地址
"underscore": "underscore.min",
"backbone": "backbone.min"
}
});
require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){ //注意:模块和参数要一一对应 });
定义环节:
define(['myLib'], function(myLib){ //用一个数组生明依赖
function foo(){ myLib.doSomething(); } return { //要返回 foo : foo }; });
如jquery的末尾有这样的语句:
if ( typeof define === "function" && define.amd && define.amd.jQuery ) { define( "jquery", [], function () { return jQuery; } );//第一个参数是模块id,为可选参数 }
若是一个库没有经过规则定义,则加载的时候能够经过require.config({});参数对象里添加一个属性:
require.config({
shim: { //若是一个模块 'underscore':{ exports: '_' }, 'backbone': { deps: ['underscore', 'jquery'],//生明依赖 exports: 'Backbone'//对外代表身份 } }
});
---------------------------------------------------------------------------------------------------
seaJS
上文require.js有如下特色:对于依赖的的模块,它是提早执行的(虽然从requireJS2.0开始改成延迟执行),并且必须把依赖前置。
而seaJS推崇as lazy as possible,且依赖就近。
define(function(require, exports, module) { //提供三个参数,require实现就近依赖,expotrs实现对外接口,module var a = require('./a') a.doSomething() // 此处略去 100 行 var b = require('./b') // 依赖能够就近书写 b.doSomething() // ... })
http://seajs.org/docs/#docs
使用简要:
引入-配置-入口
<script src="../sea-modules/seajs/seajs/2.2.0/sea.js"></script>
seajs.config({ //配置 base: "../sea-modules/", //设定统一目录 alias: { //设定别名 "jquery": "jquery/jquery/1.10.1/jquery.js" } });
seajs.use("examples/hello/1.0.0/main");//入口
main模块:
define(function(require) { var Spinning = require('./spinning'); var s = new Spinning('#container'); s.render(); });
spinning模块:
define(function(require, exports, module) { var $ = require('jquery'); function Spinning(container) { this.container = $(container); this.icons = this.container.children(); this.spinnings = []; } module.exports = Spinning; Spinning.prototype.render = function() { this._init(); this.container.css('background', 'none'); this.icons.show(); this._spin(); } Spinning.prototype._init = function() { var spinnings = this.spinnings; $(this.icons).each(function(n) { var startDeg = random(360); var node = $(this); var timer; node.css({ top: random(40), left: n * 50 + random(10), zIndex: 1000 }).hover( function() { node.fadeTo(250, 1) .css('zIndex', 1001) .css('transform', 'rotate(0deg)'); }, function() { node.fadeTo(250, .6).css('zIndex', 1000); timer && clearTimeout(timer); timer = setTimeout(spin, Math.ceil(random(10000))); } ); function spin() { node.css('transform', 'rotate(' + startDeg + 'deg)'); } spinnings[n] = spin; }) return this; } Spinning.prototype._spin = function() { $(this.spinnings).each(function(i, fn) { setTimeout(fn, Math.ceil(random(3000))); }); return this; } function random(x) { return Math.random() * x }; });
具体文档:
https://github.com/seajs/seajs/issues/266
*exports
仅仅是 module.exports
的一个引用。在 factory
内部给 exports
从新赋值时,并不会改变 module.exports
的值。所以给 exports
赋值是无效的,不能用来更改模块接口。
比如:
var a={x:1};b=a;b={x:2};console.log(a);//->{x:1} b和a指向关系以及断裂
------------------------------------------------------------------------------------------------------------------------
requireJS和seaJS二者的主要区别以下:
定位有差别。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js 则专一于 Web 浏览器端,同时经过 Node 扩展的方式能够很方便跑在 Node 环境中。
遵循的规范不一样。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD (通用模块定义)规范。规范的不一样,致使了二者 API 不一样。Sea.js 更贴近 CommonJS Modules/1.1 和 Node Modules 规范。
推广理念有差别。RequireJS 在尝试让第三方类库修改自身来支持 RequireJS,目前只有少数社区采纳。Sea.js 不强推,采用自主封装的方式来“海纳百川”,目前已有较成熟的封装策略。
对开发调试的支持有差别。Sea.js 很是关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS 无这方面的明显支持。
插件机制不一样。RequireJS 采起的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采起的是通用事件机制,插件类型更丰富。
还有很多差别,涉及具体使用方式和源码实现,欢迎有兴趣者研究并发表见解。
总之,若是说 RequireJS 是 Prototype 类库的话,则 Sea.js 致力于成为 jQuery 类库。
最后,向 RequireJS 致敬!RequireJS 和 Sea.js 是好兄弟,一块儿努力推广模块化开发思想,这才是最重要的。
------------------------------------------------------------------------------------------
尾巴:
软件危机:1960年代中期开始爆发众所周知的软件危机是指落后的软件生产方式没法知足迅速增加的计算机软件需求,从而致使软件开发与维护过程当中出现一系列严重问题的现象。
对于前端,不单是js的模块化极具意义,这个前端的模块化(工程化的实现必经)也极具意义。
没有银弹:
软件危机被提出之后,IBM大型电脑之父Fred Brooks在1987年所发表的一篇关于软件工程的经典论文
《没有银弹》,该论述中强调真正的银弹并不存在(银弹是西方驱魔驱鬼的特效武器),而所谓的没有银弹则是指
没有任何一项技术或方法能够能让软件工程的生产力在十年内提升十倍。
因此,前端工程化是一个复杂的实践,咱们不能期望掌握了什么时下很流行的框架或者什么前沿知识就能掌握前端,前端是一个永无止息的动态演进过程,对于咱们而言,也永远没有“学成本事”这一说,在此互相勉励你们,学无止境,多多益善。
前段工程化是将来之路,模块化开发是前端工程化的最初实践,咱们应该学习这种”分而治之“的思想,故我今天作此分享。