成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

深入 CommonJs 與 ES6 Module

sanyang / 3122人閱讀

摘要:目前主流的模塊規(guī)范模塊通用模塊如果你在文件頭部看到這樣的代碼,那么這個(gè)文件使用的就是規(guī)范實(shí)際上就是全局變量這三種風(fēng)格的結(jié)合這段代碼就是對(duì)當(dāng)前運(yùn)行環(huán)境的判斷,如果是環(huán)境就是使用規(guī)范,如果不是就判斷是否為環(huán)境,最后導(dǎo)出全局變量有了后我們的代碼和

目前主流的模塊規(guī)范

UMD

CommonJs

es6 module

umd 模塊(通用模塊)
(function (global, factory) {
    typeof exports === "object" && typeof module !== "undefined" ? module.exports = factory() :
    typeof define === "function" && define.amd ? define(factory) :
    (global.libName = factory());
}(this, (function () { "use strict";})));

如果你在js文件頭部看到這樣的代碼,那么這個(gè)文件使用的就是 UMD 規(guī)范
實(shí)際上就是 amd + commonjs + 全局變量 這三種風(fēng)格的結(jié)合
這段代碼就是對(duì)當(dāng)前運(yùn)行環(huán)境的判斷,如果是 Node 環(huán)境 就是使用 CommonJs 規(guī)范, 如果不是就判斷是否為 AMD 環(huán)境, 最后導(dǎo)出全局變量
有了 UMD 后我們的代碼和同時(shí)運(yùn)行在 Node瀏覽器上
所以現(xiàn)在前端大多數(shù)的庫(kù)最后打包都使用的是 UMD 規(guī)范

CommonJs

Nodejs 環(huán)境所使用的模塊系統(tǒng)就是基于CommonJs規(guī)范實(shí)現(xiàn)的,我們現(xiàn)在所說(shuō)的CommonJs規(guī)范也大多是指Node的模塊系統(tǒng)

模塊導(dǎo)出

關(guān)鍵字:module.exports exports

// foo.js

//一個(gè)一個(gè) 導(dǎo)出
module.exports.age = 1
module.exports.foo = function(){}
exports.a = "hello"

//整體導(dǎo)出
module.exports = { age: 1, a: "hello", foo:function(){} }

//整體導(dǎo)出不能用`exports` 用exports不能在導(dǎo)入的時(shí)候使用
exports = { age: 1, a: "hello", foo:function(){} }

這里需要注意 exports 不能被賦值,可以理解為在模塊開(kāi)始前exports = module.exports, 因?yàn)橘x值之后exports失去了 對(duì)module.exports的引用,成為了一個(gè)模塊內(nèi)的局部變量

模塊導(dǎo)入

關(guān)鍵字:require

const foo = require("./foo.js")
console.log(foo.age) //1
模塊導(dǎo)入規(guī)則:

假設(shè)以下目錄為 src/app/index.js 的文件 調(diào)用 require()

./moduleA 相對(duì)路徑開(kāi)頭

在沒(méi)有指定后綴名的情況下
先去尋找同級(jí)目錄同級(jí)目錄:src/app/

src/app/moduleA 無(wú)后綴名文件 按照javascript解析

src/app/moduleA.js js文件 按照javascript解析

src/app/moduleA.json json文件 按照json解析

src/app/moduleA.node node文件 按照加載的編譯插件模塊dlopen

同級(jí)目錄沒(méi)有 moduleA 文件會(huì)去找同級(jí)的 moduleA目錄src/app/moduleA

src/app/moduleA/package.json 判斷該目錄是否有package.json文件, 如果有 找到main字段定義的文件返回, 如果 main 字段指向文件不存在 或 main字段不存在 或 package.json文件不存在向下執(zhí)行

src/app/moduleA/index.js

src/app/moduleA/index.json

src/app/moduleA/index.node

結(jié)束

/module/moduleA 絕對(duì)路徑開(kāi)頭

直接在/module/moduleA目錄中尋找 規(guī)則同上

react 沒(méi)有路徑開(kāi)頭

沒(méi)有路徑開(kāi)頭則視為導(dǎo)入一個(gè)包
會(huì)先判斷moduleA是否是一個(gè)核心模塊path,http,優(yōu)先導(dǎo)入核心模塊
不是核心模塊 會(huì)從當(dāng)前文件的同級(jí)目錄的node_modules尋找

/src/app/node_modules/ 尋找規(guī)則同上 以導(dǎo)入react為例 先 node_modules 下 react 文件 -> react.js -> react.json -> react.node ->react目錄 -> react package.json main -> index.js -> index.json -> index.node 如果沒(méi)找到 繼續(xù)向父目錄的node_modules中找

/src/node_modules/

/node_modules/

直到最后找不到 結(jié)束

require wrapper

Node的模塊 實(shí)際上可以理解為代碼被包裹在一個(gè)函數(shù)包裝器內(nèi)
一個(gè)簡(jiǎn)單的require demo

function wrapper (script) {
    return "(function (exports, require, module, __filename, __dirname) {" + 
        script +
     "
})"
}

function require(id) {
 var cachedModule = Module._cache[id];
  if(cachedModule){
    return cachedModule.exports;
  }
  
  const module = { exports: {} }

  // 這里先將引用加入緩存 后面循環(huán)引用會(huì)說(shuō)到
  Module._cache[id] = module

  //當(dāng)然不是eval這么簡(jiǎn)單
  eval(wrapper("module.exports = "123""))(module.exports, require, module, "filename", "dirname")


  return module.exports
}

也可以查看:node module 源碼
從以上代碼我們可以知道:

模塊只執(zhí)行一次 之后調(diào)用獲取的 module.exports 都是緩存哪怕這個(gè) js 還沒(méi)執(zhí)行完畢(因?yàn)橄燃尤刖彺婧髨?zhí)行模塊)

模塊導(dǎo)出就是return這個(gè)變量的其實(shí)跟a = b賦值一樣, 基本類型導(dǎo)出的是, 引用類型導(dǎo)出的是引用地址

exportsmodule.exports 持有相同引用,因?yàn)樽詈髮?dǎo)出的是 module.exports, 所以對(duì)exports進(jìn)行賦值會(huì)導(dǎo)致exports操作的不再是module.exports的引用

循環(huán)引用
// a.js
module.exports.a = 1
var b = require("./b")
console.log(b)
module.exports.a = 2

// b.js
module.exports.b = 11
var a = require("./a")
console.log(a)
module.exports.b = 22

//main.js
var a = require("./a")
console.log(a)

運(yùn)行此段代碼結(jié)合上面的require demo,分析每一步過(guò)程:

執(zhí)行 node main.js -> 第一行 require(a.js),(node 執(zhí)行也可以理解為調(diào)用了require方法,我們省略require(main.js)內(nèi)容)

進(jìn)入 require(a)方法: 判斷緩存(無(wú)) -> 初始化一個(gè) module -> 將 module 加入緩存 -> 執(zhí)行模塊 a.js 內(nèi)容,(需要注意 是加入緩存, 后執(zhí)行模塊內(nèi)容)

a.js: 第一行導(dǎo)出 a = 1 -> 第二行 require(b.js)(a 只執(zhí)行了第一行)

進(jìn)入 require(b) 內(nèi) 同 1 -> 執(zhí)行模塊 b.js 內(nèi)容

b.js: 第一行 b = 11 -> 第二行 require(a.js)

require(a) 此時(shí) a.js 是第二次調(diào)用 require -> 判斷緩存(有)-> cachedModule.exports -> 回到 b.js(因?yàn)?b>js對(duì)象引用問(wèn)題 此時(shí)的 cachedModule.exports = { a: 1 }

b.js:第三行 輸出 { a: 1 } -> 第四行 修改 b = 22 -> 執(zhí)行完畢回到 a.js

a.js:第二行 require 完畢 獲取到 b -> 第三行 輸出 { b: 22 } -> 第四行 導(dǎo)出 a = 2 -> 執(zhí)行完畢回到 main.js

main.js:獲取 a -> 第二行 輸出 { a: 2 } -> 執(zhí)行完畢

以上就是nodemodule模塊解析和運(yùn)行的大致規(guī)則

es6 module

ES6 之前 javascript 一直沒(méi)有屬于自己的模塊規(guī)范,所以社區(qū)制定了 CommonJs規(guī)范, NodeCommonjs 規(guī)范中借鑒了思想于是有了 Nodemodule,而 AMD 異步模塊 也同樣脫胎于 Commonjs 規(guī)范,之后有了運(yùn)行在瀏覽器上的 require.js

es6 module 基本語(yǔ)法:

