精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

談談前端模塊化的演變歷程

開發 前端
Node.js 使用的是 CommonJS 模塊規范,它也是支持 ES 模塊的。在 Node.js 13 之前,ES 模塊是一項實驗性技術,因此,可以通過使用 .mjs 擴展名保存模塊并通過標志訪問它來使用模塊。


隨著前端項目越來越大,代碼復雜性不斷增加,對于模塊化的需求越來越大。模塊化是工程化基礎,只有將代碼模塊化,拆分為合理單元,才具備調度整合的能力。下面就來看看模塊化的概念,以及不同模塊化方案的使用方式和優缺點。

1、模塊概述

由于代碼之間會發生大量交互,如果結構不合理,這些代碼就會變得難以維護、難以測試、難以調試。而使用模塊化就解決了這些問題,模塊化的特點如下:

可重用性: 當應用被組織成模塊時,可以方便的在其他地方重用這些模塊,避免編寫重復代碼,從而加快開發流程;

可讀性: 當應用變得越來越復雜時,如果在一個文件中編寫所有功能,代碼會變得難以閱讀。如果使用模塊設計應用,每個功能都分布在各自的模塊中,代碼就會更加清晰、易讀;

可維護性: 軟件的美妙之處在于進化,從長遠來看,我們需要不斷為應用增加新的功能。當應用被結構化為模塊時,可以輕松添加或刪除功能。除此之外,修復錯誤也是軟件維護的一部分,使用模塊就可以更快速地定位問題。

模塊化是一種將系統分離成獨立功能部分的方法,可以將系統分割成獨立的功能部分,嚴格定義模塊接口,模塊間具有透明性。通過將代碼進行模塊化分隔,每個文件彼此獨立,開發者更容易開發和維護代碼,模塊之間又能夠互相調用和通信,這就是現代化開發的基本模式。

(2)模式

JavaScript 模塊包含三個部分:

  • 導入: 在使用模塊時,需要將所需模塊作為依賴項導入。例如,如果想要創建一個 React 組件,就需導入 react 模塊。要使用像 Lodash 這樣的工具庫,就需要安裝并導入它作為依賴項;
  • 代碼: 模塊具體代碼;
  • 導出: 模塊接口,從模塊中導出的內容可供導入模塊的任何地方使用。

(3)類型

模塊化的貫徹執行離不開相應的約定,即規范。這是能夠進行模塊化工作的重中之重。實現模塊化的規范有很多,比如:AMD、RequireJS、CMD、SeaJS、UMD、CommonJS、ES6 Module。除此之外,IIFE(立即執行函數)也是實現模塊化的一種方案。

本文將介紹其中的六個:

  • IIFE: 立即調用函數表達式
  • AMD: 異步模塊加載機制
  • CMD: 通用模塊定義
  • UMD: 統一模塊定義
  • CommonJS: Node.js 采用該規范
  • ES 模塊: JavaScript 內置模塊系統

2. IIFE

在 ECMAScript 6 之前,模塊并沒有被內置到 JavaScript 中,因為 JavaScript 最初是為小型瀏覽器腳本設計的。這種模塊化的缺乏,導致在代碼的不同部分使用了共享全局變量。

比如,對于以下代碼:

var name = 'JavaScript';
var age = 20;

當上面的代碼運行時,name 和 age 變量會被添加到全局對象中。因此,應用中的所有 JavaScript 腳本都可以訪問全局變量 name 和 age,這就很容易導致代碼錯誤,因為在其他不相關的單元中也可以訪問和修改這些全局變量。除此之外,向全局對象添加變量會使全局命名空間變得混亂并增加了命名沖突的機會。

所以,我們就需要一種封裝變量和函數的方法,并且只對外公開定義的接口。因此,為了實現模塊化并避免使用全局變量,可以使用如下方式來創建模塊:

(function () {
    // 聲明私有變量和函數
 
    return {
        // 聲明公共變量和函數
    }
})();

上面的代碼就是一個返回對象的閉包,這就是我們常說的IIFE(Immediately Invoked Function Expression),即立即調用函數表達式。在該函數中,就創建了一個局部范圍。這樣就避免了使用全局變量(IIFE 是匿名函數),并且代碼單元被封裝和隔離。

可以這樣來使用 IIFE 作為一個模塊:

var module = (function(){
  var age = 20;
  var name = 'JavaScript'
  
  var fn1 = function(){
    console.log(name, age)
  };
  
  var fn2 = function(a, b){
    console.log(a + b)
  };
  
  return {
    age,
    fn1,
    fn2,
  };
})();

module.age;           // 20
module.fn1();         // JavaScript 20
module.fn2(128, 64);  // 192

在這段代碼中,module 就是我們定義的一個模塊,它里面定義了兩個私有變量 age 和 name,同時定義了兩個方法 fn1 和 fn2,其中 fn1 中使用 module 中定義的私有變量,fn2 接收外部傳入參數。最后,module 向外部暴露了age、fn1、fn2。這樣就形成了一個模塊。

當試圖在 module 外部直接調用fn1時,就會報錯:

fn1(); // Uncaught ReferenceError: fn1 is not defined

當試圖在 module 外部打印其內部的私有變量name時,得到的結果是 undefined:

module.name; // undefined

上面的 IIFE 的例子是遵循模塊模式的,具備其中的三部分,其中 age、name、fn1、fn2 就是模塊內部的代碼實現,返回的 age、fn1、fn2 就是導出的內容,即接口。調用 module 方法和變量就是導入使用。

3. CommonJS

(1)概念

① 定義

CommonJS 是社區提出的一種 JavaScript 模塊化規范,它是為瀏覽器之外的 JavaScript 運行環境提供的模塊規范,Node.js 就采用了這個規范。

注意:

  • 瀏覽器不支持使用 CommonJS 規范;
  • Node.js 不僅支持使用 CommonJS 來實現模塊,還支持最新的  ES 模塊。

CommonJS 規范加載模塊是同步的,只有加載完成才能繼續執行后面的操作。不過由于 Node.js 主要運行在服務端,而所需加載的模塊文件一般保存在本地硬盤,所以加載比較快,而無需考慮使用異步的方式。

② 語法

CommonJS 規范規定每個文件就是一個模塊,有獨立的作用域,對于其他模塊不可見,這樣就不會污染全局作用域。在 CommonJS 中,可以分別使用 export 和 require 來導出和導入模塊。在每個模塊內部,都有一個 module 對象,表示當前模塊。通過它來導出 API,它有以下屬性:

  • exports:模塊導出值。
  • filename:模塊文件名,使用絕對路徑;
  • id:模塊識別符,通常是使用絕對路徑的模塊文件名;
  • loaded:布爾值,表示模塊是否已經完成加載;
  • parent:對象,表示調用該模塊的模塊;
  • children:數組,表示該模塊要用到的其他模塊;

③ 特點

CommonJS 規范具有以下特點:

  • 文件即模塊,文件內所有代碼都運行在獨立的作用域,因此不會污染全局空間;
  • 模塊可以被多次引用、加載。第一次被加載時,會被緩存,之后都從緩存中直接讀取結果。
  • 加載某個模塊,就是引入該模塊的 module.exports 屬性,該屬性輸出的是值拷貝,一旦這個值被輸出,模塊內再發生變化不會影響到輸出的值。
  • 模塊加載順序按照代碼引入的順序。

④ 優缺點

CommonJS 的優點:

  • 使用簡單
  • 很多工具系統和包都是使用 CommonJS 構建的;
  • 在 Node.js 中使用,Node.js 是流行的 JavaScript 運行時環境。

CommonJS 的缺點

  • 可以在 JavaScript 文件中包含一個模塊;
  • 如果想在 Web 瀏覽器中使用它,則需要額外的工具;
  • 本質上是同步的,在某些情況下不適合在 Web 瀏覽器中使用。

(2)使用

在 CommonJS 中,可以通過 require 函數來導入模塊,它會讀取、執行 JavaScript 文件,并返回該模塊的 exports 對象,該對象只有在模塊腳本運行完才會生成。

① 模塊導出

可以通過以下兩種方式來導出模塊內容:

module.exports.TestModule = function() {
    console.log('exports');
}

exports.TestModule = function() {
    console.log('exports');
}

則合兩種方式的導出結果是一樣的,module.exports和exports的區別可以理解為:exports是module.exports的引用,如果在exports調用之前調用了exports=...,那么就無法再通過exports來導出模塊內容,除非通過exports=module.exports重新設置exports的引用指向。

當然,可以先定義函數,再導出:

function testModule() {
    console.log('exports');
}

module.exports = testModule;

這是僅導出一個函數的情況,使用時就是這樣的:

testModule = require('./MyModule');

testModule();

如果是導出多個函數,就可以這樣:

function testModule1() {
    console.log('exports1');
}

function testModule2() {
    console.log('exports2');
}

導入多個函數并使用:

({testModule1, testModule2} = require('./MyModule'));

testModule1();
testModule2();

② 模塊導入

可以通過以下方式來導入模塊:

const module = require('./MyModule');

注意,如果 require 的路徑沒有后綴,會自動按照.js、.json和.node的順序進行補齊查找。

③ 加載過程

在  CommonJS 中,require 的加載過程如下:

  1. 優先從緩存中加載;
  2. 如果緩存中沒有,檢查是否是核心模塊,如果是直接加載;
  3. 如果不是核心模塊,檢查是否是文件模塊,解析路徑,根據解析出的路徑定位文件,然后執行并加載;
  4. 如果以上都不是,沿當前路徑向上逐級遞歸,直到根目錄的node_modules目錄。

(3)示例

下面來看一個購物車的例子,主要功能是將商品添加到購物車,并計算購物車商品總價格:

// cart.js

var items = [];

function addItem (name, price) 
    item.push({
    name: name,
    price: price
  });
}

exports.total = function () {
    return items.reduce(function (a, b) {
      return a + b.price;
    }, 0);
};

exports.addItem = addItem;

這里通過兩種方式在 exports 對象上定義了兩個方法:addItem 和 total,分別用來添加購物車和計算總價。

下面在控制臺測試一下上面定義的模塊:

let cart = require('./cart');

這里使用相對路徑來導入 cart 模塊,打印 cart 模塊,結果如下:

cart // { total: [Function], addItem: [Function: addItem] }

向購物車添加一些商品,并計算當前購物車商品的總價格:

cart.addItem('book', 60);
cart.total()  // 60

cart.addItem('pen', 6);
cart.total()  // 66

這就是創建模塊的基本方法,我們可以創建一些方法,并且只公開希望其他文件使用的部分代碼。該部分成為 API,即應用程序接口。

