第二節:ESModule簡介、按需導出導入、默認導出導入、動態加載、內部原理等


一. 前言

1. 背景

  因為AMD,CMD局限使用與瀏覽器端,而CommonJS在服務器端使用。 ESModule才是瀏覽器端和服務器端通用的規范

2. 關鍵字

 (1). 使用export、 export default進行導出

 (2). 使用import關鍵字進行導入

3. import的匹配規則

 這是在Vue項目中的匹配規則哦,如果是node環境下,必須寫全路徑哦!!!

(1). 如果是完整路徑,則直接引入 。eg:import moudleA from "./find.js";

(2). 如果不是完整路徑,比如:import mA from  './find'

 A. 先找同名的js文件,即找 find.js

 B. 如果找不到,再找find文件夾,找到后,再匹配find文件夾中的index.js文件。

 C. 如果找不到index.js文件,會去當前文件夾中的package.json文件中查找main選項中的入口文件。

 D. 全都沒有的話,則報錯。

4. 使用環境

 (1). 在vue項目中,可以直接使用

 (2). 在瀏覽器中,需要

    如:<script src="./modules/foo.js"  type="module"></script>

    注意:運行的時候, (比如一個 file:// 路徑的文件 的運行模式), 你將會遇到 CORS 錯誤,因為Javascript 模塊安全性需要。可以使用VSCode中有一個插件:Live Server

 (3). 在node環境中,需要npm init一下,然后在package.json中,加上一句話: "type": "module", 詳見package.json    

{
  "type": "module",
  "name": "05_esmodule",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

 

二. 按需導出/導入

1. export按需導出

  export關鍵字將一個模塊中的變量、函數、類等導出,有以下三種寫法:

(1)  在語句聲明的前面直接加上export關鍵字

(2)  聲明和導出分開,將所有需要導出的標識符,放到export后面的 {}中

 注意:這里的 {}里面不是ES6的對象字面量的增強寫法,{}也不是表示一個對象的; 所以: export {name: name},是錯誤的寫法!!!

(3) 導出時給標識符起一個別名

總結:上述三種寫法,可以在一個js模塊中共存的!!!

代碼分享--代碼中只是為了演示三種寫法

/* 
   export按需導出
*/

// 方式1: export + 聲明語句 【推薦】

export const myName = "ypf";
export const myAge = 18;
export function foo() {
	console.log("foo start");
}
export class Person {
	GetMsg() {
		console.log("Person GetMsg start");
	}
}


// 方式2: 聲明和export導出分開 【推薦】

const myName = "ypf";
const myAge = 18;
function foo() {
	console.log("foo start");
}
class Person {
	GetMsg() {
		console.log("Person GetMsg start");
	}
}
export { myName, myAge, foo, Person };

// 方式3: 導出的時候起別名
const myName1 = "ypf";
const myAge1 = 18;
function foo1() {
	console.log("foo start");
}
class Person1 {
	GetMsg() {
		console.log("Person GetMsg start");
	}
}
export { myName1 as myName, myAge1 as myAge, foo1 as foo, Person1 as Person };

2. import按需導入

 import關鍵字負責從另外一個模塊中導入內容

 (1). import {標識符列表} from '模塊';

  注意:這里的{}也不是一個對象,里面只是存放導入的標識符列表內容;

 (2).導入時給標識符起別名

 (3).通過 * 將模塊功能放到一個模塊功能對象(a module object)上

總結:只有使用上面export按需導出的方式,才能使用下面import按需導入的方式進行接收

代碼分享:

// 導入方式1:直接原名輸出
console.log("-----------導入方式1:直接原名輸出---------------");
import { myName, myAge, foo, Person } from "./111.js";
console.log(myName, myAge);
foo();
new Person().GetMsg();

// 導入方式2:導入的時候起別名
console.log("-----------導入方式2:導入的時候起別名---------------");
import {
	myName as myName2,
	myAge as myAge2,
	foo as foo2,
	Person as Person2,
} from "./111.js";
console.log(myName2, myAge2);
foo2();
new Person2().GetMsg();

// 導入方式3:將導出的所有內容放到一個標識符中
console.log(
	"-----------導入方式3:將導出的所有內容放到一個標識符中---------------"
);

import * as myData from "./111.js";
console.log(myData.myName, myData, myAge);
myData.foo();
new myData.Person().GetMsg();

3. 封裝結合使用【重】

  比如在實際開發,有很多工具類,每個工具類js文件中,都有很多方法,但在一個vue頁面中,需要使用很多工具類文件中的某一個 或 某幾個方法,如果每個文件都導入,顯得很繁瑣。

  這里我們通常采用一種統一出口的思想解決這個問題:

  如下:

    utils文件夾中有很多工具類文件: 001.js  002.js  003.js, 我們新建一個 index.js文件,在該文件中引入001 002 003 三個js文件,然后對外export所有方法,那么在vue頁面使用的 時候,我們只需引入index.js文件即可。

  index.js統一出口文件,有以下3種寫法:

  寫法1:先import進來需要的,然后再export

  寫法2:直接export導出需要的【推薦】

  寫法3:  直接全部導出 【推薦】

總結:寫法1和2,都可以按照需要對外暴露,寫法3直接全部暴露

utils/001.js

export const myName = "ypf";
export const myAge = 18;

utils/002.js

function foo1() {
	console.log("foo1 start");
}
function foo2() {
	console.log("foo2 start");
}
export { foo1, foo2 };

utils/003.js

export class Person {
	GetMsg() {
		console.log("Person GetMsg");
	}
}

utils/index.js 【重點】

// 寫法1:先import進來需要的,然后再export
/*
import { myName, myAge } from "./001.js";
import { foo1, foo2 } from "./002.js";
import { Person } from "./003.js";
export { myName, myAge, foo1, foo2, Person };
 */

// 寫法2:直接export導出需要的【推薦】
/* 
export { myName, myAge } from "./001.js";
export { foo1, foo2 } from "./002.js";
export { Person } from "./003.js";
 */

// 寫法3:直接全部導出 【推薦】
export * from "./001.js";
export * from "./002.js";
export * from "./003.js";

 最外層引用--引入的是index.js文件


import { myName, myAge, foo1, foo2, Person } from "./utils/index.js";
console.log(myName, myAge);
foo1();
foo2();
new Person().GetMsg();

 

三. 默認導出/導入

1. default默認導出

  默認導出export時不需要指定名字,有以下幾種寫法:

  (1). 最常見的寫法1:先聲明,然后 exprot default {} 導出

  (2). 寫法2:  直接在某個方法、變量、類前,加 export default

  (3). 寫法3: 使用 export {}, 在里面的某個內容上 + as default, 注意,也是只能加1個 【了解即可】

特別注意:在一個js模塊中,只能有一個默認導出,也就是說 export default只能出現一次!!!!

寫法1

/* 
    export default默認導出-寫法1
*/

// 最常見的寫法為:先聲明,然后 exprot default {} 導出
const myName = "ypf";
const myAge = 18;
function foo() {
	console.log("foo start");
}
class Person {
	GetMsg() {
		console.log("Person GetMsg start");
	}
}
export default {
	myName,
	myAge,
	foo,
	Person,
};

寫法2

/* 
    export default默認導出-寫法2
*/

// 寫法2: 直接在某個方法、變量、類前,加 export default
export default function foo2() {
	console.log("foo2 start");
}

// 總結:因為 一個模塊中export default 只能出現一次,所以這種寫法也就只能導出去一樣東西哦!!!

寫法3


// 寫法3: 使用 export {}, 在里面的某個內容上 + as default, 注意,也是只能加1個 【了解即可】
const myName2 = "ypf";
const myAge2 = 18;
function foo2() {
	console.log("foo start");
}

export { myName2 as default, myAge2, foo2 };

2. 默認導入

  默認導入時不能使用 {} 接收,必須使用一個變量來接收,這個變量可以自己命名,根據默認導出形式的不同,這個變量可能是對象、函數、類、普通變量等

  (1). 針對默認導出寫法1的---默認導入, 此時接收的這個變量是一個對象

  (2). 針對默認導出寫法2的---默認導入, 此時接收的這個變量是一個函數

  (3). 針對默認導出寫法3的---默認導入, 此時接收的這個變量是一個普通string類型的變量


//1. 針對默認導出寫法1的---默認導入
console.log("--1. 針對默認導出寫法1的---默認導入--");
import myModel from "./111.js";
console.log(myModel.myName, myModel.myAge);
myModel.foo();
new myModel.Person().GetMsg();

// 特別注意:這里不支持{}解構寫法哦,必須寫一個對象接受
// import { myName } from "./111.js";   //報錯!!

//2. 針對默認導出寫法2的---默認導入
console.log("--2. 針對默認導出寫法2的---默認導入--");
import myFun from "./222.js"; //這里導入的myFun就是函數foo2
myFun();

//3. 針對默認導出寫法3的---默認導入
console.log("--3. 針對默認導出寫法3的---默認導入--");
import myData from "./333.js"; //這里導入的myData就是變量myName2
console.log(myData);

 

四. 其它用法

1. 動態加載

    我們可以利用 import() 函數實現在js業務代碼中動態加載模塊

導出代碼

const myName = "ypf";
const myAge = 18;
function foo() {
	console.log("foo start");
}
class Person {
	GetMsg() {
		console.log("Person GetMsg start");
	}
}
export { myName, myAge, foo, Person };

導入代碼

//1.  import函數返回的結果是一個Promise
import("./111.js").then(res => {
	console.log(res);
	console.log(res.myName, res.myAge);
	console.log(res.foo());
});

2. import meta

  import.meta是一個給JavaScript模塊暴露特定上下文的元數據屬性的對象。

  它包含了這個模塊的信息,比如說這個模塊的URL;它是ES11(ES2020)中新增的特性;

//2. ES11新增的特性
// meta屬性本身也是一個對象: { url: "當前模塊所在的路徑" }
console.log(import.meta);

 

五. ESModule內部原理

Module的解析過程可以划分為三個階段:

 階段一:構建(Construction),根據地址查找js文件,並且下載,將其解析成模塊記錄(Module Record);

 階段二:實例化(Instantiation),對模塊記錄進行實例化,並且分配內存空間,解析模塊的導入和導出語句,把模塊指向對應的內存地址。

 階段三:運行(Evaluation),運行代碼,計算值,並且將值填充到內存地址中;

 

六. ESModule和CommonJs混合使用

 二者可以相互引用,但需要安裝一下webpack環境,這里不再演示

ESModule導出

const name = "bar"
const age = 100

// es module導出
export {
  name,
  age
}

CommonJs導出

const name = "foo"
const age = 18

// commonjs導出
module.exports = {
  name,
  age
}

導入

// es module導入
import { name, age } from "./foo";
console.log(name, age);

// commonjs導入
// const bar = require("./bar.js");
// console.log(bar.name, bar.age);

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM