技術(shù)選擇

2018-07-10 15:48 更新

技術(shù)選擇

模塊化

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

其他語言中的模塊支持

  • Java - import
  • C# - using
  • css - @import

但在 JavaScript 中并不存在模塊組織在并不支持,于是產(chǎn)生了很多,模塊系統(tǒng)。

模塊的職責(zé)

  • 封裝實(shí)現(xiàn)(將復(fù)雜的內(nèi)容于外界個(gè)例)
  • 暴露接口(外部可通過接口使用模塊)
  • 聲明依賴(提供給模塊系統(tǒng)使用)

模塊的使用

反模式(Anti-Pattern)

反模式既沒有使用任何設(shè)計(jì)模式。

math.js

function add(a, b) {
  return a + b;
}
function sub(a, b) {
  return a - b;
}

上面的代碼有下面的幾個(gè)缺點(diǎn):

  • 無封裝性
  • 接口結(jié)構(gòu)不明顯

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):

  • 沒有依賴聲明
  • 使用全局狀態(tài)
字面量(Object Literal)

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);
    }
  }
}

同樣沒有依賴聲明

IIFE(Immediately-invoked Function Expresion)

其為自執(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)行依賴管理。

命名空間(Namespace)

命運(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

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)

  • 依賴管理成熟可靠
  • 社區(qū)活躍且規(guī)范接受度高
  • 運(yùn)行時(shí)支持且模塊化定義簡(jiǎn)單
  • 文件級(jí)別的模塊作用域隔離
  • 可以處理循環(huán)依賴

缺點(diǎn)

  • 不是標(biāo)準(zhǔn)組織規(guī)范
  • 同步請(qǐng)求未考慮瀏覽器環(huán)境(可以使用 Browserify 來解決)
# 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]);
AMD(Asynchronous Module Definition)

適合異步環(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)

  • 依賴管理成熟可靠
  • 社區(qū)活躍且規(guī)范接受度高
  • 轉(zhuǎn)為異步環(huán)境制作,適合瀏覽器
  • 支持 CommonJS 的書寫方式
  • 通過插件 API 可以加載非 JavaScript 資源
  • 成熟的打包構(gòu)建工具,并可結(jié)合插件一同使用

缺點(diǎn)

  • 模塊定義繁瑣,需要額外嵌套
  • 酷基本的支持,需要引入額外的庫
  • 無法處理循環(huán)依賴
  • 無法實(shí)現(xiàn)條件加載
Simplified CommonJS Wrapping

使用同樣的 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;
})
Loader Plugins

允許調(diào)用處理腳本外的其他資源(例如 HTML 與 CSS 文件),這樣就可以形成一個(gè)完整的組件。

完整組件 = 結(jié)構(gòu) + 邏輯 + 樣式
ECMAScript 6 Module

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)

  • 真正的規(guī)范未來標(biāo)準(zhǔn)
  • 語言基本支持
  • 適用于所有的 JavaScript 允許環(huán)境
  • 可用于處理循環(huán)依賴

缺點(diǎn)

  • 規(guī)范未達(dá)到穩(wěn)定級(jí)別
  • 暫無瀏覽器支持

SystemJS

SystemJS 是一個(gè)動(dòng)態(tài)模塊加載器,下面是它的一下特性:

  • 支持加載 AMD
  • 支持加載 CommonJS
  • 支持加載 ES6
  • 支持加載 Transpiler 也可支持任意類型資源

模塊管理的對(duì)比

  • IIFE,沒有解決核心的依賴分析和注入的問題。
  • AMD,可以直接使用,庫基本的支持。
  • CommonJS,可以直接使用,在運(yùn)行時(shí)的支持。
  • ES6,語言本身的支持。

使用插件工具,可以將后三種模塊管理系統(tǒng)進(jìn)行相互轉(zhuǎn)換。

框架

NOTE:以下討論都是基于 JavaScript 的框架。

庫(Library)與框架(Framework)的區(qū)別

 為針對(duì)特定問題的解答具有專業(yè)性,不控制應(yīng)用的流程且被動(dòng)調(diào)用。框架 具有控制翻轉(zhuǎn),決定應(yīng)用的生命周期,于是便集成了大量的庫。

解決方案

常見的解決方案針對(duì)的方面:

  • DOM
  • Communication
  • Utility
  • Templating
  • Component
  • Routing(單頁系統(tǒng)中尤其重要)
  • Architecture

使用外部專業(yè)解決方案的原因 可以提高開發(fā)效率,可靠性高(瀏覽器兼容,測(cè)試覆蓋),也配備優(yōu)良的配套(文檔及工具)。如果外部框架的質(zhì)量可可靠性無法保證或無法滿足業(yè)務(wù)學(xué)期時(shí)則不應(yīng)該選擇外部的框架。

實(shí)際項(xiàng)目中的使用

  • 開發(fā)式:基于外部模塊系統(tǒng)自由組合
  • 半開放:基于一個(gè)定制的模塊系統(tǒng),內(nèi)部外部解決方案共存
  • 封閉式:深度定制的模塊系統(tǒng)不引入外部模塊
DOM

與其相關(guān)的有 SelectorManipulation、Event(DOM)、Animation。 它的主要職責(zé)則為為下面的這些:

  • 提供便利的 DOM 查詢、操作、移動(dòng)等操作
  • 提供事件綁定及事件代理支持
  • 提供瀏覽器特性檢測(cè)及 UserAgent 偵測(cè)
  • 提供節(jié)點(diǎn)屬性、樣式、類名的操作
  • 保證目標(biāo)平臺(tái)的跨瀏覽器支持

常用的 DOM 庫有 jQuery(使用鏈?zhǔn)浇涌冢?strong>zepto.JS,MOOTOO.JS(使用原生 DOM 對(duì)象,通過直接跨站了 DOM 原生對(duì)象)。

基礎(chǔ)領(lǐng)域

庫名大小兼容性優(yōu)點(diǎn)缺點(diǎn)
MOOTOO.JS96KBIE6+概念清晰、無包裝對(duì)象、接口設(shè)計(jì)優(yōu)秀、源碼清晰易懂、不局限于 DOM 與 AJAX擴(kuò)展原生對(duì)象(致命)、社區(qū)衰弱
jQuery94KBIE6+社區(qū)強(qiáng)大普及率高、包裝對(duì)象、專注于 DOM包裝對(duì)象(容易混淆)
zepto.JS25KBIE10+小且啟動(dòng)快、接口與 jQuery 兼容、提供簡(jiǎn)單手勢(shì)操作無法與 jQuery 100% 對(duì)于、支持瀏覽器少、功能弱

專業(yè)領(lǐng)域

領(lǐng)域庫名大小描述
手勢(shì)Hammer.JS12KB常見手勢(shì)封裝(Tab、Hold、Transform、Swifp)并支持自定義
高級(jí)動(dòng)畫Velocity.JS12KB復(fù)雜動(dòng)畫序列實(shí)現(xiàn),不僅局限于 DOM
視頻播放Video.JS101KB類似原生 video 標(biāo)簽的使用方式,對(duì)低級(jí)瀏覽器使用 flash 播放器
局部滾動(dòng)isscroll.JS13KB移動(dòng)端position:fix + overflow:scroll的救星
Communication

與其相關(guān)的有 XMLHttpRequestForm、JSONP、Socket。 它的主要職責(zé)則為為下面的這些:

  • 處理與服務(wù)器的請(qǐng)求與相應(yīng)
  • 預(yù)處理請(qǐng)求數(shù)據(jù)與響應(yīng)數(shù)據(jù) Error/Success 的判斷封裝
  • 多類型請(qǐng)求,統(tǒng)一接口(XMLHttpRequest1/2、JSONP、iFrame)
  • 處理瀏覽器兼容性
庫名大小支持
Reqwest3.4KBJSONP支持、穩(wěn)定 IE6+支持、CORS 跨域、Promise/A 支持
qwest2.5KB代碼少、支持XMLHttpRequest2、CORS 跨域、支持高級(jí)數(shù)據(jù)類型(ArrayBuffer、Blob、FormData)

實(shí)時(shí)性要求高的需求

庫名支持
socket.io實(shí)時(shí)性、支持二進(jìn)制數(shù)據(jù)流、智能自動(dòng)回退支持、支持多種后端語言(NodeJS 最為穩(wěn)妥)
Utility(Lang)

與其相關(guān)的有 函數(shù)增強(qiáng) & Shim(保證實(shí)現(xiàn)與規(guī)范一致)Flow Control。 它的主要職責(zé)則為為下面的這些:

  • 提供 JavaScript 原生不提供的功能
  • 方法門面包裝使其便于使用
  • 異步列隊(duì)及流程控制
庫名大小描述
es5-shim53KB提供 ES3 環(huán)境下的 ES5 支持
es6-shim38KB
underscore16.5KB兼容 IE6+ 的擴(kuò)展功能函數(shù)
Lodash50KB其為 underscore 的高性能版本,方法多為 runtime 編譯出來的
Templating

與其相關(guān)的有 String-based、DOM-basedLiving 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-basedDOM-basedLiving-Template
好處可以服務(wù)器端運(yùn)行
解決方案dust.JS、hogan、dot.JSAngular.JS、Vue.JS、KnockoutRegular.JS、Ractive.JS、htmlbar
初始化時(shí)間☆☆☆☆☆
動(dòng)態(tài)更新☆☆☆☆☆☆
DOM 無關(guān)☆☆☆☆☆
語法☆☆☆☆☆
學(xué)習(xí)成本☆☆☆☆☆
SVG 支持☆☆☆☆
安全性☆☆☆
Component

與其相關(guān)的有 ModalSlider、DatePicker、TabsEditor(其為產(chǎn)品開發(fā)中最耗時(shí)也是最必要的一部分)。它的主要職責(zé)則為為下面的這些:

  • 提供基礎(chǔ)的 CSS 支持
  • 提供常見的組件
  • 提供聲明式的調(diào)用方式(類似 Bootstrap)
組件庫名版本特定支持
Bootstrap3.xMobile First 流式柵格,基于 LESS與 SASS 組織可定制 UI,提供大量組件IE8+
Foundation5.xMobile First 流式柵格,基于 SASS 組織,可定制 UI,提供大量組件IE9+

NOTE:有存在不使用 jQuery 版本的 Bootstrap 可供使用。

Router

與其相關(guān)的有 Client SideServer Side。它的主要職責(zé)則為為下面的這些:

  • 監(jiān)聽 URL 變化,并通知注冊(cè)的模塊
  • 通過 JavaScript 進(jìn)行主動(dòng)跳轉(zhuǎn)
  • 歷史管理
  • 對(duì)目標(biāo)瀏覽器的兼容性支持
路由庫名大小特定支持
page.JS6.2KB類似 Express.Router 的路由規(guī)則的前端路由庫IE8+
Director.JS10KB可以前后端使用同一套規(guī)則定義路由IE6+
Stateman10KB處理深層復(fù)雜路由的獨(dú)立路優(yōu)庫IE6+
crossroad.JS7.5KB老牌路由庫,API 功能較為繁瑣
Architecture(解耦)

與其相關(guān)的有 MVCMVVC、MV*,解耦又可以通過很多方式來實(shí)現(xiàn)(例如事件、分層)。它的主要職責(zé)則為為下面的這些:

  • 提供一種凡是幫助(強(qiáng)制)開發(fā)者進(jìn)行模塊解耦
  • 視圖與模型分離
  • 容易進(jìn)行單元測(cè)試
  • 容易實(shí)現(xiàn)應(yīng)用擴(kuò)展

下面以 MVVM為例:

  • Model 數(shù)據(jù)實(shí)體用于記錄應(yīng)用程序的數(shù)據(jù)
  • View 友好的界面其為數(shù)據(jù)定制的反映,它包含樣式結(jié)構(gòu)定義以及 VM 享有的聲明式數(shù)據(jù)以及數(shù)據(jù)綁定
  • ViewModel 其為 View 與 Model 的粘合,它通過綁定事件與 View 交互并可以調(diào)用 Service 處理數(shù)據(jù)持久化,也可以通過數(shù)據(jù)綁定將 Model 的變動(dòng)反映到 View 中

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


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)