這里有一個問題,只有一個購物車,即只有一個模塊實例。下面來在控制臺執行以下代碼:

second_cart = require('./cart');

那這時會創建一個新的購物車嗎?事實并非如此,打印當前購物車的商品總金額,它仍然是66:

second_cart.total();  // 66

當我們?創建多個實例時,就需要再模塊內創建一個構造函數,下面來重寫 cart.js 文件:

// cart.js

function Cart () {
    this.items = [];
}

Cart.prototype.addItem = function (name, price) {
    this.items.push({
        name: name,
        price: price
    });
}

Cart.prototype.total = function () {
    return this.items.reduce(function(a, b) {
        return a + b.price;
    }, 0);
};

module.export = Cart;

現在,當需要使用此模塊時,返回的是 Cart 構造函數,而不是具有 cart 函數作為一個屬性的對象。下面來導入這個模塊,并創建兩個購物車實例:

Cart = require('./second_cart');

cart1 = new Cart();
cart2 = new Cart();

cart1.addItem('book', 50);
cart1.total();   // 50
cart2.total();   // 50

4. AMD

(1)概念

CommonJS 的缺點之一是它是同步的,AMD 旨在通過規范中定義的 API 異步加載模塊及其依賴項來解決這個問題。AMD 全稱為 Asynchronous Module Definition,即異步模塊加載機制。它規定了如何定義模塊,如何對外輸出,如何引入依賴。

AMD規范重要特性就是異步加載。所謂異步加載,就是指同時并發加載所依賴的模塊,當所有依賴模塊都加載完成之后,再執行當前模塊的回調函數。這種加載方式和瀏覽器環境的性能需求剛好吻合。

① 語法

AMD 規范定義了一個全局函數 define,通過它就可以定義和引用模塊,它有 3 個參數:

define(id?, dependencies?, factory);

其包含三個參數:

  • id:可選,指模塊路徑。如果沒有提供該參數,模塊名稱默認為模塊加載器請求的指定腳本的路徑。
  • dependencies:可選,指模塊數組。它定義了所依賴的模塊。依賴模塊必須根據模塊的工廠函數優先級執行,并且執行的結果應該按照依賴數組中的位置順序以參數的形式傳入工廠函數中。
  • factory:為模塊初始化要執行的函數或對象。如果是函數,那么該函數是單例模式,只會被執行一次;如果是對象,此對象應該為模塊的輸出值。

除此之外,要想使用此模塊,就需要使用規范中定義的 require 函數:

require(dependencies?, callback);

其包含兩個參數:

  • dependencies:依賴項數組;
  • callback:加載模塊時執行的回調函數。

有關 AMD API 的更詳細說明,可以查看 GitHub 上的 AMD API 規范:https://github.com/amdjs/amdjs-api/blob/master/AMD.md。

② 兼容性

該規范的瀏覽器兼容性如下:

③ 優缺點

AMD 的優點:

  • 異步加載導致更好的啟動時間;
  • 能夠將模塊拆分為多個文件;
  • 支持構造函數;
  • 無需額外工具即可在瀏覽器中工作。

AMD 的缺點:

  • 語法很復雜,學習成本高;
  • 需要一個像 RequireJS 這樣的加載器庫來使用 AMD。

(2)使用

當然,上面只是 AMD 規范的理論,要想理解這個理論在代碼中是如何工作的,就需要來看看 AMD 的實際實現。RequireJS 就是 AMD 規范的一種實現,它被描述為“JavaScript 文件和模塊加載器”。下面就來看看 RequireJS 是如何使用的。

① 引入RequireJS

可以通過 npm 來安裝 RequireJS:

npm i requirejs

也可以在 html 文件引入 require.js 文件:

<script data-main="js/config" src="js/require.js"></script>

這里 script標簽有兩個屬性:

  • data-main="js/config":這是 RequireJS 的入口,也是配置它的地方;
  • src="js/require.js":加載腳本的正常方式,會加載 require.js 文件。

在 script 標簽下添加以下代碼來初始化 RequireJS:

<script>
    require(['config'], function() {
        //...
    })
</script>

當頁面加載完配置文件之后, require() 中的代碼就會運行。這個 script 標簽是一個異步調用,這意味著當 RequireJS 通過 src="js/require.js 加載時,它將異步加載 data-main 屬性中指定的配置文件。因此,該標簽下的任何 JavaScript 代碼都可以在 RequireJS 獲取時執行配置文件。

那 AMD 中的 require() 和 CommonJS 中的 require() 有什么區別呢?

  • AMD require() 接受一個依賴數組和一個回調函數,CommonJS require() 接受一個模塊 ID;
  • AMD require() 是異步的,而 CommonJS require() 是同步的。

② 定義 AMD 模塊

下面是 AMD 中的一個基本模塊定義:

define(['dependency1', 'dependency2'], function() {
  // 模塊內容
});

這個模塊定義清楚地顯示了其包含兩個依賴項和一個函數。

下面來定義一個名為addition.js的文件,其包含一個執行加法操作的函數,但是沒有依賴項:

// addition.js
define(function() {
    return function(a, b) {
        alert(a + b);
    }
});

再來定義一個名為 calculator.js 的文件:

define(['addition'], function(addition) {
    addition(7, 9);
});

當 RequireJS 看到上面的代碼塊時,它會去尋找依賴項,并通過將它們作為參數傳遞給函數來自動將其注入到模塊中。

