JS模塊化,Javascript 模塊化管理的歷史

模塊管理這個概念其實在前幾年前端度過了刀耕火種年代以後就一直被提起。html

直接回想起來的就是 cmd amd commonJS 這三大模塊管理的印象。接下來,咱們來詳細聊聊。前端

1、什麼是模塊化開發

爲了讓一整個龐大的項目看起來整整齊齊,規規整整而出現的模塊化管理,咱們常見的三種模塊化管理的庫: requireJS、seaJS、commonJS規範 ( 注意,這裏的commonJS不是一個庫,而是一種規範) 逐漸的在咱們平常的開發中出現。 同時依賴於 這三類規範而出現的一些構建工具,但最後都敗給了 webpack 。這是一篇介紹 webpack 基本入門的文章,能夠結合着這篇文章來進行解讀。 《前端之路》之 webpack 4.0+ 的應用構建webpack

1-一、模塊化第一階段

在這個階段中,咱們經常會把很是多複雜的功能 封裝成一個個的函數:程序員

function f1() {
    // todo
}

function f2() {
    // todo
}
.
.
.

可是當整個項目變大了之後,就會遇到不少問題,都是定義的全局變量,造成了比較嚴重的污染,還有可能會出現由於重命名致使的各類問題。web

因此這些是須要進化的。因此就會進入到模塊化的第二階段: 對象。後端

1-二、封裝到對象

到了第二個階段爲了不全局變量的污染,咱們會將單個模塊封裝到一個對象內部。以下:瀏覽器

const module = {
    _number: 10,
    f1: () => { 
        console.log(123)
    },
    f2: () => { 
        console.log(456)
    },
    .
    .
    .
}

這樣咱們就設個模塊定義一個對象,在須要的時候直接調用就行了,可是這樣也會存在一個問題 這樣寫的話會暴露所有的對象內部的屬性,內部狀態能夠被外部改變. 例如:緩存

module._number = 100

若是咱們支付相關模塊這樣子來寫的話。咱們隨意的來改變支付的金額,那樣就會出現比較危險的狀況。模塊化

1-三、 對象的優化

後來,聰明的人類就想到了利用 當即執行函數 來達到 不暴露私有成員的目的函數

const module2 = (function() {
    let _money = 100
    const m1 = () => {
        console.log(123)
    }
    const m2 = () => {
        console.log(456)
    }
    return {
        f1: m1,
        f2: m2
    }
})()

經過當即執行函數,讓外部根本沒有時間從外部去修改內部的屬性,從而達到必定的防護做用。

以上就是模塊化開發的基礎中的基礎。 沒有庫,沒有規範,一切的維護都是靠人力,一切的創新,都是來源於 解放生產力。

2、模塊化管理的發展歷程

2-一、CommonJS

CommonJS 的出發點: JS 沒有完善的模塊系統,標準庫較少,缺乏包管理工具。(雖然這些東西,在後端語言中已是 早就被玩的不想玩的東西了) 伴隨着 NodeJS 的興起,能讓 JS 能夠在任何地方運行,特別是服務端,以達到 也具有 Java、C#、PHP這些後臺語言具有開發大型應用的能力,因此 CommonJS 應運而生。

2-1-一、 CommonJS常見規範

  • 一個文件就是一個模塊,擁有單獨的做用域
  • 普通方式定義的 變量、函數、對象都屬於該模塊內
  • 經過 require 來加載模塊
  • 經過 exports 和 module.exports 來暴露模塊中的內容

咱們經過編寫一個 Demo 來嘗試寫這個規範

Demo 1 : 經過 module.exports 來導出模塊

// module.js
module.exports = {
  name: "zhang",
  getName: function() {
    console.log(this.name);
  },
  changeName: function(n) {
    this.name = n;
  }
};

// index.js
const module = require("./module/index");
console.log(module)    //  {name: "zhang", getName: ƒ, changeName: ƒ} "commons"

Demo 2 : 經過 exports 來導出模塊

// module1.js
const getParam = () => {
  console.log(a);
};
let a = 123;
let b = 456;

exports.a = a;
exports.b = b;
exports.getParam = getParam;

// index.js
const module1 = require("./module/index1");
consoel.log(module1, "commons1")    // {a: 123, b: 456, getParam: ƒ} "commons1"

Demo 3 : 同時存在 exports 和 module.exports 來導出模塊

// module2.js
let a = 123;

const getSome = () => {
  console.log("yyy");
};

const getA = () => {
  console.log(a);
};

exports.getSome = getSome;
module.exports = getA;

