如今的前端開發, 不只僅是完成瀏覽的基本需求,而且一般是一個單頁面應用,每個視圖經過異步的方式加載,這致使頁面初始化和使用過程當中會加載愈來愈多的 JavaScript 代碼. 如何在開發環境組織好這些碎片化的代碼和資源,而且保證他們在瀏覽器端快速、優雅的加載和更新,就須要一個模塊化系統css
把函數做爲模塊 缺陷: 污染全局變量 模塊成員之間沒什麼關係 面向對象思想 並使用當即執行函數 實現閉包 避免了變量污染 同時同一模塊內的成員也有了關係 在模塊外部沒法修改咱們沒有暴露出來的變量、函數 這就是簡單的模塊前端
模塊的加載和傳輸,咱們首先能想到兩種極端的方式,一種是每一個模塊文件都單獨請求,另外一種是把全部模塊打包成一個文件而後只請求一次。顯而易見,每一個模塊都發起單獨的請求形成了請求次數過多,致使應用啓動速度慢;一次請求加載全部模塊致使流量浪費、初始化過程慢。這兩種方式都不是好的解決方案,它們過於簡單粗暴。node
分塊傳輸,按需進行懶加載,在實際用到某些模塊的時候再增量更新,纔是較爲合理的模塊加載方案。要實現模塊的按需加載,就須要一個對整個代碼庫中的模塊進行靜態分析、編譯打包的過程。jquery
在上面的分析過程當中,咱們提到的模塊僅僅是指JavaScript模塊文件。然而,在前端開發過程當中還涉及到樣式、圖片、字體、HTML 模板等等衆多的資源。若是他們均可以視做模塊,而且均可以經過require
的方式來加載,將帶來優雅的開發體驗,那麼如何作到讓 require
能加載各類資源呢?在編譯的時候,要對整個代碼進行靜態分析,分析出各個模塊的類型和它們依賴關係,而後將不一樣類型的模塊提交給適配的加載器來處理。Webpack 就是在這樣的需求中應運而生。webpack
<script>
的書寫順序進行加載服務器端的 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
缺點:gulp
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 = ...
})
複製代碼
優勢:
缺點:
5.UMD
6.ES6模塊
ES6 模塊的設計思想,是儘可能的靜態化,使得編譯時就能肯定模塊的依賴關係,以及輸入和輸出的變量。CommonJS 和 AMD 模塊,都只能在運行時肯定這些東西。
import "jquery";
export function doStuff() {}
module "localModule" {}
複製代碼
優勢:
缺點:
實現:
從前有兩個規範,一個是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
模塊化規範
Webpack 對 CommonJS 的 AMD 的語法作了兼容, 方便遷移代碼
不過實際上, 引用模塊的規則是依據 CommonJS 來的
require('lodash') // 從模塊目錄查找
require('./file') // 按相對路徑查找
複製代碼
AMD 語法中, 也要注意, 是按 CommonJS 的方案查找的
define (require, exports. module) ->
require('lodash') # commonjs 當中這樣是查找模塊的
複製代碼
在一開始,咱們先講一下它和以往咱們所用的模塊管理工具備什麼不同。在最開始的階段,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支持哪些功能特性:
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'
複製代碼
CommonJS - Node.js
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 應用程序。最緊迫的緣由是如何在一個大規模的代碼庫中,維護各類模塊資源的分割和存放,維護它們之間的依賴關係,而且無縫的將它們整合到一塊兒生成適合瀏覽器端請求加載的靜態資源。 這些已有的模塊化工具並不能很好的完成以下的目標:
Webapck 和其餘模塊化工具備什麼區別呢?
require("./templates/" + name + ".jade")
。CommonJS和AMD是用於JavaScript模塊管理的兩大規範,前者定義的是模塊的同步加載,主要用於NodeJS;然後者則是異步加載,經過requirejs等工具適用於前端。隨着npm成爲主流的JavaScript組件發佈平臺,愈來愈多的前端項目也依賴於npm上的項目,或者 自身就會發布到npm平臺。所以,讓前端項目更方便的使用npm上的資源成爲一大需求。 web開發中經常使用到的靜態資源主要有JavaScript、CSS、圖片、Jade等文件,webpack中將靜態資源文件稱之爲模塊。 webpack是一個module bundler(模塊打包工具),其能夠兼容多種js書寫規範,且能夠處理模塊間的依賴關係,具備更強大的js模塊化的功能。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 很快且在增量編譯上更加快