RequireJS 會自動為 addition.js 和 calculator.js 文件創建一個 <script> 標簽,并將其放在HTML <head> 元素中,等待它們加載,然后運行函數,這類似于 require() 的行為。

下面來更新一下 index.html 文件:

// index.html
require(['config'], function() {
    require(['calculator']);
});

當瀏覽器加載 index.html 文件時,RequireJS 會嘗試查找 calculator.js 模塊,但是沒有找到,所以瀏覽器也不會有任何反應。那該如何解決這個問題呢?我們必須提供配置文件來告訴 RequireJS 在哪里可以找到 calculator.js(和其他模塊),因為它是引用的入口。

下面是配置文件的基本結構:

requirejs.config({
    baseURL: "string",
    paths: {},
    shim: {},
});

這里有三個屬性值:

  • baseURL:告訴 RequireJS 在哪里可以找到模塊;
  • path:這些是與 define() 一起使用的模塊的名稱。 在路徑中,可以使用文件的 CDN,這時 RequireJS 將嘗試在本地可用的模塊之前加載模塊的 CDN 版本;
  • shim:允許加載未編寫為 AMD 模塊的庫,并允許以正確的順序加載它們

我們的配置文件如下:

requirejs.config({
    baseURL: "js",
    paths: {
        // 這種情況下,模塊位于 customScripts 文件中
        addition: "customScripts/addition",
        calculator: "customScripts/calculator",
    },
});

配置完成之后,重新加載瀏覽器,就會收到瀏覽器的彈窗:

這就是在 AMD 中使用 RequireJS 定義模塊的方法之一。我們還可以通過指定其路徑名來定義模塊,該路徑名是模塊文件在項目目錄中的位置。 下面給出一個例子:

define("path/to/module", function() {
    // 模塊內容
})

當然,RequireJS 并不鼓勵這種方法,因為當我們將模塊移動到項目中的另一個位置時,就需要手動更改模塊中的路徑名。

在使用 AMD 定義模塊時需要注意:

  • 在依賴項數組中列出的任何內容都必須與工廠函數中的分配相匹配;
  • 盡量不要將異步代碼與同步代碼混用。當在 index.html 上編寫其他 JavaScript 代碼時就是這種情況。

5. CMD

CMD 全稱為 Common Module Definition,即通用模塊定義。CMD 規范整合了 CommonJS 和 AMD 規范的特點。sea.js 是 CMD 規范的一個實現 。

CMD 定義模塊也是通過一個全局函數 define 來實現的,但只有一個參數,該參數既可以是函數也可以是對象:

define(factory);

如果這個參數是對象,那么模塊導出的就是對象;如果這個參數為函數,那么這個函數會被傳入 3 個參數:

define(function(require, exports, module) {
  //...
});

這三個參數分別如下: (1)require:一個函數,通過調用它可以引用其他模塊,也可以調用 require.async 函數來異步調用模塊; (2)exports:一個對象,當定義模塊的時候,需要通過向參數 exports 添加屬性來導出模塊 API; (3)module 是一個對象,它包含 3 個屬性:

  • uri:模塊完整的 URI 路徑;
  • dependencies:模塊依賴;
  • exports:模塊需要被導出的 API,作用同第二個參數 exports。

下面來看一個例子,定義一個 increment 模塊,引用 math 模塊的 add 函數,經過封裝后導出成 increment 函數:

define(function(require, exports, module) {
  var add = require('math').add;
  exports.increment = function(val) {
    return add(val, 1);
  };
  module.id = "increment";
});

CMD 最大的特點就是懶加載,不需要在定義模塊的時候聲明依賴,可以在模塊執行時動態加載依賴。除此之外,CMD 同時支持同步加載模塊和異步加載模塊。

AMD 和 CMD 的兩個主要區別如下:

  • AMD 需要異步加載模塊,而 CMD 在加載模塊時,可以同步加載(require),也可以異步加載(require.async)。
  • CMD 遵循依賴就近原則,AMD 遵循依賴前置原則。也就是說,在 AMD 中,需要把模塊所需要的依賴都提前在依賴數組中聲明。而在 CMD 中,只需要在具體代碼邏輯內,使用依賴前,把依賴的模塊 require 進來。

6. UMD

UMD 全程為 Universal Module Definition,即統一模塊定義。其實 UMD 并不是一個模塊管理規范,而是帶有前后端同構思想的模塊封裝工具。

UMD 是一組同時支持 AMD 和 CommonJS 的模式,它旨在使代碼無論執行代碼的環境如何都能正常工作,通過 UMD 可以在合適的環境選擇對應的模塊規范。比如在 Node.js 環境中采用 CommonJS 模塊管理,在瀏覽器環境且支持 AMD 的情況下采用 AMD 模塊,否則導出為全局函數。

一個UMD模塊由兩部分組成:

  • **立即調用函數表達式 (IIFE)**:它會檢查使用模塊的環境。其有兩個參數:root 和 factory。 root 是對全局范圍的 this 引用,而 factory 是定義模塊的函數。
  • 匿名函數: 創建模塊,此匿名函數被傳遞任意數量的參數以指定模塊的依賴關系。

UMD 的代碼實現如下:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    define([], factory);
  } else if (typeof exports === 'object') {
    module.exports,
    module.exports = factory();
  } else {
    root.returnExports = factory();
  }
}(this, function () {
  // 模塊內容定義
  return {};
}));

