聊聊什麼是CommonJs和Es Module及它們的區別

前言

初衷: 將我整理的筆記分享給你們,但願本篇文章能給你帶來不同的認知,不喜勿噴。javascript

適合人羣: 前端初級開發,大佬繞道。前端

內容結構: 爲何有模塊化 -> 基本語法 -> 二者區別。java

爲何會有CommonJs和Es Module呢

咱們都知道在早期JavaScript模塊這一律念,都是經過script標籤引入js文件代碼。固然這寫基本簡單需求沒有什麼問題,但當咱們的項目愈來愈龐大時,咱們引入的js文件就會越多,這時就會出現如下問題:markdown

  • js文件做用域都是頂層,這會形成變量污染
  • js文件多,變得很差維護
  • js文件依賴問題,稍微不注意順序引入錯,代碼全報錯

爲了解決以上問題JavaScript社區出現了CommonJsCommonJs是一種模塊化的規範,包括如今的NodeJs裏面也採用了部分CommonJs語法在裏面。那麼在後來Es6版本正式加入了Es Module模塊,這兩種都是解決上面問題,那麼都是解決什麼問題呢。數據結構

  • 解決變量污染問題,每一個文件都是獨立的做用域,因此不存在變量污染
  • 解決代碼維護問題,一個文件裏代碼很是清晰
  • 解決文件依賴問題,一個文件裏能夠清楚的看到依賴了那些其它文件

那麼咱們下面來一一瞭解它們的語法及弊端吧app

CommonJs 基本語法

導出

CommonJs中使用module.exports導出變量及函數,也能夠導出任意類型的值,看以下案例。ide

// 導出一個對象
module.exports = {
    name: "蛙人",
    age: 24,
    sex: "male"
}

// 導出任意值
module.exports.name = "蛙人"
module.exports.sex = null
module.exports.age = undefined
複製代碼

直接導出模塊化

導出也能夠省略module關鍵字,直接寫exports導出也能夠,看以下案例。函數

exports.name = "蛙人"
exports.sex = "male"
複製代碼

注意:若是使用exports導出單個值以後,就不能在導出一個對象值,這隻會修改exports的對象改變,然而修改無效,最終導出仍是name,和sex,由於最終的導出是由module.exports決定的。oop

exports.name = "蛙人"
exports.sex = "male"
exports = {
    name: "蛙人"
}
複製代碼

上面example中,這種狀況會改變對象的引用值則導出無效,因此最後導出的仍是namesex

混合導出

混合導出,exportsmodule.exports能夠同時使用,不會存在問題。

exports.name = "蛙人"
module.exports.age = 24
複製代碼

導入

CommonJs中使用require語法能夠導入,若是想要單個的值,能夠經過解構對象來獲取。

// index.js
module.exports.name = "蛙人"
module.exports.age = 24

let data = require("./index.js")
console.log(data) // { name: "蛙人", age: 24 }
複製代碼

重複導入

不論是CommonJs仍是Es Module都不會重複導入,就是隻要該文件內加載過一次這個文件了,我再次導入一次是不會生效的。

let data = require("./index.js")
let data = require("./index.js") // 不會在執行了
複製代碼

動態導入

CommonJs支持動態導入,什麼意思呢,就是能夠在語句中,使用require語法,來看以下案例。

let lists = ["./index.js", "./config.js"]
lists.forEach((url) => require(url)) // 動態導入

if (lists.length) {
    require(lists[0]) // 動態導入
}
複製代碼

導入值的變化

CommonJs導入的值是拷貝的,因此能夠修改拷貝值,但這會引發變量污染,一不當心就重名。

// index.js
let num = 0;
module.exports = {
    num,
    add() {
       ++ num 
    }
}

let { num, add } = require("./index.js")
console.log(num) // 0
add()
console.log(num) // 0
num = 10
複製代碼

上面example中,能夠看到exports導出的值是值的拷貝,更改完++ num值沒有發生變化,而且導入的num的值咱們也能夠進行修改

總結

CommonJs解決了變量污染,文件依賴等問題,上面咱們也介紹了它的基本語法,它能夠動態導入(代碼發生在運行時),不能夠重複導入。

Es Module 基本語法

導出

