web前端學習路線:HTML5教程以前端模塊化開發

1. 命名衝突css

  首先從一個簡單的習慣開始。前端

  因爲之前一直作 JavaEE 開發的緣故,在 JavaScript 開發中,我已經習慣將項目中的一些通用功能抽象出來,造成一個個的獨立函數,以便於實現代碼複用,如:jquery

  function css(element, attr) { // 獲取 element 元素的 attr 對應的 CSS 屬性值git

  // ...github

  }編程

  function offset(element) { // 獲取 element 元素在文檔中的位置座標gulp

  // ...api

  }數組

  並把這些封裝的函數放在統一的 tools.js 文件中。瀏覽器

  若是頁面功能實現須要使用到這些函數,則直接經過 引入便可。

  前期感受一切都好,你們也都以爲寫這樣的工具文件對開發來講很方便,直到使用愈來愈多,頁面功能愈來愈複雜,你們要實現的需求也愈來愈多樣。

  這時有人就抱怨,由於引入了 tools.js 文件,若是要定義一個可以設置 css 屬性值的函數,那麼就只有取另外的函數名稱(如 setCss )而不能再使用 css 這個函數名稱了,一樣若是要設置一個元素在整個文檔中的定位座標,也不能再使用 offset 這個函數名稱,由於那樣的話,就與 tools.js 文件中已定義的函數名稱衝突了。

  既然問題出現了,就須要解決。

  在 Java 中有一個很是實用的技術——package,它將邏輯上相關的代碼組織在一塊兒使用「包」來進行管理,這至關於文件系統中的文件夾。在文件系統中,文件夾內是相對獨立的一個空間,不用擔憂一個文件夾和另外一個文件夾中文件命名的衝突。在「包」中也同樣,能夠解決文件命名衝突問題,若是要在包外部再使用到包內的資源,直接經過 import 導入相關的 package 便可。相似包這樣的概念,在其它的語言(如 C#)中也稱爲命名空間。

  JavaScript 中並無提供原生的包或命名空間的支持,但可使用其它的方法(如對象、閉包)來實現相似的效果。

  參照 Java 的方式,我使用 JavaScript 中的對象來簡單修改 tools.js 文件:

  var Util = {

  css : function(element, attr) {

  // ...

  },

  offset : function(element) {

  // ...

  }

  };

  這樣,當引入 tools.js 文件後,要獲取 CSS 樣式或獲取元素的文檔座標,就經過相似 Util.css()/Util.offset() 的方法來實現。css 與 offset 的做用域是在對象 Util 下,再全局或是新對象中定義 css 屬性是不受影響的。

  Util 這個名稱也具備通用性,一般用做輔助工具定義的時候會使用到這個名稱,爲了體現該名稱的惟一性,能夠繼續借鑑 Java 中 package 的命名規範(域名倒置):

  var com = {};

  com.github = {};

  com.github.itrainhub = {};

  com.github.itrainhub.Util = {

  css : function(element, attr) {

  // ...

  },

  offset : function(element) {

  // ...

  }

  };

  要獲取 CSS 樣式值,則可以使用 com.github.itrainhub.Util.css() 方法。但這樣的寫法增長了記憶難度,YUI 中關於這一點有比較好的解決方案,先按下暫且不表。

  使用對象的寫法可解決命名衝突問題,但這種寫法也會暴露對象的全部成員,使對象內部狀態能夠在對象外部被改寫。好比在對象內部存在計數器:

  var Util = {

  _count : 0

  }

  在對象外部能夠經過 Util._count = 18; 修改該計數器的值,這是不安全的。

  像計數器這樣的變量,一般多是做爲對象的私有成員存在,不但願在對象外部還能繼續修改其值,這時,可使用 IIFE(當即執行函數)來設計:

  var Util = (function(){

  var _count = 0;

  function _css(element, attr) {

  // ...

  }

  function _offset(element) {

  // ...

  }

  return {

  css : _css,

  offset : _offset

  }

  })();

  這樣,在外部就不能再直接修改 _count 的值了。

  經過命名空間,的確能夠解決命名衝突的問題,咱們能夠暫時鬆一口氣了。

  2. 文件依賴

  接着 tools.js 繼續開發。

  在 tools.js 的基礎上,能夠開發出一些 UI 層通用的組件,如放大鏡、輪播圖之類的,這樣各個項目中要使用這些功能的時候就不用重複造輪子了。

  一般狀況下,每一個 UI 組件都是以獨立的 js 文件存在的,好比放大鏡,能夠將它放到一個 zoom.js 的文件中,當要使用到放大鏡組件時,經過 引入便可。

  但不少時候,在使用 zoom.js 以前忘記了引入 tools.js,則使用 zoom.js 就會報錯,沒法保證它的正常執行。

  zoom.js 的正常執行依賴於 tools.js 的使用,上述的問題都仍是比較容易解決的,但隨着團隊愈來愈大,業務需求愈來愈複雜,項目中組件間的依賴關係也會變得愈來愈複雜。好比:

  某一天,我擴充了 zoom.js 組件的功能,但除了使用到 tools.js 外,還使用到另外一個工具 js 組件:helper.js。若是項目中已有 N 個地方以前使用到了 zoom.js 組件,我就只好全局搜索每一個引用 zoom.js 的地方,再加上對 helper.js 的引用。

  再想一想,隨着項目推動,咱們會繼續修改 tools.js,添加更多的組件 component_1.js、component_2.js……某些組件中只使用到 tools.js,某些只使用到 helper.js,而某些組件既使用到了 tools.js 又使用到了 helper.js。那麼關於組件間依賴關係的維護,工做量可想而知,若是以人肉的方式來保證依賴關係的維護,簡直就要崩潰掉了。

  爲何維護組件間的依賴關係這麼費神呢,由於 JavaScript 中天生缺乏了引入其它 js 文件的語法。在 Java 中能夠經過 import 引入依賴組件,在 CSS 中也有 @import 命令去引入其它的 CSS 文件,而 js 中卻不能自動管理依賴。

  除了文件間的依賴關係維護不便外,若是在頁面中引入的組件很是多,咱們還得保證引用組件的路徑及前後順序不能出錯,一旦出錯,又得花時間查找錯誤,可想而知工做量是很可觀了,再加上組件引入過多,又是以同步的方式加載各組件,也可能致使瀏覽器假死的現象。

  要解決這些問題,模塊化開發的價值就體現出來了。

  3. 模塊化開發

  3.1 模塊化

  所謂模塊化,就是把一個相對獨立的功能,單獨造成一個文件,可輸入指定依賴、輸出指定的函數,供外界調用,其它都在內部隱藏實現細節。這樣便可方便不一樣的項目重複使用,也不會對項目形成額外的影響。

  前端使用模塊化載發主要的做用是:

  • 異步加載 js,避免瀏覽器假死

  • 管理模塊間依賴關係,便於模塊的維護

  有了模塊,咱們就能夠更方便地使用別人的代碼,想要什麼功能,就加載什麼模塊。

  但要使用模塊的前提,是必然要造成可遵循的開發規範,使得開發者和使用者都有據可尋,不然你有你的寫法,我有個人寫法,你們沒辦法統一,也就不能很好的互用了。

  目前通用的規範是,服務器端使用 CommonJS 規範,客戶端使用 AMD/CMD 規範。

  3.2 CommonJS

  CommonJS 規範出現是在 2009 年,Node.js 就是該規範的實現。CommonJS 規範中是這樣加載模塊的:

  var gulp = require("gulp");

  gulp.task(/* 任務 */);

  模塊的加載是同步的,這種寫法適合服務器端,由於在服務器讀取的模塊都是在本地磁盤,加載速度很快,可同步加載完成。可是若是在客戶端瀏覽器中,由於模塊是放在服務器端的,模塊加載取決於網絡環境,以同步的方式加載模塊時有可能出現「假死」情況。

  今天我主要介紹針對瀏覽器編程,不針對 Node.js 內容,因此在此關於 CommonJS 規範就不做深究,知道 require() 用於加載模塊便可。

  3.3 AMD

  因爲在瀏覽器端,模塊使用同步方式加載可能出現假死,那麼咱們採用異步加載的方式來實現模塊加載,這就誕生了 AMD 的規範。

  AMD 即 Asynchronous Module Definition 的簡稱,表示「異步模塊定義」的意思。AMD 規範:https://github.com/amdjs/amdjs-api/wiki/AMD。

  AMD 採用異步方式加載模塊,模塊的加載不影響它後面語句的運行。全部依賴所加載模塊的語句,都被定義在一個回調函數中,等到模塊加載完畢後,回調函數纔會執行。

  AMD 也採用 require() 來加載模塊,語法結構爲:

  require([module], callback);

  module 是數組參數,表示所加載模塊的名稱;callback 是回調函數參數,全部模塊加載完畢後執行該回調函數。如:

  require(["jquery"], function($){

  $("#box").text("test");

  });

  3.4 CMD

  CMD 即 Common Module Definition 的簡稱,表示「通用模塊定義」的意思。CMD 規範:https://github.com/cmdjs/specification/blob/master/draft/module.md。

  CMD 規範明確了模塊的基本書寫格式和基本交互規則,該規範是在國內發展出來的,由玉伯在推廣 SeaJS 過程當中規範產出的。

  SeaJS 實現了 CMD 規範。SeaJS 要解決的問題和 RequireJS 同樣,只不過在模塊定義方式和模塊加載(運行、解析)時機上有所不一樣。

  3.5 AMD 與 CMD 的區別

  AMD 是 RequireJS 在推廣過程當中對模塊定義的規範化產出。

  CMD 是 SeaJS 在推廣過程當中對模塊定義的規範化產出。

  兩者主要區別以下:

  1 對於依賴的模塊,AMD 是提早執行,CMD 是延遲執行。

  2 CMD 推崇依賴就近,AMD 推崇依賴前置。

  3 AMD 的 API 默認是一個當多個用,CMD 的 API 嚴格區分,推崇職責單一。

  固然還有一些其它細節上的區別,具體看規範的定義就好。

相關文章
相關標籤/搜索