它的執行過程如下:

  1. 先判斷是否支持 Node.js 模塊格式(exports 是否存在),存在則使用 Node.js 模塊格式;
  2. 再判斷是否支持 AMD(define 是否存在),存在則使用 AMD 方式加載模塊;
  3. 若兩個都不存在,則將模塊公開到全局(Window 或 Global)。

UMD的特點如下:① UMD 的優點:

  • 小而簡潔;
  • 適用于服務器端和客戶端。

② UMD 的缺點:

  • 不容易正確配置。

7. ES 模塊

(1)概念

通過上面的例子,你可能會發現,使用 UMD、AMD、CMD 的代碼會變得難以編寫和理解。于是在 2015 年,負責 ECMAScript 規范的 TC39 委員會將模塊添加為 JavaScript 的內置功能,這些模塊稱為 ECMAScript模塊,簡稱 ES 模塊。

模塊和經典 JavaScript 腳本略有不同:

  • 模塊默認啟用嚴格模式,比如分配給未聲明的變量會報錯:
<script type="module">
  a = 5; 
</script>
  • 模塊有一個詞法頂級作用域。 這意味著,例如,運行 var foo = 42; 在模塊內不會創建名為 foo 的全局變量,可通過瀏覽器中的 window.foo 訪問,盡管在經典JavaScript腳本中會出現這種情況;
<script type="module">
  let person = "Alok";
</script>

<script type="module">
   alert(person);{/* Error: person is not defined */}
</script>
  • 模塊中的 this 并不引用全局 this,而是 undefined。 (如果需要訪問全局 this,可以使用 globalThis);
<script>
  alert(this); {/* 全局對象 */}
</script>

<script type="module">
  alert(this); {/* undefined */}
</script>
  • 新的靜態導入和導出語法僅在模塊中可用,并不適用于經典腳本。
  • 頂層 await 在模塊中可用,但在經典 JavaScript 腳本中不可用;
  • await 不能在模塊中的任何地方用作變量名,經典腳本中的變量可以在異步函數之外命名為 await;
  • JavaScript 會提升 import 語句。因此,可以在模塊中的任何位置定義它們。

CommonJS 和 AMD 都是在運行時確定依賴關系,即運行時加載,CommonJS 加載的是拷貝。而 ES 模塊是在編譯時就確定依賴關系,所有加載的其實都是引用,這樣做的好處是可以執行靜態分析和類型檢查。

(2)語法

① 導出

當導出模塊代碼時,需要在其前面添加 export 關鍵詞。導出內容可以是變量、函數或類。任何未導出的代碼都是模塊私有的,無法在該模塊之被外訪問。ES 模塊支持兩種類型的導出:

  • 命名導出:
export const first = 'JavaScript';
export function func() {
    return true;
}

當然,我們也可以先定義需要導出的變量/函數,最后統一導出這些變量/函數:

const first = 'JavaScript';
const second = 'TypeScript';
function func() {
    return true;
}
export {first, second, func};
  • 默認導出:
function func() {
    return true;
}

export default func;

當然,也可以直接默認導出:

export default function func() {
    return true;
}

默認導出可以省略變量/函數/類名,在導入時可以為其指定任意名稱:

// 導出
export default function () {
  console.log('foo');
}
// 導入
import customName from './module';

注意: 導入默認模塊時不需要大括號,導出默認的變量或方法可以有名字,但是對外是無效的。export default 在一個模塊文件中只能使用一次。

可以使用 as 關鍵字來重命名需要暴露出的變量或方法,經過重命名后同一變量可以多次暴露出去:

const first = 'test';
export {first as second};

② 導入

使用命名導出的模塊,可以通過以下方式來導入:

import {first, second, func} from './module';

使用默認導出的模塊,可以通過以下方式來引入,導入名稱可以自定義,無論導出的名稱是什么:

import customName from './module.js';

導入模塊位置可以是相對路徑也可以是絕對路徑,.js擴展名是可以省略的,如果不帶路徑而只是模塊名,則需要通過配置文件告訴引擎查找的位置:

import {firstName, lastName} from './module';

可以使用 as 關鍵字來將導入的變量/函數重命名:

import { fn as fn1 } from './profile';

在 ES 模塊中,默認導入和命名導入是可以同時使用的,比如在 React 組件中:

import React, {usestate, useEffect} from 'react';

const Comp = () => {
 return <React.Fragment>...</React.Fragment> 
}

export default Comp;

可以使用 as 關鍵字來加載整個模塊,用于從另一個模塊中導入所有命名導出,會忽略默認導出:

import * as circle from './circle';
console.log('圓面積:' + circle.area(4));
console.log('圓周長:' + circle.circumference(14));

③ 動態導入

上面我們介紹的都是靜態導入,使用靜態 import 時,整個模塊需要先下載并執行,然后主代碼才能執行。有時我們不想預先加載模塊,而是按需加載,僅在需要時才加載。這可以提高初始加載時的性能,動態 import 使這成為可能:

<script type="module">
  (async () => {
    const moduleSpecifier = './lib.mjs';
    const {repeat, shout} = await import(moduleSpecifier);
    repeat('hello');
    // → 'hello hello'
    shout('Dynamic import in action');
    // → 'DYNAMIC IMPORT IN ACTION!'
  })();
</script>

與靜態導入不同,動態導入可以在常規腳本中使用。

