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