淺談模塊化開發

如今的前端開發

如今的前端開發, 不只僅是完成瀏覽的基本需求,而且一般是一個單頁面應用,每個視圖經過異步的方式加載,這致使頁面初始化和使用過程當中會加載愈來愈多的 JavaScript 代碼. 如何在開發環境組織好這些碎片化的代碼和資源,而且保證他們在瀏覽器端快速、優雅的加載和更新,就須要一個模塊化系統css

什麼是模塊化

把函數做爲模塊 缺陷: 污染全局變量 模塊成員之間沒什麼關係 面向對象思想 並使用當即執行函數 實現閉包 避免了變量污染 同時同一模塊內的成員也有了關係 在模塊外部沒法修改咱們沒有暴露出來的變量、函數 這就是簡單的模塊前端

指望的模塊系統

模塊的加載和傳輸,咱們首先能想到兩種極端的方式,一種是每一個模塊文件都單獨請求,另外一種是把全部模塊打包成一個文件而後只請求一次。顯而易見,每一個模塊都發起單獨的請求形成了請求次數過多,致使應用啓動速度慢;一次請求加載全部模塊致使流量浪費、初始化過程慢。這兩種方式都不是好的解決方案,它們過於簡單粗暴。node

分塊傳輸,按需進行懶加載,在實際用到某些模塊的時候再增量更新,纔是較爲合理的模塊加載方案。要實現模塊的按需加載,就須要一個對整個代碼庫中的模塊進行靜態分析、編譯打包的過程。jquery

在上面的分析過程當中,咱們提到的模塊僅僅是指JavaScript模塊文件。然而,在前端開發過程當中還涉及到樣式、圖片、字體、HTML 模板等等衆多的資源。若是他們均可以視做模塊,而且均可以經過require的方式來加載,將帶來優雅的開發體驗,那麼如何作到讓 require 能加載各類資源呢?在編譯的時候,要對整個代碼進行靜態分析,分析出各個模塊的類型和它們依賴關係,而後將不一樣類型的模塊提交給適配的加載器來處理。Webpack 就是在這樣的需求中應運而生。webpack

模塊系統

script

  • 全局做用域下容易形成變量衝突
  • 文件只能按照 <script> 的書寫順序進行加載
  • 開發人員必須主觀解決模塊和代碼庫的依賴關係
  • 在大型項目中各類資源難以管理,長期積累的問題致使代碼庫混亂不堪

CommonJS

服務器端的 Node.js 遵循 CommonJS規範,該規範的核心思想是容許模塊經過 require 方法來同步加載所要依賴的其餘模塊,而後經過 exports 或 module.exports 來導出須要暴露的接口。es6

require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
複製代碼

web

// moduleA.js
module.exports = function( value ){
    return value * 2;
}
複製代碼
// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);
複製代碼

優勢:npm

  • 服務器端模塊便於重用
  • NPM 中已經有將近20萬個可使用模塊包
  • 簡單並容易使用

缺點:gulp

  • 同步的模塊加載方式不適合在瀏覽器環境中,同步意味着阻塞加載,瀏覽器資源是異步加載的
  • 不能非阻塞的並行加載多個模塊

AMD

define(id?, dependencies?, factory),它要在聲明模塊的時候指定全部的依賴 dependencies,而且還要當作形參傳到 factory 中,對於依賴的模塊提早執行,依賴前置。瀏覽器

define("module", ["dep1", "dep2"], function(d1, d2) {
  return someExportedValue;
});
require(["module", "../file"], function(module, file) { /* ... */ });
複製代碼

一些用例: 定義一個名爲 myModule 的模塊,它依賴 jQuery 模塊:

define('myModule', ['jquery'], function($) {
    // $ 是 jquery 模塊的輸出
    $('body').text('hello world');
});
// 使用
define(['myModule'], function(myModule) {});
複製代碼

注意:在 webpack 中,模塊名只有局部做用域,在 Require.js 中模塊名是全局做用域,能夠在全局引用。 定義一個沒有 id 值的匿名模塊,一般做爲應用的啓動函數:

define(['jquery'], function($) {
    $('body').text('hello world');
});
複製代碼

依賴多個模塊的定義:

define(['jquery', './math.js'], function($, math) {
    // $ 和 math 一次傳入 factory
    $('body').text('hello world');
});
複製代碼

模塊輸出:

define(['jquery'], function($) {

    var HelloWorldize = function(selector){
        $(selector).text('hello world');
    };

    // HelloWorldize 是該模塊輸出的對外接口
    return HelloWorldize;
});
複製代碼

在模塊定義內部引用依賴:

define(function(require) {
    var $ = require('jquery');
    $('body').text('hello world');
});
複製代碼

優勢:

  • 適合在瀏覽器環境中異步加載模塊
  • 能夠並行加載多個模塊

缺點:

  • 提升了開發成本,代碼的閱讀和書寫比較困難,模塊定義方式的語義不暢
  • 不符合通用的模塊化思惟方式,是一種妥協的實現

4.CMD

define(function(require, exports, module) {
  var $ = require('jquery');
  var Spinning = require('./spinning');
  exports.doSomething = ...
  module.exports = ...
})
複製代碼

優勢:

  • 依賴就近,延遲執行
  • 能夠很容易在 Node.js 中運行

缺點:

  • 依賴 SPM 打包,模塊的加載邏輯偏重

5.UMD

6.ES6模塊

ES6 模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。

import "jquery";
export function doStuff() {}
module "localModule" {}
複製代碼

優勢:

  • 容易進行靜態分析
  • 面向將來的 EcmaScript 標準

缺點:

  • 原生瀏覽器端尚未實現該標準
  • 全新的命令字,新版的 Node.js才支持

實現:

AMD、CMD、webpack的區別:

從前有兩個規範,一個是AMD 一個是CMD  RequireJS是AMD規範的實現,SeaJS是CMD規範的實現,  一個主張提早加載依賴,一個主張延遲加載依賴 後來出現了 commonjs規範  webpack就是支持commonjs規範的  目前能夠說是主導了前端構建格局。

CommomJS是服務端規範,node就是採用這個規範,他是同步加載,畢竟服務端不用考慮異步。  它是對通用的JavaScript模式的標準化嘗試,它包含有 AMD 定義  AMD是異步加載模塊的縮寫,使用require引入模塊,提倡依賴前置。  CMD與AMD其實挺接近的,還由於有sea.js,中文資料仍是比較親切的。  還有就是AMD和CommomJS的中間者UMD  Webpack其實就是一個打包工具,他的思想就是一切皆模塊,css是模塊,js是模塊,圖片是模塊。而且提供了一些列模塊加載(各類-loader)來編譯模塊。官方推薦使用commonJS規範,可是也支持CMD和AMD

不管是node應用模塊,仍是webpack 配置 ,均是採用CommonJS模塊化規範

CommonJS 與 AMD 支持

Webpack 對 CommonJS 的 AMD 的語法作了兼容, 方便遷移代碼

不過實際上, 引用模塊的規則是依據 CommonJS 來的

require('lodash') // 從模塊目錄查找
require('./file') // 按相對路徑查找
複製代碼

AMD 語法中, 也要注意, 是按 CommonJS 的方案查找的

define (require, exports. module) ->
  require('lodash') # commonjs 當中這樣是查找模塊的 

複製代碼

CommonJs與AMD

在一開始,咱們先講一下它和以往咱們所用的模塊管理工具備什麼不同。在最開始的階段,Js並無這些模塊機制,各類Js處處飛,得不到有效妥善的管理。後來前端圈開始制定規範,最耳熟能詳的是CommonJs和AMD。

CommonJs是應用在NodeJs,是一種同步的模塊機制。它的寫法大體以下:

var firstModule = require("firstModule");
//your code...
module.export = anotherModule
複製代碼

AMD的應用場景則是瀏覽器,異步加載的模塊機制。require.js的寫法大體以下:

define(['firstModule'], function(module){
   //your code...
   return anotherModule
})
複製代碼