Es Module中導出分爲兩種,單個導出(export)、默認導出(export default),單個導出在導入時不像CommonJs同樣直接把值所有導入進來了,Es Module中能夠導入我想要的值。那麼默認導出就是所有直接導入進來,固然Es Module中也能夠導出任意類型的值。

// 導出變量
export const name = "蛙人"
export const age = 24

// 導出函數也能夠
export function fn() {}
export const test = () => {}


// 若是有多個的話
const name = "蛙人"
const sex = "male"
export { name, sex }
複製代碼

混合導出

可使用exportexport default同時使用而且互不影響,只須要在導入時地方注意,若是文件裏有混合導入,則必須先導入默認導出的,在導入單個導入的值。

export const name = "蛙人"
export const age = 24

export default {
    fn() {},
    msg: "hello 蛙人"
}
複製代碼

導入

Es Module使用的是import語法進行導入。若是要單個導入則必須使用花括號{}注意:這裏的花括號跟解構不同

// index,js
export const name = "蛙人"
export const age = 24

import { name, age } from './index.js'
console.log(name, age) // "蛙人" 24

// 若是裏面全是單個導出,咱們就想所有直接導入則能夠這樣寫
import * as all from './index.js'
console.log(all) // {name: "蛙人", age: 24}
複製代碼

混合導入

混合導入,則該文件內用到混合導入,import語句必須先是默認導出,後面再是單個導出,順序必定要正確不然報錯。

// index,js
export const name = "蛙人"
export const age = 24
export default {
    msg: "蛙人"
}

import msg, { name, age } from './index.js'
console.log(msg) // { msg: "蛙人" }
複製代碼

上面example中,若是導入的名稱不想跟本來地名稱同樣,則能夠起別名。

// index,js
export const name = "蛙人"
export const age = 24
export default {
    msg: "蛙人"
}

import { default as all,  name, age } from './index.js'
console.log(all) // { msg: "蛙人" }
複製代碼

導入值的變化

export導出的值是值的引用,而且內部有映射關係,這是export關鍵字的做用。並且導入的值,不能進行修改也就是隻讀狀態。

// index.js
export let num = 0;
export function add() {
    ++ num
}

import { num, add } from "./index.js"
console.log(num) // 0
add()
console.log(num) // 1
num = 10 // 拋出錯誤
複製代碼

Es Module是靜態

就是Es Module語句``import只能聲明在該文件的最頂部,不能動態加載語句,Es Module`語句運行在代碼編譯時。

if (true) {
	import xxx from 'XXX' // 報錯
}
複製代碼

總結

Es Module也是解決了變量污染問題,依賴順序問題,Es Module語法也是更加靈活,導出值也都是導出的引用,導出變量是可讀狀態,這增強了代碼可讀性。

CommonJs和Es Module的區別

CommonJs

  • CommonJs能夠動態加載語句,代碼發生在運行時
  • CommonJs混合導出,仍是一種語法,只不過不用聲明前面對象而已,當我導出引用對象時以前的導出就被覆蓋了
  • CommonJs導出值是拷貝,能夠修改導出的值,這在代碼出錯時,很差排查引發變量污染

Es Module

  • Es Module是靜態的,不能夠動態加載語句,只能聲明在該文件的最頂部,代碼發生在編譯時

  • Es Module混合導出,單個導出,默認導出,徹底互不影響

  • Es Module導出是引用值以前都存在映射關係,而且值都是可讀的,不能修改

感謝

謝謝各位在百忙之中點開這篇文章,但願對大家能有所幫助,若有問題歡迎各位大佬指正。

我是蛙人,若是以爲寫得能夠的話,請點個贊吧。

感興趣的小夥伴能夠加入 [ 前端娛樂圈交流羣 ] 歡迎你們一塊兒來交流討論

往期好文

《帶你輕鬆理解數據結構之Map》

《這些工做中用到的JavaScript小技巧你都知道嗎?》

《理解數據結構之Set,只要5分鐘!》

《【建議收藏】分享一些工做中經常使用的Git命令及特殊問題場景怎麼解決》

《解構:使數據訪問更便捷!》

《你真的瞭解ES6中的函數特性麼?》

《一看就懂的var、let、const三者區別》

相關文章
相關標籤/搜索