引言: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年所發表的一篇關於軟件工程的經典論文
《沒有銀彈》,該論述中強調真正的銀彈並不存在(銀彈是西方驅魔驅鬼的特效武器),而所謂的沒有銀彈則是指
沒有任何一項技術或方法能夠能讓軟件工程的生產力在十年內提升十倍。
因此,前端工程化是一個複雜的實踐,咱們不能期望掌握了什麼時下很流行的框架或者什麼前沿知識就能掌握前端,前端是一個永無止息的動態演進過程,對於咱們而言,也永遠沒有「學成本事」這一說,在此互相勉勵你們,學無止境,多多益善。
前段工程化是將來之路,模塊化開發是前端工程化的最初實踐,咱們應該學習這種」分而治之「的思想,故我今天作此分享。