其實咱們單比較寫法,就知道CommonJs是更爲優秀的。它是一種同步的寫法,對Human友好,並且代碼也不會繁瑣臃腫。但更重要的緣由是,隨着npm成爲主流的JavaScript組件發佈平臺,愈來愈多的前端項目也依賴於npm上的項目,或者自身就會發布到npm平臺。因此咱們對如何可使用npm包中的模塊是咱們的一大需求。因此browserify工具就出現了,它支持咱們直接使用require()的同步語法去加載npm模塊。

固然咱們這裏不得不說的是,ES2015(ES6)裏也有了本身的模塊機制,也就是說ES6的模塊機制是官方規定的,咱們經過babel(一種6to5的編譯器)可使用比較多的新特性了,包括咱們提到的模塊機制,而它的寫法大體以下:

import {someModule} from "someModule";
// your codes...
export anotherModule;
複製代碼

固然上面的寫法只是最基本的,還有其餘的不一樣加載模塊的寫法,能夠看一下阮一峯老師的ECMAScript 6 入門或者babel的相關文檔Learn ES2015。

功能特性

browserify的出現很是棒,但webpack更勝一籌!

咱們來看看webpack支持哪些功能特性:

  • 支持CommonJs和AMD模塊,意思也就是咱們基本能夠無痛遷移舊項目。
  • 支持模塊加載器和插件機制,可對模塊靈活定製。特別是我最愛的babel-loader,有效支持ES6。
  • 能夠經過配置,打包成多個文件。有效利用瀏覽器的緩存功能提高性能。
  • 將樣式文件和圖片等靜態資源也可視爲模塊進行打包。配合loader加載器,能夠支持sass,less等CSS預處理器。
  • 內置有source map,即便打包在一塊兒依舊方便調試。
  • 看完上面這些,能夠想象它就是一個前端工具,可讓咱們進行各類模塊加載,預處理後,再打包。以前咱們對這些的處理是放在grunt或gulp等前端自動化工具中。有了webpack,咱們無需藉助自動化工具對模塊進行各類處理,讓咱們工具的任務分的更加清晰。

回顧ES6模塊

對象的導出

1. export default{
        add(){}
 }
2. export fucntion add(){} 至關於 將add方法當作一個屬性掛在到exports對象
複製代碼

對象的導入

若是導出的是:export default{ add(){}}
那麼能夠經過  import obj from './calc.js'
若是導出的是:
export fucntion add(){} 
export fucntion substrict(){} 
export const PI=3.14
那麼能夠經過按需加載 import {add,substrict,PI} from './calc.js'
複製代碼

回顧Node模塊

傳統非模塊化開發有以下的缺點

  1. 命名衝突
  2. 文件依賴

前端標準的模塊化規範

  1. AMD - requirejs
  2. CMD - seajs

服務器端的模塊化規範

CommonJS - Node.js

Node模塊化相關的規則

  1. 如何定義模塊:一個js文件就是一個模塊,模塊內部的成員都是相互獨立
  2. 模塊成員的導出和引入: exports與module的關係:module.exports = exports = {}; 模塊成員的導出最終以module.exports爲準 若是要導出單個的成員或者比較少的成員,通常咱們使用exports導出;若是要導出的成員比較多,通常咱們使用module.exports的方式;這兩種方式不能同時使用
var sum = function(a,b){
    return parseInt(a) + parseInt(b);
}
// 方法1
// 導出模塊成員
exports.sum = sum;
//引入模塊
var module = require('./xx.js');
var ret = module.sum(12,13);

// 方法2
// 導出模塊成員
module.exports = sum;
//引入模塊
var module = require('./xx.js');
module();

// // 方法1
// exports.sum = sum;
// exports.subtract = subtract;
// 
// var m = require('./05.js');
// var ret = m.sum(1,2);
// var ret1 = m.subtract(1,2);
// console.log(ret,ret1);
// 
// // 方法2
// module.exports = {
//     sum : sum,
//     subtract : subtract,
//     multiply : multiply,
//     divide : divide
// }
// 
// var m = require('./05.js');
// console.log(m);
複製代碼

##回顧webpack

模塊打包器

根據模塊的依賴關係進行靜態分析,而後將這些模塊按照指定的規則生成對應的靜態資源。如何在一個大規模的代碼庫中,維護各類模塊資源的分割和存放,維護它們之間的依賴關係,而且無縫的將它們整合到一塊兒生成適合瀏覽器端請求加載的靜態資源。市面上已經存在的模塊管理和打包工具並不適合大型的項目,尤爲單頁面 Web 應用程序。最緊迫的緣由是如何在一個大規模的代碼庫中,維護各類模塊資源的分割和存放,維護它們之間的依賴關係,而且無縫的將它們整合到一塊兒生成適合瀏覽器端請求加載的靜態資源。 這些已有的模塊化工具並不能很好的完成以下的目標:

  • 將依賴樹拆分紅按需加載的塊
  • 初始化加載的耗時儘可能少
  • 各類靜態資源均可以視做模塊
  • 將第三方庫整合成模塊的能力
  • 能夠自定義打包邏輯的能力
  • 適合大項目,不管是單頁仍是多頁的 Web 應用

Webpack 的特色

Webapck 和其餘模塊化工具備什麼區別呢?

  1. 代碼拆分 Webpack 有兩種組織模塊依賴的方式,同步和異步。異步依賴做爲分割點,造成一個新的塊。在優化了依賴樹後,每個異步區塊都做爲一個文件被打包。
  2. Loader Webpack 自己只能處理原生的 JavaScript 模塊,可是 loader 轉換器能夠將各類類型的資源轉換成 JavaScript 模塊。這樣,任何資源均可以成爲 Webpack 能夠處理的模塊。
  3. 智能解析 Webpack 有一個智能解析器,幾乎能夠處理任何第三方庫,不管它們的模塊形式是 CommonJS、 AMD 仍是普通的 JS 文件。甚至在加載依賴的時候,容許使用動態表達式 require("./templates/" + name + ".jade")
  4. 插件系統 Webpack 還有一個功能豐富的插件系統。大多數內容功能都是基於這個插件系統運行的,還能夠開發和使用開源的 Webpack 插件,來知足各式各樣的需求。
  5. 快速運行 Webpack 使用異步 I/O 和多級緩存提升運行效率,這使得 Webpack 可以以使人難以置信的速度快速增量編譯。

webpack是什麼?

CommonJS和AMD是用於JavaScript模塊管理的兩大規範,前者定義的是模塊的同步加載,主要用於NodeJS;然後者則是異步加載,經過requirejs等工具適用於前端。隨着npm成爲主流的JavaScript組件發佈平臺,愈來愈多的前端項目也依賴於npm上的項目,或者 自身就會發布到npm平臺。所以,讓前端項目更方便的使用npm上的資源成爲一大需求。 web開發中經常使用到的靜態資源主要有JavaScript、CSS、圖片、Jade等文件,webpack中將靜態資源文件稱之爲模塊。 webpack是一個module bundler(模塊打包工具),其能夠兼容多種js書寫規範,且能夠處理模塊間的依賴關係,具備更強大的js模塊化的功能。Webpack對它們進行統 一的管理以及打包發佈

爲何使用 webpack?

1. 對 CommonJS 、 AMD 、ES6的語法作了兼容 2. 對js、css、圖片等資源文件都支持打包 3. 串聯式模塊加載器以及插件機制,讓其具備更好的靈活性和擴展性,例如提供對CoffeeScript、ES6的支持 4. 有獨立的配置文件webpack.config.js 5. 能夠將代碼切割成不一樣的chunk,實現按需加載,下降了初始化時間 6. 支持 SourceUrls 和 SourceMaps,易於調試 7. 具備強大的Plugin接口,大可能是內部插件,使用起來比較靈活 8.webpack 使用異步 IO 並具備多級緩存。這使得 webpack 很快且在增量編譯上更加快

相關文章
相關標籤/搜索