④ 其他用法

可以使用以下方式來先導入后導出模塊內容:

export { foo, bar } from './module';

上面的代碼就等同于:

import { foo, bar } from './module';
export { foo, boo};

另一個與模塊相關的新功能是import.meta,它是一個給 JavaScript 模塊暴露特定上下文的元數據屬性的對象。它包含了這個模塊的信息,比如說這個模塊的 URL。

默認情況下,圖像是相對于 HTML 文檔中的當前 URL 加載的。import.meta.url可以改為加載相對于當前模塊的圖像:

function loadThumbnail(relativePath) {
  const url = new URL(relativePath, import.meta.url);
  const image = new Image();
  image.src = url;
  return image;
}

const thumbnail = loadThumbnail('../img/thumbnail.png');
container.append(thumbnail);

(3)在瀏覽器使用

目前主流瀏覽器都支持 ES 模塊:

圖片

如果想在瀏覽器中使用原生 ES 模塊方案,只需要在 script 標簽上添加 type="module" 屬性。通過該屬性,瀏覽器知道這個文件是以模塊化的方式運行的。而對于不支持的瀏覽器,需要通過 nomodule 屬性來指定某腳本為 fallback 方案:

<script type="module">
  import module1 from './module1'
</script>
<script nomodule src="fallback.js"></script>

支持 type="module" 的瀏覽器會忽略帶有 nomodule 屬性的腳本。使用 type="module" 的另一個作用就是進行 ES Next 兼容性的嗅探。因為支持 ES 模塊化的瀏覽器,都支持 ES Promise 等特性。

由于默認情況下模塊是延遲的,因此可能還希望以延遲方式加載 nomodule 腳本:

<script nomodule defer src="fallback.js"></script>

(4)在 Node.js 使用

上面提到,Node.js 使用的是 CommonJS 模塊規范,它也是支持 ES 模塊的。在 Node.js 13 之前,ES 模塊是一項實驗性技術,因此,可以通過使用 .mjs 擴展名保存模塊并通過標志訪問它來使用模塊。

從 Node.js 13 開始,可以通過以下兩種方式使用模塊:

  • 使用 .mjs 擴展名保存模塊;
  • 在最近的文件夾中創建一個 type="module" 的 package.json 文件。

那如何在小于等于 12 版本的 Node.js 中使用 ES 模塊呢?可以在執行腳本啟動時加上 --experimental-modules,不過這一用法要求相應的文件后綴名必須為 .mjs:

node --experimental-modules module1.mjs
import module1 from './module1.mjs'
module1


責任編輯:武曉燕 來源: 前端充電寶
相關推薦

2020-09-17 10:30:21

前端模塊化組件

2020-09-18 09:02:32

前端模塊化

2022-09-05 09:01:13

前端模塊化

2013-08-20 15:31:18

前端模塊化

2022-03-11 13:01:27

前端模塊

2019-12-02 16:05:10

前端模塊化JavaScript

2014-04-27 10:16:31

QCon北京2014Andrew Bett

2019-08-28 16:18:39

JavaScriptJS前端

2016-09-23 11:08:35

前端Javascript模塊化

2013-03-19 10:50:38

2013-03-11 10:00:13

前端模塊化

2018-12-18 11:20:28

前端模塊化JavaScript

2016-10-09 11:03:41

Javascript模塊化Web

2013-03-11 10:10:03

2022-09-21 11:51:26

模塊化應用

2015-10-10 10:01:28

前端模塊化webpack

2017-05-18 10:23:55

模塊化開發RequireJsJavascript

2015-10-10 11:29:45

Java模塊化系統初探

2021-07-14 09:26:51

UPS電源模塊化

2010-05-28 10:31:28

模塊化IT
點贊
收藏

51CTO技術棧公眾號

