詳解es6的export和import命令

花懷櫻發表於2021-10-12

1、概述

一直以來開發vue專案,對export和import的用法都比較模糊,看別人怎麼寫我就照葫蘆畫瓢,不報錯或者功能實現就行,完全不懂其中的原理,今日閒下來了,就揭穿它們的真面目吧!

歷史上,JavaScript是沒有模組的概念的,就像它沒有類的概念一樣,就連css都有@import,所以社群制定了CommonJS和AMD規範實現模組載入。為此ES6新增了export和import命令實現了模組功能,而且它的實現方式簡單得不可思議,完全取代了CommonJS和AMD,現已成為瀏覽器和伺服器通用的模組解決方案。

ES6的模組設計思想是靜態化的,它在編譯時確定模組的依賴關係以及輸入和輸出的變數,也就是說它在編譯時就完成了模組載入,我們稱為‘編譯時載入’或者靜態載入,效率上自然比CommonJS要高得多。模組功能主要由兩個命令構成:export和import。

  • export命令規定模組的對外介面
  • import命令輸入其他模組提供的功能

2、export

一個模組是一個獨立的檔案,該檔案內部的所有變數,外部無法獲取,如果希望外部能夠讀取模組內部的某個變數,就必須使用export關鍵字輸出該變數。所以我們通常在vue專案中引入第三方js檔案(比如粒子動畫)時,往往需要改動此檔案,並將最終的變數或者函式或者類暴露出來才能在vue檔案中通過import引入。
export可以輸出變數、函式和類。

// export.js
// bad
export const name = '飄搖的淺櫻';
export const age = 17;
export const gender = '女';
export function foo() {
  console.log('函式foo');
}
export class Person {
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
  getInfo() {
    return `姓名:${this.name},性別:${this.gender},年齡:${this.age}`;
  }
}


// good
const name = '飄搖的淺櫻';
const age = 17;
const gender = '女';
function foo() {
  console.log('函式foo');
}
class Person {
  constructor(name, age, gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
  }
  getInfo() {
    return `姓名:${this.name},性別:${this.gender},年齡:${this.age}`;
  }
}
export { 
  name, 
  age, 
  gender, 
  foo, 
  Person 
};

上面兩種寫法是等價的,但是優先推薦使用下面的寫法,因為在檔案末尾你能一眼就看清楚輸出了哪些變數。
export輸出的變數可以使用as關鍵字重新命名。

// export.js
const arr = ['html', 'css', 'js']
export { arr as technologies }

// import.js
import { technologies } from './export.js';

export命令規定的是對外的介面,因此必須與模組內部的變數建立一一對應的關係。

// 錯誤寫法一
export 10;

// 錯誤寫法二
var n = 10;
export n;

// 正確寫法一
export const n = 10;

// 正確寫法二
const n = 10;
export {n};

// 正確寫法三
const n = 10;
export {n as m};

正確寫法中,import的時候通過變數n或m拿到值10。而在錯誤寫法中,輸出的只有值而沒有變數,缺少對應關係。

3、import

使用export命令定義了對外介面之後,就能夠使用import命令載入此模組。

// import.js
import { name, age, gender, foo, Person } from './export.js';
console.log(name, age, gender);
foo();
const p = new Person('飄搖的淺櫻', 17, '女')
const info = p.getInfo()
console.log(info)
// import.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="import.js" type="module"></script>
</body>
</html>

結果如下:
image.png

從例子中可以看出,import的{}內的變數必須與export輸出的變數名稱相同,否則無法載入。

import { name1 } from './export.js';

image.png

import命令輸入的變數都是隻讀的,因為它的本質是輸入介面,所以不允許在載入模組的指令碼里面改寫介面。

import { obj } from 'export.js';
obj = {};  // Syntax Error : 'obj' is read-only;

4、export default

在第三點中,我們已經知道了,import的變數必須與export輸出的變數名稱相同,這個規則導致我們必須知道輸出的變數名稱是什麼才能順利import,這就增加了我們閱讀文件的時間,不利於快速上手。export default的出現就是為了讓使用者不用知道模組內定義的變數就能載入模組,它為模組指定了預設輸出。

// export.js
const tellYourName = (name) => name
export default tellYourName;

// import.js
import tellName from './export.js'
const name1 = tellName('飄搖的淺櫻')
console.log(name1)  // 飄搖的淺櫻

image.png

可以看到import後面的tellName沒有加花括號{},並且名稱和export.js中輸出的變數名稱不同,這樣我們在引用第三方模組時就無需知曉內部export的變數名稱是什麼。
export default命令在一個模組中只能使用一次。

5、import()

import命令做不到node的require的動態載入功能,因此ES2020提案引入import()函式,支援動態載入模組。

import(spec)

引數spec代表載入的模組的位置,import命令能接受什麼引數import()函式就能接受什麼引數,區別只是後者是動態載入。
我所接觸到的import()的應用最常見的就是路由懶載入了。因為當打包構建應用時,JavaScript包會變得非常大,影響頁面載入。這時候我們將不同路由對應的元件分割成不同的程式碼塊,然後當路由被訪問的時候才載入對應元件。寫法如下:

// index.js
{
  path: 'home',
  component: () => import('@/views/page/home'),
  name: 'home',
  meta: {
    title: '首頁'
  },
}

上面例子中,是再熟悉不過的路由檔案了,它代表的就是當訪問home這個路由時,才載入home元件,其他路由均使用此方法,就能大大減小js包體積,加快頁面載入速度。

import()函式可以用在任何地方;
import()函式與所載入的模組沒有任何靜態連線關係;
import()函式類似於Node的require方法,但它是非同步載入。

6、關鍵知識點總結

(1)ES6新增export和import兩個命令實現模組載入;
(2)ES6模組自動採用嚴格模式,而不管你有沒有在模組頭部加上'use strict;';
(3)export命令可以輸出變數、函式和類;
(4)export輸出的變數就是本來的名字,但是可以使用as關鍵字重新命名,引用的時候就是重新命名的那個名稱;
(5)export命令規定的是對外的介面,因此必須與模組內部的變數、函式或類建議一一對應關係;
(6)export命令輸出的介面與其對應的值是動態繫結關係,意思是通過此介面可以獲取模組內部實時的值;
(7)import和export命令只能在模組的頂層,不能在程式碼塊之中(比如在函式或者if語句之中);
(8)import()函式在路由懶載入、條件載入中最能體現價值;
(9)比較一下import各個寫法的含義:
import '@/style/index.scss';
執行所載入的模組,但是不輸入任何值

import HelloWorld from "@/components/HelloWorld.vue";
引用元件(export default)

import Vue from 'vue';
引用Vue模組(export default)

import { uploadOss as alias } from '@/utils/upload';
引用uploadOss並重新命名為alias(export)

import { queryList, addUser } from '@/api/home.js';
引用home.js中的變數或函式或類(export)

import * as alias from 'vue-router';
載入整個模組(export),alias.xxx

import _, { each, forEach } from 'lodash';
引入預設方法和其他介面

參考地址:https://es6.ruanyifeng.com/#d...

相關文章