export
export * from "module"; //重定向?qū)С?不包括 module內(nèi)的default
export { name1, name2, ..., nameN } from "module"; // 重定向命名導(dǎo)出
export { import1 as name1, import2 as name2, ..., nameN } from "module"; // 重定向重命名導(dǎo)出

export { name1, name2, …, nameN }; // 與之前聲明的變量名綁定 命名導(dǎo)出
export { variable1 as name1, variable2 as name2, …, nameN }; // 重命名導(dǎo)出

export let name1 = "name1"; // 聲明命名導(dǎo)出 或者 var, const,function, function*, class

export default expression; // 默認(rèn)導(dǎo)出
export default function () { ... } // 或者 function*, class
export default function name1() { ... } // 或者 function*, class
export { name1 as default, ... }; // 重命名為默認(rèn)導(dǎo)出

export 規(guī)則

export * from "" 或者 export {} from "",重定向?qū)С?,重定向的命名并不能在本模塊使用,只是搭建一個(gè)橋梁,例如:這個(gè)a并不能在本模塊內(nèi)使用

export {}, 與變量名綁定,命名導(dǎo)出

export Declaration,聲明的同時(shí),命名導(dǎo)出, Declaration就是: var, let, const, function, function*, class 這一類的聲明語(yǔ)句

export default AssignmentExpression,默認(rèn)導(dǎo)出, AssignmentExpression的 范圍很廣,可以大致理解 為除了聲明Declaration(其實(shí)兩者是有交叉的),a=2,i++,i/4,a===b,obj[name],name in obj,func(),new P(),[1,2,3],function(){}等等很多

import
// 命名導(dǎo)出 module.js
let a = 1,b = 2
export { a, b }
export let c = 3

// 命名導(dǎo)入 main.js
import { a, b, c } from "module"; // a: 1  b: 2  c: 3
import { a as newA, b, c as newC } from "module"; // newA: 1  b: 2  newC: 3


// 默認(rèn)導(dǎo)出 module.js
export default 1

// 默認(rèn)導(dǎo)入 main.js
import defaultExport from "module"; // defaultExport: 1


// 混合導(dǎo)出 module.js
let a = 1
export { a }
const b = 2
export { b }
export let c = 3
export default [1, 2, 3]

// 混合導(dǎo)入 main.js
import defaultExport, { a, b, c as newC} from "module"; //defaultExport: [1, 2, 3]  a: 1  b: 2  newC: 3
import defaultExport, * as name from "module"; //defaultExport: [1, 2, 3]  name: { a: 1, b: 2, c: 3 }
import * as name from "module"; // name: { a: 1, b: 2, c: 3, default: [1, 2, 3] }


// module.js
Array.prototype.remove = function(){}

//副作用 只運(yùn)行一個(gè)模塊
import "module"; // 執(zhí)行module 不導(dǎo)出值  多次調(diào)用module.js只運(yùn)行一次

//動(dòng)態(tài)導(dǎo)入(異步導(dǎo)入)
var promise = import("module");
import 規(guī)則

import { } from "module", 導(dǎo)入module.js命名導(dǎo)出

import defaultExport from "module", 導(dǎo)入module.js默認(rèn)導(dǎo)出

import * as name from "module", 將module.js的所有導(dǎo)出合并為name的對(duì)象,key為導(dǎo)出的命名,默認(rèn)導(dǎo)出的keydefault

import "module",副作用,只是運(yùn)行module,不為了導(dǎo)出內(nèi)容例如 polyfill,多次調(diào)用次語(yǔ)句只能執(zhí)行一次

import("module"),動(dòng)態(tài)導(dǎo)入返回一個(gè) Promise,TC39stage-3階段被提出 tc39 import

ES6 module 特點(diǎn) ES6 module的語(yǔ)法是靜態(tài)的

import 會(huì)自動(dòng)提升到代碼的頂層

exportimport 只能出現(xiàn)在代碼的頂層,下面這段語(yǔ)法是錯(cuò)誤

 //if for while 等都無(wú)法使用
{
  export let a = 1

  import defaultExport from "module"
}

true || export let a = 1

import 的導(dǎo)入名不能為字符串或在判斷語(yǔ)句,下面代碼是錯(cuò)誤

import "defaultExport" from "module"

let name = "Export"
import "default" + name from "module"

靜態(tài)的語(yǔ)法意味著可以在編譯時(shí)確定導(dǎo)入和導(dǎo)出,更加快速的查找依賴,可以使用lint工具對(duì)模塊依賴進(jìn)行檢查,可以對(duì)導(dǎo)入導(dǎo)出加上類型信息進(jìn)行靜態(tài)的類型檢查

ES6 module的導(dǎo)出是綁定的

使用 import 被導(dǎo)入的模塊運(yùn)行在嚴(yán)格模式

使用 import 被導(dǎo)入的變量是只讀的,可以理解默認(rèn)為 const 裝飾,無(wú)法被賦值

使用 import 被導(dǎo)入的變量是與原變量綁定/引用的,可以理解為 import 導(dǎo)入的變量無(wú)論是否為基本類型都是引用傳遞

// js中 基礎(chǔ)類型是值傳遞
let a = 1
let b = a
b = 2
console.log(a,b) //1 2

// js中 引用類型是引用傳遞
let obj = {name:"obj"}
let obj2 = obj
obj2.name = "obj2"
console.log(obj.name, obj2.name) // obj2  obj2


// es6 module 中基本類型也按引用傳遞
// foo.js
export let a = 1
export function count(){
  a++
}

// main.js
import { a, count } from "./foo"
console.log(a) //1
count()
console.log(a) //2

// export default 是無(wú)法 a 的動(dòng)態(tài)綁定 這一點(diǎn)跟 CommonJs 有點(diǎn)相似 都是值的拷貝
let a = 1;
export default a 

// 可以用另一種方式實(shí)現(xiàn) default 的動(dòng)態(tài)綁定
let a = 1;
export { a as default }
export function count(){
  a++
}
// 就跟上面 main.js 一樣

上面這段代碼就是 CommonJs 導(dǎo)出變量 和 ES6 導(dǎo)出變量的區(qū)別

es module 循環(huán)引用
// bar.js
import { foo } from "./foo"
console.log(foo);
export let bar = "bar"

// foo.js
import { bar } from "./bar"
console.log(bar);
export let foo = "foo"

// main.js
import { bar } from "./bar"
console.log(bar)

執(zhí)行 main.js -> 導(dǎo)入 bar.js

bar.js -> 導(dǎo)入 foo.js

foo.js -> 導(dǎo)入 bar.js -> bar.js 已經(jīng)執(zhí)行過(guò)直接返回 -> 輸出 bar -> bar is not defined, bar 未定義報(bào)錯(cuò)

我們可以使用function的方式解決:

// bar.js
import { foo } from "./foo"
console.log(foo());
export function bar(){
  return "bar"
}

// foo.js
import { bar } from "./bar"
console.log(bar());
export function foo(){
  return "foo"
}

// main.js
import { bar } from "./bar"
console.log(bar)

因?yàn)楹瘮?shù)聲明會(huì)提示到文件頂部,所以就可以直接在 foo.js 調(diào)用還沒(méi)執(zhí)行完畢的bar.jsbar 方法,不要在函數(shù)內(nèi)使用外部變量,因?yàn)樽兞窟€未聲明(let,const)和賦值,var

CommonJs 和 ES6 Module 的區(qū)別

其實(shí)上面我們已經(jīng)說(shuō)到了一些區(qū)別

CommonJs導(dǎo)出的是變量的一份拷貝,ES6 Module導(dǎo)出的是變量的綁定(export default 是特殊的)

CommonJs是單個(gè)值導(dǎo)出,ES6 Module可以導(dǎo)出多個(gè)

CommonJs是動(dòng)態(tài)語(yǔ)法可以寫(xiě)在判斷里,ES6 Module靜態(tài)語(yǔ)法只能寫(xiě)在頂層

CommonJsthis 是當(dāng)前模塊,ES6 Modulethisundefined

易混淆點(diǎn) 模塊語(yǔ)法與解構(gòu)

module語(yǔ)法解構(gòu)語(yǔ)法很容易混淆,例如:

import { a } from "module"

const { a } = require("module")

盡管看上去很像,但是不是同一個(gè)東西,這是兩種完全不一樣的語(yǔ)法與作用,ps:兩個(gè)人撞衫了,穿一樣的衣服你不能說(shuō)這倆人就是同一個(gè)人
module 的語(yǔ)法: 上面有寫(xiě) import/export { a } / { a, b } / { a as c} FromClause
解構(gòu) 的語(yǔ)法:

let { a } = { a: 1 }
let { a = 2 } = { }
let { a: b } = { a: 1 }
let { a: b = 2, ...res } = { name:"a" }
let { a: b, obj: { name } } = { a: 1, obj: { name: "1" } }

function foo({a: []}) {}

他們是差別非常大的兩個(gè)東西,一個(gè)是模塊導(dǎo)入導(dǎo)出,一個(gè)是獲取對(duì)象的語(yǔ)法糖

導(dǎo)出語(yǔ)法與對(duì)象屬性簡(jiǎn)寫(xiě)

同樣下面這段代碼也容易混淆

let a = 1

export { a } // 導(dǎo)出語(yǔ)法
export default { a } // 屬性簡(jiǎn)寫(xiě) 導(dǎo)出 { a: 1 } 對(duì)象

module.exports = { a } // 屬性簡(jiǎn)寫(xiě) 導(dǎo)出 { a: 1 } 對(duì)象

export defaultmodule.exports 是相似的

ES6 module 支持 CommonJs 情況

先簡(jiǎn)單說(shuō)一下各個(gè)環(huán)境的 ES6 module 支持 CommonJs 情況,后面多帶帶說(shuō)如何在不同環(huán)境中使用

因?yàn)?module.exports 很像 export default 所以 ES6模塊 可以很方便兼容 CommonJs
ES6 module中使用CommonJs規(guī)范,根據(jù)各個(gè)環(huán)境,打包工具不同也是不一樣的

我們現(xiàn)在大多使用的是 webpack 進(jìn)行項(xiàng)目構(gòu)建打包,因?yàn)楝F(xiàn)在前端開(kāi)發(fā)環(huán)境都是在 Node 環(huán)境原因,而 npm 的包都是 CommonJs 規(guī)范的,所以 webpack 對(duì)ES6模塊進(jìn)行擴(kuò)展 支持 CommonJs,并支持node的導(dǎo)入npm包的規(guī)范

如果你使用 rollup,想在ES Module中支持Commonjs規(guī)范就需要下載rollup-plugin-commonjs插件,想要導(dǎo)入node_modules下的包也需要rollup-plugin-node-resolve插件

如果你使用 node,可以在 .mjs 文件使用 ES6,也支持 CommonJs 查看 nodejs es-modules.md

在瀏覽器環(huán)境 不支持CommonJs

node 與 打包工具webpack,rollup的導(dǎo)入 CommonJs 差異

// module.js
module.export.a = 1

// index.js webpack rollup
import * as a from "./module"
console.log(a) // { a: 1, default: { a:1 } }

// index.mjs node
import * as a from "./module"
console.log(a) // { default: { a:1 } }

node 只是把 module.exports 整體當(dāng)做 export default
打包工具除了把 module.export 整體當(dāng)做 export default,還把 module.export 的每一項(xiàng) 又當(dāng)做 export 輸出,這樣做是為了更加簡(jiǎn)潔
import defaultExport from "./foo", defaultExport.foo()
import { foo } from "./foo"foo()

使用 ES6 Module

可以在 es6module example 倉(cāng)庫(kù)中獲取代碼在本地進(jìn)行測(cè)試驗(yàn)證

瀏覽器中使用

你需要起一個(gè)Web服務(wù)器來(lái)訪問(wèn),雙擊本地運(yùn)行 index.html 并不會(huì)執(zhí)行 type=module 標(biāo)簽
我們可以對(duì) script 標(biāo)簽的 type 屬性加上 module
先定義兩個(gè)模塊

// index.js
import module from "./module.js"
console.log(module) // 123

// module.js
export default 123

html中內(nèi)聯(lián)調(diào)用


html中通過(guò) scriptsrc 引用



// 控制臺(tái) 123
瀏覽器導(dǎo)入路徑規(guī)則

https://example.com/apples.mjs

http://example.com/apples.js

//example.com/bananas

./strawberries.mjs.cgi

../lychees

/limes.jsx

data:text/javascript,export default "grapes";

blob:https://whatwg.org/d0360e2f-caee-469f-9a2f-87d5b0456f6f

補(bǔ)充:

不加 后綴名 找不到具體的文件

后端可以修改接口/getjs?name=module這一類的,不過(guò)后端要返回 Content-Type: application/javascript 確保返回的是js,因?yàn)闉g覽器是根據(jù) MIME type 識(shí)別的

因?yàn)?ES6 Module 在瀏覽器中兼容并不是很好兼容性表,這里就不介紹瀏覽器支持情況了,我們一般不會(huì)直接在瀏覽器中使用

Nodejs中使用

nodejs es-modules.md

Node v8.5.0 以上支持 ES Module,需要 .mjs擴(kuò)展名

NOTE: DRAFT status does not mean ESM will be implemented in Node core. Instead that this is the standard, should Node core decide to implement ESM. At which time this draft would be moved to ACCEPTED.
(上面鏈接可以知道 ES Module的狀態(tài)是 DRAFT, 屬于起草階段)
// module.mjs
export default 123

// index.mjs
import module from "./module.mjs"
console.log(module) // 123

我們需要執(zhí)行 node --experimental-modules index.mjs 來(lái)啟動(dòng)
會(huì)提示一個(gè) ExperimentalWarning: The ESM module loader is experimental.該功能是實(shí)驗(yàn)性的(此提示不影響執(zhí)行)
ES Module 中導(dǎo)入 CommonJs

// module.js
module.exports.a = 123 // module.exports 就相當(dāng)于 export default

// index.mjs
import module from "./module.js"
console.log(module) // { a: 123 }

import * as module from "./module.js"
console.log(module) // { get default: { a: 123 } }

import { default as module } from "./module.js";
console.log(module) // { a: 123 }

import module from "module"; // 導(dǎo)入npm包 導(dǎo)入規(guī)則與 require 差不多

導(dǎo)入路徑規(guī)則與require差不多
這里要注意 module 擴(kuò)展名為 .js,.mjs專屬于 es module,import form導(dǎo)入的文件后綴名只能是.mjs,在 .mjsmodule未定義, 所以調(diào)用 module.exports,exports 會(huì)報(bào)錯(cuò)

nodeCommonJs 導(dǎo)入 es module 只能使用 import() 動(dòng)態(tài)導(dǎo)入/異步導(dǎo)入

// es.mjs
let foo = {name: "foo"};
export default foo;

export let a = 1

// cjs
import("./es").then((res)=>{
  console.log(res) // { get default: {name: "foo"}, a: 1 }
});
webpack中使用

webpack2 就默認(rèn)支持 es module 了,并默認(rèn)支持 CommonJs,支持導(dǎo)入 npm包, 這里 import 語(yǔ)法上面寫(xiě)太多 就不再寫(xiě)了

rollup中使用

rollup 專注于 es module,可以將 es module 打包為主流的模塊規(guī)范,注意這里與 webpack 的區(qū)別,我們可以在 webpackjs 中使用 Commonjs 語(yǔ)法, 但是 rollup 不支持,rollup需要 plugin 支持,包括加載 node_modules 下的包 form "react" 也需要 plugin 支持

可以看到 es module瀏覽器node兼容性差實(shí)驗(yàn)功能
我們大多時(shí)候在 打包工具 中使用

Tree-shaking

在最后我們說(shuō)一下經(jīng)常跟 es module 一起出現(xiàn)的一個(gè)名詞 Tree-shaking
Tree-shaking 我們先直譯一下 樹(shù)木搖晃 就是 搖晃樹(shù)木把上面枯死的樹(shù)葉晃下來(lái),在代碼中就是把沒(méi)有用到的代碼刪除
Tree-shaking 最早由 rollup 提出,之后 webpack 2 也開(kāi)始支持
這都是基于 es module 模塊特性的靜態(tài)分析

rollup

下面代碼使用 rollup 進(jìn)行打包:

// module.js
export let foo = "foo"
export let bar = "bar"

// index.js
import { foo } from "./module"
console.log(foo) // foo

在線運(yùn)行 我們可以修改例子與導(dǎo)出多種規(guī)范

打包結(jié)果:

let foo = "foo";

console.log(foo); // foo

可以看到 rollup 打包結(jié)果非常的簡(jiǎn)潔,并去掉了沒(méi)有用到的 bar
是否支持對(duì)導(dǎo)入 CommonJs 的規(guī)范進(jìn)行 Tree-shaking

// index.js
import { a } from "./module"
console.log(a) // 1

// module.js
module.exports.a = 1
module.exports.b = 2

打包為 es module

var a_1 = 2;

console.log(a_1);

可以看到去掉了未使用的 b

webpack

我們下面看看 webpack 的支持情況

// src/module.js
export function foo(){ return "foo" }
export function bar(){ return "bar" }

// src/index.js
import { foo } from "./module"
console.log(foo())

執(zhí)行 npx webpack -p(我們使用webpack 4,0配置,-p開(kāi)啟生成模式 自動(dòng)壓縮)
打包后我們?cè)诖虬募阉?bar 沒(méi)有搜到,bar被刪除
我們將上面例子修改一下:

// src/module.js
module.exports.foo = function (){ return "foo" }
module.exports.bar = function (){ return "bar" }

// src/index.js
import { foo } from "./module"
console.log(foo())

打包后搜索 bar 發(fā)現(xiàn)bar存在,webpack 并不支持對(duì)CommonJs 進(jìn)行 Tree-shaking

pkg.module

webpack 不支持 Commonjs Tree-shaking,但現(xiàn)在npm的包都是CommonJs規(guī)范的,這該怎么辦呢 ?如果我發(fā)了一個(gè)新包是 es module 規(guī)范, 但是如果代碼運(yùn)行在 node 環(huán)境,沒(méi)有經(jīng)過(guò)打包 就會(huì)報(bào)錯(cuò)

有一種按需加載的方案

全路徑導(dǎo)入,導(dǎo)入具體的文件:

// src/index.js
import remove from "lodash/remove"
import add from "lodash/add"

console.log(remove(), add())

使用一個(gè)還好,如果用多個(gè)的話會(huì)有很多 import 語(yǔ)句
還可以使用插件如 babel-plugin-lodash, & lodash-webpack-plugin

但我們不能發(fā)一個(gè)庫(kù)就自己寫(xiě)插件

這時(shí)就提出了在 package.json 加一個(gè) module 的字段來(lái)指向 es module規(guī)范的文件,main -> CommonJs,那么module - es module pkg.module

webpackrollup 都支持 pkg.module

加了 module 字段 webpack 就可以識(shí)別我們的 es module,但是還有一個(gè)問(wèn)題就是 babel

我們一般使用 babel 都會(huì)排除 node_modules,所以我們這個(gè) pkg.module 只是的 es6 module必須是編譯之后的 es5 代碼,因?yàn)?babel 不會(huì)幫我們編譯,我們的包就必須是 擁有 es6 module 規(guī)范的 es5 代碼

如果你使用了 presets-env 因?yàn)闀?huì)把我們的代碼轉(zhuǎn)為 CommonJs 所以就要設(shè)置 "presets": [["env", {"modules":false}] 不將es module 轉(zhuǎn)為 CommonJs

webpackrollup 的區(qū)別

webpack 不支持導(dǎo)出 es6 module 規(guī)范,rollup 支持導(dǎo)出 es6 module

webpack 打包后代碼很多冗余無(wú)法直接看,rollup 打包后的代碼簡(jiǎn)潔,可讀,像源碼

webpack 可以進(jìn)行代碼分割,靜態(tài)資源處理,HRM,rollup 專注于 es moduletree-shaking更加強(qiáng)大的,精簡(jiǎn)

如果是開(kāi)發(fā)應(yīng)用可以使用 webpack,因?yàn)榭梢赃M(jìn)行代碼分割,靜態(tài)資源,HRM,插件
如果是開(kāi)發(fā)類似 vue,react 等類庫(kù),rollup 更好一些,因?yàn)榭梢允鼓愕拇a精簡(jiǎn),無(wú)冗余代碼,執(zhí)行更快,導(dǎo)出多種模塊語(yǔ)法

結(jié)語(yǔ)

本文章介紹了 CommonjsES6 Module,導(dǎo)入導(dǎo)出的語(yǔ)法規(guī)則,路徑解析規(guī)則,兩者的區(qū)別,容易混淆的地方,在不同環(huán)境的區(qū)別,在不同環(huán)境的使用,Tree-shaking,與 webpack,rollup 的區(qū)別
希望您讀完文章后,能對(duì)前端的模塊化有更深的了解

參考鏈接

ECMAScript? 2015 Language Specification sec-imports/sec-exports

MDN import

github nodejs lib/module

github nodejs node-eps/002-es-modules

nodejs docs modules

Understanding ECMAScript 6

ECMAScript 6 入門(mén)

es6-modules-final

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101001.html

相關(guān)文章

  • JavaScript模塊化發(fā)展

    摘要:所有依賴這個(gè)模塊的語(yǔ)句,都定義在一個(gè)回調(diào)函數(shù)中,等到所有依賴加載完成之后前置依賴,這個(gè)回調(diào)函數(shù)才會(huì)運(yùn)行。如果將前面的代碼改寫(xiě)成形式,就是下面這樣定義了一個(gè)文件,該文件依賴模塊,當(dāng)模塊加載完畢之后執(zhí)行回調(diào)函數(shù),這里并沒(méi)有暴露任何變量。 模塊化是我們?nèi)粘i_(kāi)發(fā)都要用到的基本技能,使用簡(jiǎn)單且方便,但是很少人能說(shuō)出來(lái)但是的原因及發(fā)展過(guò)程。現(xiàn)在通過(guò)對(duì)比不同時(shí)期的js的發(fā)展,將JavaScript模...

    mengbo 評(píng)論0 收藏0
  • 帶你了解JavaScript相關(guān)的模塊機(jī)制

    摘要:本文從最簡(jiǎn)單的模塊開(kāi)始,然后主要從的模塊規(guī)范和的模塊機(jī)制對(duì)模塊進(jìn)行梳理。對(duì)象的屬性模塊的識(shí)別符,通常是帶有絕對(duì)路徑的模塊文件名。返回一個(gè)對(duì)象,表示調(diào)用該模塊的模塊。表示模塊對(duì)外輸出的值。可見(jiàn)當(dāng)刪除了相關(guān)模塊的緩存再一次加載時(shí)則不再有了。 前言 java有類文件,Python有import機(jī)制,Ruby有require等,而Javascript 通過(guò)標(biāo)簽引入代碼的機(jī)制顯得雜亂無(wú)章,語(yǔ)言自...

    ningwang 評(píng)論0 收藏0
  • 從 IIFE 聊到 Babel 帶你深入了解前端模塊化發(fā)展體系

    摘要:我覺(jué)得那時(shí)他可能并沒(méi)有料到,這一規(guī)則的制定會(huì)讓整個(gè)前端發(fā)生翻天覆地的變化。前言 作為一名前端工程師,每天的清晨,你走進(jìn)公司的大門(mén),回味著前臺(tái)妹子的笑容,摘下耳機(jī),泡上一杯茶,打開(kāi) Terminal 進(jìn)入對(duì)應(yīng)的項(xiàng)目目錄下,然后 npm run start / dev 或者 yarn start / dev 就開(kāi)始了一天的工作。 當(dāng)你需要進(jìn)行時(shí)間的轉(zhuǎn)換只需要使用 dayjs 或者 momentj...

    tinylcy 評(píng)論0 收藏0
  • JS 模塊導(dǎo)入/導(dǎo)出

    摘要:本文主要介紹幾種模塊導(dǎo)入導(dǎo)出的方法。默認(rèn)導(dǎo)出如果只在一個(gè)文件中提供了一個(gè)導(dǎo)出的口,就可以使用默認(rèn)導(dǎo)出在中可以看到輸入同樣是模塊導(dǎo)入導(dǎo)出方法,使用的模塊方法,要比中的也就是模塊方法更加的差異非常大。 在開(kāi)發(fā)中基本不會(huì)將所有的業(yè)務(wù)邏輯代碼放在一個(gè)JS文件中,特別是在使用前端框架,進(jìn)行組件化開(kāi)發(fā)中時(shí),會(huì)復(fù)用相應(yīng)的組件。這時(shí),就會(huì)用到模塊導(dǎo)入/導(dǎo)出的方法了。 當(dāng)然,上面提到有模塊的概念,也是在...

    wall2flower 評(píng)論0 收藏0
  • 深入淺出node.js總結(jié)-模塊機(jī)制(1)

    摘要:先天就缺乏一項(xiàng)功能模塊通過(guò)標(biāo)簽引入代碼的方式顯得雜亂無(wú)章,語(yǔ)言自身毫無(wú)組織和約束能力。與文件模塊區(qū)別地方在于它從內(nèi)存中加載緩存執(zhí)行結(jié)果的位置核心模塊在對(duì)象上,文件模塊在對(duì)象上未完待續(xù) javascript先天就缺乏一項(xiàng)功能:模塊 javasciprt 通過(guò)script標(biāo)簽引入代碼的方式顯得雜亂無(wú)章,語(yǔ)言自身毫無(wú)組織和約束能力。人們不得不用命名空間等方式人為地約束代碼,以求達(dá)到安全和易用的...

    jifei 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<