<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入 CommonJs 與 ES6 Module

          共 16805字,需瀏覽 34分鐘

           ·

          2021-04-12 09:56

          點擊上方藍字“TianTianUp”關(guān)注我
          您的關(guān)注意義重大

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

          CommonJs

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

          模塊導(dǎo)出

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

          // foo.js

          //一個一個 導(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)入的時候使用
          exports = { age: 1, a: 'hello', foo:function(){} }

          這里需要注意 exports 不能被賦值,可以理解為在模塊開始前exports = module.exports, 因為賦值之后exports失去了 對module.exports的引用,成為了一個模塊內(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 相對路徑開頭

          在沒有指定后綴名的情況下
          先去尋找同級目錄同級目錄:src/app/

          • src/app/moduleA 無后綴名文件 按照javascript解析

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

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

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

          同級目錄沒有 moduleA 文件會去找同級的 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 絕對路徑開頭

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

          react 沒有路徑開頭

          沒有路徑開頭則視為導(dǎo)入一個包
          會先判斷moduleA是否是一個核心模塊 如pathhttp,優(yōu)先導(dǎo)入核心模塊
          不是核心模塊 會從當(dāng)前文件的同級目錄的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 如果沒找到 繼續(xù)向父目錄的node_modules中找

          • /src/node_modules/

          • /node_modules/

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

          require wrapper

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

          function wrapper (script) {
          return '(function (exports, require, module, __filename, __dirname) {' +
          script +
          '\n})'
          }

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

          const module = { exports: {} }

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

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


          return module.exports
          }

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

          • 模塊只執(zhí)行一次 之后調(diào)用獲取的 module.exports 都是緩存哪怕這個 js 還沒執(zhí)行完畢(因為先加入緩存后執(zhí)行模塊)

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

          • exports 和 module.exports 持有相同引用,因為最后導(dǎo)出的是 module.exports, 所以對exports進行賦值會導(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)

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

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

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

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

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

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

          6. require(a) 此時 a.js 是第二次調(diào)用 require -> 判斷緩存(有)-> cachedModule.exports -> 回到 b.js(因為js對象引用問題 此時的 cachedModule.exports = { a: 1 }

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

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

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

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

          es6 module

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

          es6 module 基本語法:

          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; // 默認導(dǎo)出
          export default function () { ... } // 或者 function*, class
          export default function name1() { ... } // 或者 function*, class
          export { name1 as default, ... }; // 重命名為默認導(dǎo)出

          export 規(guī)則

          • export * from '' 或者 export {} from '',重定向?qū)С觯囟ㄏ虻拿⒉荒茉诒灸K使用,只是搭建一個橋梁,例如:這個a并不能在本模塊內(nèi)使用

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

          • export Declaration,聲明的同時,命名導(dǎo)出, Declaration就是: varletconstfunctionfunction*class 這一類的聲明語句

          • export default AssignmentExpression,默認導(dǎo)出, AssignmentExpression的 范圍很廣,可以大致理解 為除了聲明Declaration(其實兩者是有交叉的),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


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

          // 默認導(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(){}

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

          //動態(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默認導(dǎo)出

          • import * as name from 'module', 將module.js的所有導(dǎo)出合并為name的對象,key為導(dǎo)出的命名,默認導(dǎo)出的keydefault

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

          • import('module'),動態(tài)導(dǎo)入返回一個 PromiseTC39stage-3階段被提出 tc39 import

          ES6 module 特點

          ES6 module的語法是靜態(tài)的

          import 會自動提升到代碼的頂層

          export 和 import 只能出現(xiàn)在代碼的頂層,下面這段語法是錯誤

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

          import defaultExport from 'module'
          }

          true || export let a = 1

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

          import 'defaultExport' from 'module'

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

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

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

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

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

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

          // 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 是無法 a 的動態(tài)綁定 這一點跟 CommonJs 有點相似 都是值的拷貝
          let a = 1;
          export default a

          // 可以用另一種方式實現(xiàn) default 的動態(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)
          1. 執(zhí)行 main.js -> 導(dǎo)入 bar.js

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

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

          我們可以使用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)

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

          CommonJs 和 ES6 Module 的區(qū)別

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

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

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

          • CommonJs是動態(tài)語法可以寫在判斷里,ES6 Module靜態(tài)語法只能寫在頂層

          • CommonJs的 this 是當(dāng)前模塊,ES6 Module的 this 是 undefined

          易混淆點

          模塊語法與解構(gòu)

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

          import { a } from 'module'

          const { a } = require('module')

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

          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: []}) {}

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

          導(dǎo)出語法與對象屬性簡寫

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

          let a = 1

          export { a } // 導(dǎo)出語法
          export default { a } // 屬性簡寫 導(dǎo)出 { a: 1 } 對象

          module.exports = { a } // 屬性簡寫 導(dǎo)出 { a: 1 } 對象

          export default 和 module.exports 是相似的

          ES6 module 支持 CommonJs 情況

          先簡單說一下各個環(huán)境的 ES6 module 支持 CommonJs 情況,后面單獨說如何在不同環(huán)境中使用

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

          我們現(xiàn)在大多使用的是 webpack 進行項目構(gòu)建打包,因為現(xiàn)在前端開發(fā)環(huán)境都是在 Node 環(huán)境原因,而 npm 的包都是 CommonJs 規(guī)范的,所以 webpack 對ES6模塊進行擴展 支持 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 的每一項 又當(dāng)做 export 輸出,這樣做是為了更加簡潔
          import defaultExport from './foo', defaultExport.foo()
          import { foo } from './foo', foo()

          使用 ES6 Module

          可以在 es6module example 倉庫中獲取代碼在本地進行測試驗證

          瀏覽器中使用

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

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

          // module.js
          export default 123

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

          <!-- index.html -->
          <script type="module">
          import module from './module.js'
          console.log(module) // 123
          </script>

          html中通過 script 的 src 引用

          <!-- index.html -->
          <script type="module" src="index.js"></script>
          // 控制臺 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

          補充:

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

          • 后端可以修改接口/getjs?name=module這一類的,不過后端要返回 Content-Type: application/javascript 確保返回的是js,因為瀏覽器是根據(jù) MIME type 識別的

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

          Nodejs中使用

          nodejs es-modules.md

          在 Node v8.5.0 以上支持 ES Module,需要 .mjs擴展名

          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 來啟動
          會提示一個 ExperimentalWarning: The ESM module loader is experimental.該功能是實驗性的(此提示不影響執(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 擴展名為 .js.mjs專屬于 es moduleimport form導(dǎo)入的文件后綴名只能是.mjs,在 .mjs中 module未定義, 所以調(diào)用 module.exports,exports 會報錯

          node中 CommonJs 導(dǎo)入 es module 只能使用 import() 動態(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 就默認支持 es module 了,并默認支持 CommonJs,支持導(dǎo)入 npm包, 這里 import 語法上面寫太多 就不再寫了

          rollup中使用

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

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

          Tree-shaking

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

          rollup

          下面代碼使用 rollup 進行打包:

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

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

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

          打包結(jié)果:

          let foo = 'foo';

          console.log(foo); // foo

          可以看到 rollup 打包結(jié)果非常的簡潔,并去掉了沒有用到的 bar
          是否支持對導(dǎo)入 CommonJs 的規(guī)范進行 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開啟生成模式 自動壓縮)
          打包后我們在打包文件搜索 bar 沒有搜到,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 并不支持對CommonJs 進行 Tree-shaking

          pkg.module

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

          有一種按需加載的方案

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

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

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

          使用一個還好,如果用多個的話會有很多 import 語句
          還可以使用插件如 babel-plugin-lodash, & lodash-webpack-plugin

          但我們不能發(fā)一個庫就自己寫插件

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

          webpack 與 rollup 都支持 pkg.module

          加了 module 字段 webpack 就可以識別我們的 es module,但是還有一個問題就是 babel

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

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

          webpack 與 rollup 的區(qū)別

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

          • webpack 打包后代碼很多冗余無法直接看,rollup 打包后的代碼簡潔,可讀,像源碼

          • webpack 可以進行代碼分割,靜態(tài)資源處理,HRMrollup 專注于 es moduletree-shaking更加強大的,精簡

          如果是開發(fā)應(yīng)用可以使用 webpack,因為可以進行代碼分割,靜態(tài)資源,HRM,插件
          如果是開發(fā)類似 vuereact 等類庫,rollup 更好一些,因為可以使你的代碼精簡,無冗余代碼,執(zhí)行更快,導(dǎo)出多種模塊語法

          結(jié)語

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

          參考鏈接

          • 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 入門

          • es6-modules-final

          END



          如果覺得這篇文章還不錯
          點擊下面卡片關(guān)注我
          來個【分享、點贊、在看】三連支持一下吧

             “分享、點贊在看” 支持一波  

          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  楪可怜Av一区二区三区 | 大鸡把网站 | 狼人奸伊人综合成人网 | 人人摸人人摸人人摸 | 婷婷五月天丁香成人社区 |