JS模塊化入門

不知道有多少同窗像我同樣寫了好久的JS代碼😅,可是對JS的模塊化仍是不清楚,這篇文章比較適合新手朋友,通讀以後能對模塊化有一個簡單的概念。前端

什麼是模塊化

模塊化,其實就是在項目中定義若干個相對獨立的可複用的模塊,模塊中包含特定的功能,當你想要用這個功能的時候,只須要引入對應的模塊便可。npm

  • 定義模塊:每一個模塊內部的執行邏輯是不被外界感知的,只是暴露出部分方法和數據
  • 引入模塊:同步或異步加載待引入的代碼,執行並獲取到其暴露的方法和數據

其實模塊化主要是爲了規避和全局屬性的衝突,同時也但願寫模塊的時候能本身維護本身的模塊,多人項目開發的時候不用擔憂和別人變量衝突。編程

模塊化其實也就是某種意義上面的全局,或者和封裝代碼,只不過是封裝的稍微好一點而已,其實也不是全局變量。設計模式

模塊化的好處

  • 避免命名衝突(減小命名空間污染)
  • 更好的分離,按需加載
  • 更高的複用性
  • 高可維護性,下降維護成本

刀耕火種的時代

在早期的時代,若是咱們想要導出一個模塊經常使用的方法就是經過閉包,加持一點設計模式來實現模塊化,比較經典的就是JQuery實現模塊的方式:前端工程化

(function(window) {
    ///   代碼
    
    window.jQuery = window.$ = jQuery; // 經過給window添加屬性而暴露到全局
})(window)
複製代碼

這種封裝模式被不少人模仿,經過匿名函數包裝代碼,所依賴的外部變量傳給這個函數,這個函數內部可使用這些依賴,而後再將模塊自身暴露給window。瀏覽器

雖然這種模式看似很方便,可是其實仍是增長了全局變量,並且當咱們要引用的時候也一樣須要注意引用模塊的順序,否則極可能致使頁面報錯。bash

AMD && CMD

這部分因爲年代久遠,並且如今在項目基本不多會使用,因此這裏做者就只把一些淺顯的知識點加以整理,算是簡單的瞭解一下吧。 AMD和CMD主要是解決在瀏覽器端的異步模塊化編程的需求,其中AMD和CMD最具表明的兩個做品分別是require.js和sea.js,有興趣的能夠詳細瞭解一下。閉包

這二者用法基本相同,區別就是AMD崇尚依賴前置,CMD崇尚的是按需加載,也就是在須要用到某個模塊的時候再進行加載。異步

AMD && CMD基本用法

  • 定義模塊:
defined('模塊名稱', ['依賴的模塊1', '依賴的模塊2', ], function (moudle1, moudle2) {
    
    class item {}
    return item 或者  module.exports = item
});
複製代碼
  • 引入模塊:引入方法和common.js相同,都是用require方法
const moudle = require('./moudle');
複製代碼

這裏須要注意的是,若是當前文件是AMD一個模塊能夠像下面這樣寫:模塊化

defined('test', function () {
        var a = require('a'); // 按需加載,在用到的地方在加載
    });
複製代碼

若是不是,按照AMD規範須要定義成局部require

var a = require(['a'], function () {
        代碼
    });
複製代碼

由於這裏require多是一個異步的形式,這是就須要用回調,若是直接var a = require('a');這樣使用也成功了的話,只能證實這個模塊加載或定義過,不過在全局的話最穩妥的方式仍是上面的那種形式。

AMD和CMD是怎麼實現依賴分析的?????

能夠思考一下當咱們在用的時候:

var utils = require('./utils');
    utils.xxx();
複製代碼

這樣寫的話,JS指定是來不及加載的,可是這個語句是同步執行的,他們是怎麼作的呢????

其實他會先把咱們的函數先執行一下toString方法,拿到函數的字符串,而後用正則一下,看看哪裏用到了require

var functionStr = function.toString();
    var regx = /require\(['"][^)*]['"]\)/ 複製代碼

這樣就能夠拿到咱們項目中須要require的模塊,而後就能夠在使用以前進行加載模塊了。

CommonJS

2009 年 ry 發佈 Node.js 的第一個版本,CommonJS 做爲其中最核心的特性之一,適用於服務端下的場景;歷年來的考察和時間的洗禮,以及前端工程化對其的充分支持,CommonJS 被普遍運用於 Node.js 和瀏覽器:

用法:

  • 引入方法:require方法,得到目標模塊導出值,能夠加載內置模塊,npm模塊和自定義模塊,在commonJS中每個文件都是一個模塊,都是獨立封裝的。
  • 導出方法:module.exports或者export.xxx = xxx;

注意:這裏不要直接修改exports的值

// a.js
console.log(require('./b'));    // {}
// b.js
exports = { name: 'changan' }
複製代碼

上面代碼能夠看出,這樣用的話咱們require的是一個空對象,這是爲何呢?

咱們能夠經過exports的僞代碼來看一下:

// 當咱們要導出模塊的時候,內核幫忙聲明
var exports = moudle.exports = { };
複製代碼

想象一下,當咱們進行例子中的操做的時候,至關於咱們會把exports變量覆蓋掉,可是moudle.exports並無改變,當咱們require模塊的時候仍是會找module.exports對象,因此咱們會拿到一個空對象,這部分須要注意一下。

require的是一個單例

//a.js
    var obj = {
        name: 1
    }
    module.exports = {
        getName: function () {
            return obj.name
        },
        setName: function (name) {
            obj.name = name;
        }
    }
    // ==================
    // b.js
    var a = require('./a');
    console.log('a-1: ', a.getName());  // a-1:1
    a.setName(2222);
    console.log('a-2: ', a.getName());  // a-2:2222
    // ==================
    // c.js
    var a = require('./a');
    var b = require('./b');
    
    console.log('a-3: ', a.getName()); // a-3:2222
複製代碼

這裏仍是值得注意一下的,由於commonJS引入的模塊是單例的,因此這也就解釋了爲何當兩個文件引用同一個模塊以後,一個文件改變了這個模塊的值的時候,另外一個模塊的值也是會改變。在開發中經常會由於不知道這個原理而焦頭爛額,當一個同事改變了模塊的一個值的時候,另外一個引用該模塊的地方也會改變,避免的最好辦法就是不要更改模塊內部的值。

ES6模塊化

ES Module 是語言層面的模塊化方案,由 ES 2015 提出,其規範與 CommonJS 比之 ,導出的值均可以當作是一個具有多個屬性或者方法的對象,能夠實現互相兼容。

  • ES Module 會對靜態代碼分析,即在代碼編譯時進行模塊的加載,在運行時以前就已經肯定了依賴關係(可解決循環引用的問題);
  • ES Module 關鍵字:import export 以及獨有的default關鍵字,肯定默認的導出值;
  • ES Module 中導出的值是一個只讀的值的引用 ,不管基礎類型和複雜類型,而在 CommonJS 中 require 的是值的拷貝,其中複雜類型是值的淺拷貝;
// a.js
export let a = 1;
export function caculate() {
  a++;
};

// b.js
import { a, caculate } from 'a.js';

console.log(a); // 1
caculate();
console.log(a); // 2

a = 2; // Syntax Error: "a" is read-only
複製代碼
相關文章
相關標籤/搜索