精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

99热这里只有精品在线观看| 强伦人妻一区二区三区| 污影院在线观看| av在线不卡电影| 国产精品www色诱视频| 国产免费美女视频| 国产精品香蕉| 欧美色网站导航| 91黄色在线看| h网站在线免费观看| 国产大片一区二区| 国产成人精彩在线视频九色| 国产成人综合在线视频| 欧美亚洲色图校园春色| 巨胸喷奶水www久久久| 国产福利精品一区二区| 日韩女优在线播放| 免费无码毛片一区二区app| 亚洲人成网www| 日韩丝袜美女视频| 五月婷婷深爱五月| 国产精品—色呦呦| 国产精品二区一区二区aⅴ污介绍| 99国产超薄肉色丝袜交足的后果| 免费视频久久久| 欧美日韩ab| 在线日韩欧美视频| 中文字幕一区三区久久女搜查官| 美女视频一区| 日韩欧美在线字幕| 日韩精品一区二区三区四| 国产三级视频在线| 成人高清在线视频| 91在线免费观看网站| 日韩精品久久久久久免费| 欧美日韩一区二区国产| 久久人人爽人人爽爽久久| 欧美黄色一级生活片| 国产精品nxnn| 精品国产一二三| 手机在线视频一区| 精品国产美女a久久9999| 一本大道综合伊人精品热热| www.九色.com| 亚洲91av| 一区二区三区在线视频免费观看| 中日韩在线视频| yourporn在线观看中文站| 91美女片黄在线观看91美女| 国产嫩草一区二区三区在线观看| 精品国产亚洲一区二区麻豆| 狠狠色狠狠色综合日日91app| 国产精品美女免费视频| 中文字幕 人妻熟女| 日韩中文字幕不卡| 秋霞成人午夜鲁丝一区二区三区| 国产精品老女人| 亚洲人成在线影院| 97在线视频观看| 五月婷婷视频在线| 午夜一区不卡| 国产精品高清在线观看| 中文字幕av影视| 久久99这里只有精品| 国产精品一区=区| 91成年人视频| 国产福利精品一区| 国产视频不卡| 欧美性孕妇孕交| 国产欧美日韩另类视频免费观看| 亚洲不卡中文字幕| 麻豆网站在线观看| 亚洲综合色网站| 久久久性生活视频| 韩日精品一区二区| 欧美伊人久久大香线蕉综合69 | 超碰在线网址| 一区二区三区不卡在线观看| 黄色成人在线看| 美女一区网站| 555www色欧美视频| 一区二区三区四区影院| 婷婷综合电影| 最新中文字幕亚洲| 久草成人在线视频| 久久精品动漫| 91麻豆桃色免费看| 内射后入在线观看一区| 久久人人爽爽爽人久久久| 手机在线观看国产精品| 最新国产在线拍揄自揄视频| 亚洲国产你懂的| 久久婷婷国产91天堂综合精品| 欧美韩国日本| 亚洲黄在线观看| 一本在线免费视频| 在线观看一区视频| 国产精品丝袜白浆摸在线| 国产国语亲子伦亲子| 99免费精品在线观看| 亚洲精品永久www嫩草| 污视频在线看网站| 欧美无人高清视频在线观看| 无码人妻少妇色欲av一区二区| 日韩电影不卡一区| 久久久精品电影| 无码人妻丰满熟妇精品 | 成人网在线免费观看| 日韩在线视频免费| 国产精品日韩精品欧美在线| 大荫蒂性生交片| 国产一区影院| 亚洲国产精彩中文乱码av| 久久日免费视频| 国产日韩1区| 成人av中文| 二区三区四区高清视频在线观看| 色视频成人在线观看免| av漫画在线观看| 欧美电影《睫毛膏》| 欧美与欧洲交xxxx免费观看| 国产chinasex对白videos麻豆| 久久亚洲一级片| 免费看黄在线看| 精品国产欧美| 色偷偷噜噜噜亚洲男人| 99超碰在线观看| 91在线免费视频观看| 精品一区二区三区无码视频| 91丨精品丨国产| 在线免费观看羞羞视频一区二区| www.国产com| 97se狠狠狠综合亚洲狠狠| 日韩欧美视频免费在线观看| www.久久久久爱免| 日韩在线中文字| www.av88| 久久影院午夜片一区| 免费一级特黄毛片| 99久久免费精品国产72精品九九| 久久久国产一区二区三区| 在线观看国产一区二区三区| 日本一区二区三区高清不卡| 99爱视频在线| 全国精品免费看| 欧美亚洲日本网站| 日韩a在线观看| 欧美日韩加勒比精品一区| 午夜视频在线观看国产| 日韩一区二区久久| 国产主播一区二区三区四区| 国产三线在线| 亚洲国产欧美一区二区三区同亚洲 | 国产精品久久久久久久久晋中| 日日摸天天爽天天爽视频| 天天做夜夜做人人爱精品 | 亚洲国产av一区二区三区| 久久久五月婷婷| 日韩中文字幕组| 欧美综合另类| 成人网欧美在线视频| 1区2区3区在线视频| 欧美mv日韩mv亚洲| 1级黄色大片儿| 久久久影视传媒| www日韩在线观看| 日韩黄色大片| 亚洲最大的免费| 女同一区二区免费aⅴ| 亚洲福利视频久久| 337p粉嫩色噜噜噜大肥臀| 日本一区二区三区dvd视频在线| 国产成人黄色网址| 欧美91视频| 麻豆av一区| 丰满少妇一区| 色综合久综合久久综合久鬼88| 丰满熟妇人妻中文字幕| 欧美日韩裸体免费视频| 一区二区黄色片| 国产一区二区三区蝌蚪| 国产精品国产对白熟妇| 亚洲男人都懂第一日本| 国产免费成人av| 超免费在线视频| 亚洲天堂开心观看| av高清一区二区| 婷婷久久综合九色国产成人| 五月婷婷综合在线观看| 免费黄网站欧美| 欧美黑人在线观看| 精品国产精品| 国产超碰91| 三级成人黄色影院| 欧美激情2020午夜免费观看| 美州a亚洲一视本频v色道| 日韩网站在线看片你懂的| 成年人av网站| 一个色综合av| 国产精品理论在线| 成人污视频在线观看| 超碰av在线免费观看| 亚洲一级黄色| 亚洲一区二区在线看| 欧美wwwwww| 亚洲在线www| 成人黄色免费网站| 538国产精品一区二区免费视频| 国产在线一区二区视频| 亚洲女人初尝黑人巨大| 成 人 免费 黄 色| 欧美日韩一区中文字幕| 国产三级av片| 亚洲香蕉伊在人在线观| 日本少妇aaa| 久久久久国产精品免费免费搜索| 又色又爽又黄18网站| 老司机免费视频一区二区 | 成人香蕉视频| 高清欧美性猛交xxxx黑人猛交| www.亚洲.com| 国产视频精品xxxx| 亚洲第一大网站| 欧美人狂配大交3d怪物一区| 女人十八岁毛片| 亚洲人成网站影音先锋播放| 欧美一区二区三区粗大| 久久午夜国产精品| 无码精品一区二区三区在线播放| 国产精品白丝jk黑袜喷水| 国产3p在线播放| 日日夜夜精品免费视频| 日本在线观看a| 一区二区三区国产在线| 日韩视频免费播放| 海角社区69精品视频| 黄色网zhan| 天天做综合网| 中文字幕中文字幕在线中一区高清| 九九综合在线| 欧美日韩一区二区三区在线观看免| 国产精品qvod| 精品乱码一区二区三区| 精品资源在线| 国产精选在线观看91| 97久久亚洲| 国产一区免费| 人体久久天天| 欧美高清性xxxxhdvideosex| 奇米影视777在线欧美电影观看| 国产一区二区久久久| 欧美黑白配在线| 蜜桃91精品入口| jvid福利在线一区二区| 亚洲精品一区二区三区四区五区 | 欧美性潮喷xxxxx免费视频看| 欧美成人嫩草网站| 日韩网站在线免费观看| 亚洲美女少妇无套啪啪呻吟| 播放灌醉水嫩大学生国内精品| 国产一区二区高清| 日本美女高潮视频| 理论电影国产精品| 三级网站免费看| 成人黄色a**站在线观看| 国产美女视频免费观看下载软件| 99久久免费国产| av男人的天堂av| 中文字幕在线观看一区二区| 国产人妻精品一区二区三区不卡| 一区二区三区日韩精品视频| 在线观看中文字幕视频| 欧美视频中文字幕| 99久久亚洲精品日本无码 | 国产理论电影在线观看| 日韩中文字幕网| heyzo中文字幕在线| 欧美夜福利tv在线| 欧美成人福利| 国产伦精品一区二区三区视频孕妇 | 国产一区二区在线看| 国产香蕉精品视频| 久久精品一区四区| 亚洲成人生活片| 欧美日韩午夜剧场| 一级日韩一级欧美| 亚洲成人aaa| 北条麻妃在线| 欧美精品久久久久a| 午夜精品成人av| 亚洲xxxx视频| 国产综合久久久| 国产青草视频在线观看| 日韩二区三区四区| 无码人妻一区二区三区在线| 久久久久9999亚洲精品| 午夜精品福利在线视频| 欧美性猛交xxxx免费看漫画| 国产美女裸体无遮挡免费视频| 亚洲精品电影久久久| 毛片网站在线免费观看| 欧日韩不卡在线视频| 日韩在线亚洲| 神马欧美一区二区| 在线视频精品| 国产人妻精品久久久久野外| 国产午夜三级一区二区三| 久久免费精彩视频| 欧美日本在线一区| 你懂的视频在线观看| 久久久久国色av免费观看性色| 国产成人福利夜色影视| 国语精品免费视频| 欧美一区免费| 奇米影视四色在线| 久久综合色一综合色88| 久久丫精品久久丫| 91精品国产福利| 欧美一区二区三区| 国产精品h在线观看| 少妇高潮一区二区三区| 2019日韩中文字幕mv| 狠狠色狠狠色合久久伊人| 色一情一交一乱一区二区三区| 亚洲韩国精品一区| 亚洲黄色小说网| 欧美成aaa人片免费看| 95精品视频| 伊人久久大香线蕉综合75| 日本vs亚洲vs韩国一区三区二区| 黄色在线免费播放| 艳妇臀荡乳欲伦亚洲一区| 国产精品伊人久久 | 午夜剧场免费在线观看| 精品视频999| 婷婷五月在线视频| 国产精品视频xxx| 日韩欧美精品| 中日韩av在线播放| 中文字幕日韩精品一区| 一级片aaaa| xvideos亚洲人网站| 日韩一区二区三区四区五区| 亚洲欧美99| 麻豆精品精品国产自在97香蕉| 免费一级做a爰片久久毛片潮| 狠狠干狠狠久久| 国际av在线| 国产精品青草久久久久福利99| 日韩av在线播放网址| 欧美成人福利在线观看| 一区二区中文视频| 99在线观看免费| 久久久久久久久久久免费| 风间由美性色一区二区三区四区| 日韩精品视频在线观看视频| www..com久久爱| 亚洲 欧美 成人| 最新日韩中文字幕| 日本精品在线观看| 800av在线免费观看| 99久久婷婷国产| 99re这里只有精品在线| 丝袜情趣国产精品| 精品一区二区三区四区五区| www.男人天堂网| 99久久777色| 最新中文字幕在线观看视频| 色七七影院综合| 粉嫩精品导航导航| 日本老熟妇毛茸茸| ...xxx性欧美| 日韩在线观看视频网站| 国产成人久久精品| 午夜国产一区二区| 亚洲av无码专区在线播放中文| 欧美午夜激情在线| 亚乱亚乱亚洲乱妇| 国产精品久久久久久免费观看| 久久精品日产第一区二区| 午夜激情福利电影| 精品少妇一区二区三区视频免付费| 亚洲天堂手机| 中文字幕中文字幕99| av亚洲产国偷v产偷v自拍| 国产美女www爽爽爽| 久久国产精品久久精品| 美国成人xxx| 午夜视频在线网站| 欧美日韩激情小视频| 麻豆影视在线观看_| 久久av一区二区| 国产一区二区女| 日韩一区二区视频在线| 久久久成人精品视频| 全球av集中精品导航福利| 一级 黄 色 片一| 色综合久久久网| 国产丝袜视频在线播放| 相泽南亚洲一区二区在线播放 |