1. 程式人生 > 其它 >瀏覽器端模組化方式es module詳解

瀏覽器端模組化方式es module詳解

在es module出現之前還有社群推出amd和cmd的規範,這兩者還有其特定的編寫方式,使用起來不算很方便。es module被官方推出來就成為了瀏覽器端實現模組化的一個很好的方案。 想要在瀏覽器端使用 es module ,首先在 html 當中引入 js 檔案的時候,就需要將script標籤中的type設定為module
index.html  
<script src="b.js" type="module"></script>

這樣瀏覽器才能執行使用es module的js檔案,定義之後就可以在對應的js檔案中使用模組化的方式來編寫檔案,匯出和匯入的方式有幾種,但都是相同的關鍵字,export 與 import,一起來看看可以如何定義匯入匯出。

第一種,直接匯出定義的變數,這種方式匯出的內容互不關聯,適用於匯出自己定義的常量,redux中定義action就經常使用這種匯出
a.js
export const name = 'alice'
export const age = 16
 
b.js
import { name, age } from 
"./a.js"; console.log(name, age) // alice 16

第二種,先定義變數,再使用 export 一起匯出,匯入方式可以使用上面的方式,也可以通過一個 * 來將所有的匯出內容定義為一個物件,從物件中再去取值 ,redux中定義的reducer、action在 index.js 中匯出會經常使用這種方式

a.js
const name = 'alice'
const age = 16
export { name, age }
 
b.js
import * as obj from "./a.js";
console.log(obj.name, obj.age) 
// alice 16

第三種,給匯出的變數取別名,匯入的變數同樣可以取別名,當名字發生衝突時、匯出變數名太長時,都可以取個別名,取完別名之後,原先的名字就不可用了

a.js
const name = 'alice'
const age = 16
 
export { name as myName, age as myAge }
 
b.js
import { myName as aliceName, myAge } from "./a.js";
 
console.log(aliceName, myAge)  // alice 16

第四種,預設匯出,預設匯出在一個js檔案中只允許存在一個,預設匯出可以不用定義變數名,在匯入的時候可以隨意起名,並且匯入的時候不需要加 {} ,這樣的定義方式在編寫redux中的reducer函式時很常見

a.js
export default function(){
    return 'hello world'
}
 
b.js
import foo from './a.js'
 
console.log(foo()) // hello world

第五種,合併匯出,在b.js檔案匯入a.js檔案中匯出的內容,b.js檔案不對匯入的內容做任何操作,直接匯出,最後由index.js匯入b.js並進行處理

a.js
export const name = 'alice'
 
b.js
export { name } from './a.js'
 
index.js
import { name } from './b.js'
 
console.log(name) // alice

以上是es module的具體的語法表現,匯入匯出的方式有很多,可以根據具體需要的場景進行判斷和使用,另外,es module 還有一些特點。

1、非同步載入,當script標籤中定義 type="module"之後,相當於給js標籤加上了 async 的標識,代表非同步載入資源,不會阻塞其它內容的執行,按照如下程式碼,列印的hi有可能是在引入的index.js檔案之前,要根據 index.js 的執行速度來判斷。
<script src="index.js" type="module"></script>
<script type="text/javascript">
    console.log('hi')
</script>
 

2、編譯時解析,簡單來說javascript的執行過程需要將原始碼編譯成抽象語法樹,執行的時候再轉成機器可識別的語言,在編譯階段解析資料,並不知道該不該載入此js檔案,只有等到檔案執行時,才知道檔案裡具體邏輯的執行過程,所以不能夠在編譯時解析的模組化方式出現類似條件判斷,動態引入等程式碼

const flag = true
if(flag){
    import xxx from './a.js'
 }

如果真的需要根據一些條件才執行程式碼,可以通過 require 函式來動態的引入,require函式執行完是一個promise物件,可以通過then方法來獲取所需要的資料

const flag = true
 
if(flag){
  import('./b.js')
  .then(({name})=>{
    console.log(name)
  })
}
3、export 關鍵字後面跟的大括號並不是代表物件,在物件中也沒有通過 as 取別名這樣的方式,如果我們嘗試以下把它當成物件來匯出,一定是會報錯的
let name = 'alice'
export {
    name: name
}
export 匯出的 name 就對應著 name 這個變數,如果修改 name 的值,export 匯出的內容會發生變化,import 匯入的內容也會發生變化
a.js
let name = 'kiki', age = 18
 
setTimeout(()=>{
  name = '嘻嘻嘻'
}, 1000)
 
export {
  name,
    age
}
 
b.js
import { name, age } from './a.js'
 
console.log(name)
 
setTimeout(()=>{
  console.log(name)
},2000)
 
// 依次列印 kiki 嘻嘻嘻

export 匯出的內容有一個模組環境記錄,用來記錄匯出時更改的變數,當變數更改時,使用新的變數值替換舊變數值

但是不可以反向的修改,因為 import 匯入的內容是一個通過 const 定義的常量,常量是不可以被修改的,以下操作是不可行的
import { name } from './a.js'
name = '哈哈哈哈'
一般而言,我們都是在瀏覽器端使用 es module,如果想要在 node 端編寫es module程式碼,可以有兩種方式,一種是在 package.json 中配置 type:module,另一種是直接把js檔案的字尾名為改為 .mjs node端常用的模組化方式是 commonjs,同樣是模組化,那麼 es module 和 commonjs 之間是否能互相呼叫呢,看看如下程式碼
a.mjs
const name = 'alice'
const age = 18
 
export {
  name,
  age
}
 
b.js
const a = require('./a.mjs')
 
console.log(a)

以上程式碼執行會報錯Must use import to load ES Module,而如下的方式在高版本的nodejs中是可以的

a.js
const name = 'alice'
const age = 18
 
module.exports = {
  name,
  age
}
 
b.js
import b from './b.js'
 
console.log(b) // { name: 'alice', age: 18 }

以上就是瀏覽器端模組方式es module的概念與用法,模組化能夠更好的將程式碼分塊並複用,nodejs端也有常用實現模組化的方式,即commonjs,如果不熟悉可以看看這篇文章 ->nodejs端模組化方式comomjs詳解