// index.js
const module2 = require("./module/index2");
consoel.log(module2, "commons2")    // function getA() {...}

總結 : 經過這樣的一個對比的例子就能夠比較清晰的對比出 exports 和 module.exports 的區別: 一、當 exports 和 module.exports 同時存在的時候,module.exports 會蓋過 exports 二、當模塊內部所有是 exports 的時候, 就等同於 module.exports 三、最後 咱們就能夠認定爲 exports 其實就是 module.exports 的子集。

以上就是咱們對於 CommonJS 的一個初級的介紹。 還有一個硬性的規範,這裏咱們只是作一下列舉,就不作詳細的 Demo 演示了。

2-1-二、 CommonJS 規範 --- 加載、做用域

全部代碼都運行在模塊做用域,不會污染全局做用域;模塊能夠屢次加載,但只會在第一次加載的時候運行一次,而後運行結果就被緩存了,之後再加載,就直接讀取緩存結果;模塊的加載順序,按照代碼的出現順序是同步加載的;

2-1-三、 CommonJS 規範 --- __dirname、__filename

__dirname表明當前模塊文件所在的文件夾路徑,__filename表明當前模塊文件所在的文件夾路徑+文件名;

2-二、CommonJS 與 ES6(ES2015) 的 import export

在 ES2015 標準爲出來以前,最主要的是CommonJS和AMD規範。上文中咱們已經介紹了 CommonJS 規範(主要是爲了服務端 NodeJS 服務),那麼當 ES6標準的出現,爲瀏覽器端 模塊化作了一個很是好的補充。

2-2-一、 ES6 的 export

export用於對外輸出本模塊(一個文件能夠理解爲一個模塊)變量的接口

Demo 1 export { xxx }

// export/index.js
const a = "123";
const fn = () => window.location.href;

export { fn };

// show/index.js
const ex = require("./export/index");
import x from "./export/index";
import { fn } from "./export/index";
console.log(ex, "export1"); // {fn: ƒ, __esModule: true} "export1"
console.log(x, "export-x"); // undefined "export-x"
console.log(fn, "export-fn"); // function() { return window.location.href; } "export-x"

Demo 2 export default xxx

 
   
// export/index1.js
const a = "123";
const fn = () => window.location.href;
export default fn;


// show/index1.js
const ex1 = require("./export/index1");
import x from "./export/index1";

console.log(ex1, "export1"); 
// {default: ƒ, __esModule: true} "export1"
console.log(x, "export2"); 
// ƒ fn() {return window.location.href;} "export2"

經過 Demo 1 和 Demo 2 咱們能夠很好的 對比了下 export 和 export default 的區別

export 能夠導出的是一個對象中包含的多個 屬性,方法。 export default 只能導出 一個 能夠不具名的 對象。

import {fn} from './xxx/xxx' ( export 導出方式的 引用方式 ) import fn from './xxx/xxx1' ( export default 導出方式的 引用方式 )

同時,咱們發現 能夠直接使用 require 來引用

這個也能引用其實有點神奇的。可是功能和 import 同樣。(緣由就是我這裏起了 webpack 的 server 相關)

2-2-二、 ES6 的 import

這裏就同上面 Demo 中的例子同樣了得出的結論就是

import {fn} from './xxx/xxx' ( export 導出方式的 引用方式 ) import fn from './xxx/xxx1' ( export default 導出方式的 引用方式 ,能夠不用在乎導出模塊的模塊名)

總結: 以前對於 模塊的導出、引用 的概念都比較的魔幻,此次經過知識的梳理終於搞清楚了。😄


4、總結

4-一、 爲何會有這個東西?

方便組織你的代碼,提升項目的可維護性。一個項目的可維護性高不高,也體現一個程序員的水平,在現在愈來愈複雜的前端項目,這一點尤其重要。

4-二、 爲何不用requirejs,seajs等

它們功能強大,可是文件體積是個問題,此外還有就是業務有時候可能沒那麼複雜。

4-三、 適用場景

移動端頁面,將js注入到html頁面,這樣就不用考慮模塊加載的問題,從而節省了不少的代碼,在實現上也更爲的簡單。 若是是多文件加載的話,須要手動執行文件加載順序,那麼其實最好用庫來進行依賴管理會好一點。

4-四、 現實狀況

webpack + commonJS + ES6 (import + export )

這樣來 實現模塊管理,實現 較大項目的管理。 好了,模塊化管理就先介紹到這裏了,歡迎一塊兒探討

相關文章
相關標籤/搜索