国产97免费视| 亚洲精品美女在线观看| 成年人三级视频| www.av黄色| 久热精品在线| 久久影视电视剧免费网站| 涩视频在线观看| 暖暖成人免费视频| 亚洲欧美日韩一区| 久久久精品动漫| 中文字幕乱码在线观看| 狠狠色综合网| 中文字幕日韩av| 熟妇高潮一区二区| 黄色精品视频网站| 五月婷婷激情综合| 四虎一区二区| 天天干天天操av| 韩国女主播成人在线| **欧美日韩vr在线| 国产十六处破外女视频| 精品少妇av| 亚洲国产中文字幕在线观看| 91视频这里只有精品| 中文字幕在线视频久| 一区二区三区四区在线播放| 亚洲精品中文字幕在线| 先锋av资源站| 国产91精品欧美| 国产日韩欧美黄色| 探花视频在线观看| 亚洲人成免费| 欧美激情国产精品| 国产中文字幕久久| 欧美综合一区| 亚洲色图五月天| 极品人妻一区二区三区| 国产人妖ts一区二区| 日韩欧美激情四射| 亚洲一区精品视频在线观看| 日韩精品一区二区三区av| 婷婷综合五月天| 99热这里只有精品免费| 欧美日韩xx| 国产精品视频一二三| 久久久com| 日韩偷拍自拍| 91污在线观看| 久久免费看av| 亚洲欧美日韩综合在线| 不卡欧美aaaaa| 国产91免费视频| 黄色小视频免费观看| 国产乱码一区二区三区| 92国产精品久久久久首页| 一级黄色录像大片| 国产一区中文字幕| 91热精品视频| av中文字幕播放| 国产福利91精品一区| 999在线观看免费大全电视剧| 一级黄色免费片| 国产乱理伦片在线观看夜一区| 成人美女av在线直播| 国产伦精品一区二区三区视频痴汉| 美国三级日本三级久久99| 国产精品久久久久久久天堂| 国产精品传媒在线观看| 日本伊人色综合网| 成人欧美一区二区三区在线 | avtt中文字幕| 日韩一区中文| 欧美一区二区国产| 免费观看污网站| 女一区二区三区| 亚洲精品综合久久中文字幕| 色屁屁草草影院ccyy.com| 久久国产成人精品| 欧美精品一二区| 天天操天天干视频| 免费高清在线一区| 亚洲最大成人免费视频| 少妇av在线播放| 久久精品一区蜜桃臀影院| 亚洲区一区二区三区| 成年人黄视频在线观看| 亚洲成年人影院| 999精品网站| 精品一区二区三区在线观看视频| 亚洲а∨天堂久久精品9966| 妺妺窝人体色WWW精品| 天天做天天爱天天爽综合网| 欧美极品第一页| 国产成人麻豆免费观看| 国产在线精品免费| 久久视频在线观看中文字幕| 在线播放麻豆| 午夜精品久久久久久不卡8050| 国模吧无码一区二区三区| 美女久久久久久| 亚洲第一精品自拍| 日本美女xxx| 伊人久久久大香线蕉综合直播| 日本久久久久久久久| www.色播.com| 国产欧美综合在线观看第十页| a级黄色片免费| 日本一道高清亚洲日美韩| 欧美一区二区二区| 黄色a一级视频| 欧美精品18| 国产精品青草久久久久福利99| 天堂av资源在线| 一区二区三区在线播放| 中文字幕av不卡在线| 乱亲女h秽乱长久久久| 久久久国产精品亚洲一区| 特级毛片www| 成人黄色国产精品网站大全在线免费观看| 午夜视频久久久| 美女搞黄视频在线观看| 日韩三级在线免费观看| 97人妻人人揉人人躁人人| 亚洲精选91| 91麻豆蜜桃| 麻豆视频在线观看免费网站| 色老头久久综合| 精品无码国产一区二区三区51安| 欧美黄色精品| 成人a在线观看| 最新电影电视剧在线观看免费观看| 婷婷开心激情综合| 香蕉视频在线观看黄| 国产精品成人a在线观看| 日韩av黄色在线观看| 天天色综合av| 精品国产户外野外| 无码人妻一区二区三区在线| 欧美国产91| 亚洲自拍在线观看| 成人在线免费看片| 欧美日韩成人综合天天影院| 国产黄色大片免费看| 久久国产主播| 欧美在线播放一区| 国产成人精品一区二三区在线观看| 日韩精品中文字幕视频在线| 黄色片免费观看视频| eeuss国产一区二区三区| 日韩av中文字幕第一页| ccyy激情综合| 97视频在线播放| 色窝窝无码一区二区三区| 亚洲不卡在线观看| av黄色一级片| 免费视频一区| 欧美尤物一区| 日日狠狠久久| 欧美成人精品影院| 亚洲h视频在线观看| 亚洲一区在线电影| 2一3sex性hd| 国产日韩视频| 日韩在线观看电影完整版高清免费| se01亚洲视频| 综合av色偷偷网| 精品久久久久久亚洲综合网站| 亚洲精品大片www| 国产精品久久久久久在线观看| 91久久夜色精品国产九色| 久久精品一区二区三区不卡免费视频| 免费一二一二在线视频| 国产亚洲欧洲高清| 国产孕妇孕交大片孕| 一区二区三区高清在线| 精品国产一区在线| 欧美aaaaaa午夜精品| 国产又粗又硬又长| 久久99国产精品久久99大师 | 成人黄色在线看| 啊啊啊一区二区| 成人免费看片39| 1卡2卡3卡精品视频| 日韩脚交footjobhd| 中文字幕无线精品亚洲乱码一区 | 免费看国产曰批40分钟| 免费av一区| 91手机视频在线观看| 国产精品一二三产区| 亚洲视频在线看| 亚洲AV无码乱码国产精品牛牛| 欧美日韩中文字幕综合视频| 欧美aaa级片| 成人性生交大合| 五月婷婷丁香综合网| 在线观看不卡| 色综合影院在线观看| 亚洲精品a区| 国产精品99免视看9| 男人天堂亚洲天堂| 色噜噜亚洲精品中文字幕| 欧美一级淫片免费视频魅影视频| 91福利精品视频| 久青草视频在线观看| 国产精品区一区二区三区| chinese麻豆新拍video| 久久国内精品自在自线400部| 国精产品一区一区三区视频| 久久久国产精品| 欧美日韩精品中文字幕一区二区| 日本一区二区乱| 国产精品日韩久久久久| 小草在线视频免费播放| 久久福利网址导航| yw视频在线观看| 精品视频偷偷看在线观看| 国产av无码专区亚洲av| 欧美色大人视频| 中文字幕第四页| 午夜伊人狠狠久久| 青青操国产视频| 一区二区中文字幕在线| 国产视频三区四区| 久久亚洲综合av| 午夜免费福利影院| 国产精品18久久久久| 在线观看av网页| 视频一区国产视频| 99视频在线免费播放| 好看的亚洲午夜视频在线| 男女激烈动态图| 午夜精品一区二区三区国产 | 欧美日韩国产精品一区二区亚洲| 亚洲日本无吗高清不卡| 欧美一区三区| 色99中文字幕| 国产精品一线天粉嫩av| 久久综合久久久| 亚洲区小说区| 久久久一本精品99久久精品| 日韩激情毛片| 好看的日韩精品视频在线| а√中文在线天堂精品| 波多野结衣一区二区三区在线观看| 粉嫩一区二区三区在线观看| 91深夜福利视频| 国产一区二区三区视频在线| 91在线视频成人| 免费一级欧美片在线观看网站| 成人免费自拍视频| 日韩三级精品| 国产精品成人一区二区三区| 超碰成人免费| 国产视频不卡| 同性恋视频一区| 品久久久久久久久久96高清| 欧美色图一区| 在线视频不卡一区二区| 午夜影院欧美| 日韩成人手机在线| 国产欧美一区二区色老头 | 成人丝袜18视频在线观看| 国内自拍偷拍视频| 成人小视频免费观看| 亚洲调教欧美在线| 久久久久久毛片| ass极品国模人体欣赏| 亚洲欧美日韩成人高清在线一区| 精品国产乱码久久久久久鸭王1| 亚洲综合丁香婷婷六月香| 日韩欧美大片在线观看| 91国偷自产一区二区三区观看| 伊人网av在线| 日韩视频在线你懂得| 精品国产av鲁一鲁一区| 亚洲精品国产精品久久清纯直播 | 成久久久网站| 超碰10000| 国产精品外国| 国产精品一区二区小说| 国产成人在线影院| 国产特黄级aaaaa片免| 国产精品久久久99| 日本三级片在线观看| 欧洲精品中文字幕| 国产xxxx在线观看| 亚洲男人的天堂在线播放| 最近高清中文在线字幕在线观看| 欧美夫妻性生活视频| 丝袜老师在线| 亚洲一区二区三区sesese| 欧美日韩另类图片| 天堂av免费看| 美女精品网站| 欧美69精品久久久久久不卡| 久久久国产精品麻豆| 久久在线视频精品| 在线中文字幕不卡| 午夜精品无码一区二区三区 | 91香蕉在线观看| 奇米影视亚洲狠狠色| 日韩精品久久久久久久软件91| 欧美深深色噜噜狠狠yyy| 欧美成人有码| av网站在线不卡| 91免费小视频| 久久免费小视频| 欧美人xxxx| 欧洲天堂在线观看| 国语自产在线不卡| www.久久爱.com| 日韩av不卡播放| 在线日韩视频| 宇都宫紫苑在线播放| 中文字幕欧美国产| 国产精品500部| 欧美不卡123| 国产原厂视频在线观看| 国产精品igao视频| 先锋影音国产精品| 国产黄色激情视频| 国产在线精品一区二区三区不卡| 婷婷色一区二区三区| 欧美日韩一区二区在线| 午夜精品久久久久久久96蜜桃 | 日本一区二区三区免费观看| 亚洲午夜在线| 香蕉网在线视频| 亚洲乱码国产乱码精品精98午夜| 亚洲中文一区二区三区| 亚洲视频在线视频| 国产精品迅雷| 蜜桃av噜噜一区二区三| 国产精品毛片在线| 捆绑裸体绳奴bdsm亚洲| 亚洲激情第一区| 午夜精品久久久久久久99热黄桃| 欧美成人免费大片| 国产精品一区二区美女视频免费看 | www.这里只有精品| 日本一区二区高清| 中文字幕欧美人妻精品| 亚洲人成网7777777国产| 中文字幕色婷婷在线视频| 免费在线观看91| 三级一区在线视频先锋| 欧美丰满美乳xxⅹ高潮www| 在线中文字幕一区| avtt亚洲| 成人写真福利网| 自拍日韩欧美| 亚洲熟妇一区二区| 亚洲电影激情视频网站| 日韩中文字幕综合| 国产91精品久久久久久久| 伊甸园亚洲一区| 网站一区二区三区| 中文字幕一区免费在线观看| 精品国产区一区二| 97视频在线观看视频免费视频 | 一区二区不卡视频| 精品一区中文字幕| 久久精品国产亚洲av无码娇色| 亚洲国产日韩欧美在线99| 波多视频一区| 亚洲精品日韩精品| 国产伦精品一区二区三区视频青涩| 久久久久久久国产视频| 日韩经典一区二区三区| 秋霞国产精品| 国产女人18毛片| 99久久久国产精品免费蜜臀| 综合久久中文字幕| 久久亚洲一区二区三区四区五区高| swag国产精品一区二区| 无码精品国产一区二区三区免费| 欧美激情综合网| www.av导航| 国产91免费观看| 艳女tv在线观看国产一区| 一级特级黄色片| 欧美午夜精品免费| 欧洲性视频在线播放| 秋霞毛片久久久久久久久| 国内精品久久久久影院一蜜桃| 亚洲国产综合久久| 在线视频日韩精品| xvideos.蜜桃一区二区| 天天视频天天爽| 午夜激情一区二区| 老司机精品影院| 久久久久久一区| 国产精品夜夜嗨| 最新国产中文字幕| 97免费视频在线| 91精品福利| 四虎永久免费在线观看| 日韩一区二区三区四区五区六区| 日韩av大片站长工具| 妞干网在线播放|