NOTE:以下討論都是基于 JavaScript 的模塊組織(每個模塊均以文件形式組織),而非工程的模塊化。
The secret to building large app is never build arge apps. Break your applications into small pieces. Then, assemble those testable, bite-sized pieces into your big application.
Justin Meyer
其他語言中的模塊支持
import
using
@import
但在 JavaScript 中并不存在模塊組織在并不支持,于是產(chǎn)生了很多,模塊系統(tǒng)。
模塊的職責
反模式既沒有使用任何設計模式。
math.js
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
上面的代碼有下面的幾個缺點:
calculator.js
var action = 'add';
function compute(a, b) {
switch (action) {
case 'add': return add(a, b);
case 'sub': return sub(a, b);
}
}
上面的代碼也有幾個缺點:
math.js
var math = {
add: function(a, b) {
return a + b;
},
sub: function(a, b) {
return a - b;
}
};
結構性好,但沒有訪問控制。
calculator.js
var calculator = {
action: 'add',
compute: function(a, b) {
switch (action) {
case 'add': return add(a, b);
case 'sub': return sub(a, b);
}
}
}
同樣沒有依賴聲明
其為自執(zhí)行函數(shù)。
版本一
calculator.js
var calculator = (function(){
var action = 'add';
return {
compute: function(a, b) {
switch (action) {
case 'add': return add(a, b);
case 'sub': return sub(a, b);
}
}
}
})();
上面的代碼可以進行訪問控制,但是不能進行依賴聲明。
版本二
calculator.js
var calculator = (function(m){
var action = 'add';
function compute(a, b) {
switch (action) {
case 'add': return m.add(a, b);
case 'sub': return m.sub(a, b);
}
}
return {
compute: compute;
}
})(math)
上面的代碼雖然可以顯示的聲明依賴,但是仍然污染了全局變量,而且必須手動進行依賴管理。
命運空間可以解決全局變量的污染的問題。
math.js
namespace('math', [], function(){
function add(a, b) { return a + b; }
function sub(a, b) { return a - b; }
return {
add: add,
sub: sub
}
})
calculator.js
// 依賴聲明 依賴注入
// | |
namespace('calculator', ['math'], function(m){
var action = 'add';
function compute(a,b) {
return m[action](a, b);
}
return {
compute: compute;
}
})
復雜的模塊管理,不能單純的通過代碼文件的排列順序來進行管理。于是引入了模塊系統(tǒng),它有下面的職責:
常用的模塊系統(tǒng)有 Common.JS
、AMD
、語言基本的模塊化。
CommonJS 是一個模塊規(guī)范,通常適用于非瀏覽器環(huán)境(NodeJS)。
A module spec for JavaScript outside the browser.
math.js
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a - b;
}
exports.add = add;
exports.sub = sub;
calculator.js
// 依賴聲明
var math = require('./math');
function Calculator(container) {
// ...
}
Calculator.prototype.compute = function(){
this.result.textContent = math.add(...);
}
// 接口暴露
exports.Calculator = Calculator;
優(yōu)點
缺點
# browserify 為 npm 下命令行工具
# > 為 Linux/Unix 添加至命令
browserify file0.js > file1.js;
打包后的文件如下所示。
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
},{}]},{},[1]);
適合異步環(huán)境的依賴管理方案。
math.js
// 依賴列表
// |
define([], function(){
function add(a, b) { return a + b; }
function sub(a, b) { return a - b; }
// 接口暴露
return {
add: add,
sub: sub
}
})
calculator.js
define(['./math'], function(math){
function Calculator(container) {
// ...
}
Calculator.prototype.compute = function(){
this.result.textContent = math.add(...);
};
// 暴露接口
return {
Calculator: Calculator;
}
})
優(yōu)點
缺點
使用同樣的 CommonJS 的依賴管理書寫方法,之后在使用正則表達式來提取依賴列表。
define(function(require, exports){
// 依賴聲明
var math = require('./math');
function Calculator(container) {
// ...
}
Calculator.prototype.compute = function(){
this.result.textContent = math.add(...);
}
// 接口暴露
exports.Calculator = Calculator;
})
允許調用處理腳本外的其他資源(例如 HTML 與 CSS 文件),這樣就可以形成一個完整的組件。
完整組件 = 結構 + 邏輯 + 樣式
ECMAScript 6 中的模塊化管理。
math.js
function add(a, b) {
return a + b;
}
function sub(a, b) {
return a- b;
}
// export 關鍵字暴漏接口
export {add, sub}
calculator.js
import {add} from './math';
class Calculator {
constructor(container) {}
compute(){
this.result.textContent = add(+this.left.value, +this.right.value);
}
}
export{Calculator}
優(yōu)點
缺點
SystemJS 是一個動態(tài)模塊加載器,下面是它的一下特性:
使用插件工具,可以將后三種模塊管理系統(tǒng)進行相互轉換。
NOTE:以下討論都是基于 JavaScript 的框架。
庫 為針對特定問題的解答具有專業(yè)性,不控制應用的流程且被動調用。框架 具有控制翻轉,決定應用的生命周期,于是便集成了大量的庫。
常見的解決方案針對的方面:
使用外部專業(yè)解決方案的原因 可以提高開發(fā)效率,可靠性高(瀏覽器兼容,測試覆蓋),也配備優(yōu)良的配套(文檔及工具)。如果外部框架的質量可可靠性無法保證或無法滿足業(yè)務學期時則不應該選擇外部的框架。
實際項目中的使用
與其相關的有 Selector、Manipulation、Event(DOM)、Animation。 它的主要職責則為為下面的這些:
常用的 DOM 庫有 jQuery(使用鏈式接口),zepto.JS,MOOTOO.JS(使用原生 DOM 對象,通過直接跨站了 DOM 原生對象)。
基礎領域
庫名 | 大小 | 兼容性 | 優(yōu)點 | 缺點 |
---|---|---|---|---|
MOOTOO.JS | 96KB | IE6+ | 概念清晰、無包裝對象、接口設計優(yōu)秀、源碼清晰易懂、不局限于 DOM 與 AJAX | 擴展原生對象(致命)、社區(qū)衰弱 |
jQuery | 94KB | IE6+ | 社區(qū)強大普及率高、包裝對象、專注于 DOM | 包裝對象(容易混淆) |
zepto.JS | 25KB | IE10+ | 小且啟動快、接口與 jQuery 兼容、提供簡單手勢操作 | 無法與 jQuery 100% 對于、支持瀏覽器少、功能弱 |
專業(yè)領域
領域 | 庫名 | 大小 | 描述 |
---|---|---|---|
手勢 | Hammer.JS | 12KB | 常見手勢封裝(Tab、Hold、Transform、Swifp)并支持自定義 |
高級動畫 | Velocity.JS | 12KB | 復雜動畫序列實現(xiàn),不僅局限于 DOM |
視頻播放 | Video.JS | 101KB | 類似原生 video 標簽的使用方式,對低級瀏覽器使用 flash 播放器 |
局部滾動 | isscroll.JS | 13KB | 移動端position:fix + overflow:scroll 的救星 |
與其相關的有 XMLHttpRequest、Form、JSONP、Socket。 它的主要職責則為為下面的這些:
庫名 | 大小 | 支持 |
---|---|---|
Reqwest | 3.4KB | JSONP支持、穩(wěn)定 IE6+支持、CORS 跨域、Promise/A 支持 |
qwest | 2.5KB | 代碼少、支持XMLHttpRequest2、CORS 跨域、支持高級數(shù)據(jù)類型(ArrayBuffer、Blob、FormData) |
實時性要求高的需求
庫名 | 支持 |
---|---|
socket.io | 實時性、支持二進制數(shù)據(jù)流、智能自動回退支持、支持多種后端語言(NodeJS 最為穩(wěn)妥) |
與其相關的有 函數(shù)增強 & Shim(保證實現(xiàn)與規(guī)范一致)、Flow Control。 它的主要職責則為為下面的這些:
庫名 | 大小 | 描述 |
---|---|---|
es5-shim | 53KB | 提供 ES3 環(huán)境下的 ES5 支持 |
es6-shim | 38KB | |
underscore | 16.5KB | 兼容 IE6+ 的擴展功能函數(shù) |
Lodash | 50KB | 其為 underscore 的高性能版本,方法多為 runtime 編譯出來的 |
與其相關的有 String-based、DOM-based、Living Template。
基于字符串的模板
之后的數(shù)據(jù)修改展現(xiàn)不會進行變化,如果重新繪制(性能低)頁面則會去除已有的 DOM 事件。
基于 DOM 的模板
修改數(shù)據(jù)可以改變顯示(性能更好)也會保留 DOM 中的已有事件,最終導致 DOM 樹與數(shù)據(jù)模型相聯(lián)系。
Living-Template
其拼接了字符串模板和 DOM 模板的技術(類似 Knockout.JS 注釋的實現(xiàn)),最終導致 DOM 樹與數(shù)據(jù)模型相聯(lián)系。
String-based | DOM-based | Living-Template | |
---|---|---|---|
好處 | 可以服務器端運行 | ||
解決方案 | dust.JS、hogan、dot.JS | Angular.JS、Vue.JS、Knockout | Regular.JS、Ractive.JS、htmlbar |
初始化時間 | ☆☆☆ | ☆ | ☆☆ |
動態(tài)更新 | 無 | ☆☆☆ | ☆☆☆ |
DOM 無關 | ☆☆☆ | 無 | ☆☆ |
語法 | ☆☆☆ | ☆ | ☆☆ |
學習成本 | ☆ | ☆☆☆ | ☆☆ |
SVG 支持 | 無 | ☆☆ | ☆☆ |
安全性 | ☆ | ☆ | ☆☆☆ |
與其相關的有 Modal、Slider、DatePicker、Tabs、Editor(其為產(chǎn)品開發(fā)中最耗時也是最必要的一部分)。它的主要職責則為為下面的這些:
組件庫名 | 版本 | 特定 | 支持 |
---|---|---|---|
Bootstrap | 3.x | Mobile First 流式柵格,基于 LESS與 SASS 組織可定制 UI,提供大量組件 | IE8+ |
Foundation | 5.x | Mobile First 流式柵格,基于 SASS 組織,可定制 UI,提供大量組件 | IE9+ |
NOTE:有存在不使用 jQuery 版本的 Bootstrap 可供使用。
與其相關的有 Client Side、Server Side。它的主要職責則為為下面的這些:
路由庫名 | 大小 | 特定 | 支持 |
---|---|---|---|
page.JS | 6.2KB | 類似 Express.Router 的路由規(guī)則的前端路由庫 | IE8+ |
Director.JS | 10KB | 可以前后端使用同一套規(guī)則定義路由 | IE6+ |
Stateman | 10KB | 處理深層復雜路由的獨立路優(yōu)庫 | IE6+ |
crossroad.JS | 7.5KB | 老牌路由庫,API 功能較為繁瑣 |
與其相關的有 MVC、MVVC、MV*,解耦又可以通過很多方式來實現(xiàn)(例如事件、分層)。它的主要職責則為為下面的這些:
下面以 MVVM為例:
NOTE:MV 不等同于 SPA,路由是 MV 系統(tǒng)的課定位狀態(tài)信息來源。 NOTE+:單頁系統(tǒng)的普世法則為可定位的應用程序狀態(tài)都應該統(tǒng)一由路由系統(tǒng)進入,以避免網(wǎng)狀的信息流。 NOTE++:庫與框架選擇站點microjs javascriptOO JavaScripting
更多建議: