技術選擇

2018-07-10 15:48 更新

技術選擇

模塊化

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

其他語言中的模塊支持

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

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

模塊的職責

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

模塊的使用

反模式(Anti-Pattern)

反模式既沒有使用任何設計模式。

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

上面的代碼也有幾個缺點:

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

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

同樣沒有依賴聲明

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

上面的代碼可以進行訪問控制,但是不能進行依賴聲明。

版本二

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)

上面的代碼雖然可以顯示的聲明依賴,但是仍然污染了全局變量,而且必須手動進行依賴管理。

命名空間(Namespace)

命運空間可以解決全局變量的污染的問題。

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

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

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

缺點

  • 不是標準組織規(guī)范
  • 同步請求未考慮瀏覽器環(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)點

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

缺點

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

使用同樣的 CommonJS 的依賴管理書寫方法,之后在使用正則表達式來提取依賴列表。

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

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

完整組件 = 結構 + 邏輯 + 樣式
ECMAScript 6 Module

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

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

缺點

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

SystemJS

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

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

模塊管理的對比

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

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

框架

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

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

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

解決方案

常見的解決方案針對的方面:

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

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

實際項目中的使用

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

與其相關的有 Selector、Manipulation、Event(DOM)、Animation。 它的主要職責則為為下面的這些:

  • 提供便利的 DOM 查詢、操作、移動等操作
  • 提供事件綁定及事件代理支持
  • 提供瀏覽器特性檢測及 UserAgent 偵測
  • 提供節(jié)點屬性、樣式、類名的操作
  • 保證目標平臺的跨瀏覽器支持

常用的 DOM 庫有 jQuery(使用鏈式接口),zepto.JS,MOOTOO.JS(使用原生 DOM 對象,通過直接跨站了 DOM 原生對象)。

基礎領域

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

專業(yè)領域

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

與其相關的有 XMLHttpRequestForm、JSONP、Socket。 它的主要職責則為為下面的這些:

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

實時性要求高的需求

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

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

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

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

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

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

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

Router

與其相關的有 Client SideServer Side。它的主要職責則為為下面的這些:

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

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

  • 提供一種凡是幫助(強制)開發(fā)者進行模塊解耦
  • 視圖與模型分離
  • 容易進行單元測試
  • 容易實現(xiàn)應用擴展

下面以 MVVM為例:

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

NOTE:MV 不等同于 SPA,路由是 MV 系統(tǒng)的課定位狀態(tài)信息來源。 NOTE+:單頁系統(tǒng)的普世法則為可定位的應用程序狀態(tài)都應該統(tǒng)一由路由系統(tǒng)進入,以避免網(wǎng)狀的信息流。 NOTE++:庫與框架選擇站點microjs javascriptOO JavaScripting


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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號