最近總碰到類似於
var a = require('./expample.js).default
這樣的代碼,感覺很奇葩,總結一波。
為什么會出現這個問題?
import
是靜態編譯的,而 require
可以動態加載,也就是說你可以通過判斷條件來決定什么時候去 require
,而 import
則不行,所以有時候我們會面臨需要通過require
去導入一個es6模塊(比如react-hot-loader官方demo :P)
當然,這只是場景之一。
前置知識
- ES6 Module常用語法。譬如
export
導出模塊接口 |import
倒入模塊|export default
語法糖 - Node.js模塊常用。譬如
module.exports
|require
- ES6模塊與commonjs模塊的區別(靜態編譯與動態加載 | 值得引用與值的拷貝)
如果上述前置知識您有所不了解的話,建議拜讀一下阮一峰老師的《ECMAScript 6 入門》一書中的兩個章節:
如果您已經具備了上述知識,我們來討論一下
export default
為什么是語法糖- require一個ES6 Module
export default
為什么是語法糖
default
關鍵字 說白了,就是別名(as)的語法糖
以下代碼應當是非常常見的:
導出接口
// a.js function a(){} export {a}
導入模塊
// b.js import {a} from './a'
花括號就是解構賦值的語法,我們可以理解為export
導出了一個對象,對象里存在a這個函數,就像下面這樣
{ a:function(){} }
於是就有了后面的通過結構賦值取出a,所以變量名必須一致。
ECMAScript 6 入門:從前面的例子可以看出,使用import命令的時候,用戶需要知道所要加載的變量名或函數名,否則無法加載。但是,用戶肯定希望快速上手,未必願意閱讀文檔,去了解模塊有哪些屬性和方法。
為了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,為模塊指定默認輸出。
default
可以理解為這一語法的語法糖
導出接口
// d.js export default function() {} // 等效於: function a() {}; export {a as default};
導入模塊
import a from './d'; // 等效於,或者說就是下面這種寫法的簡寫,是同一個意思 import {default as a} from './d';
這個語法糖的好處就是import的時候,可以省去花括號{}。
簡單的說,如果import的時候,你發現某個變量沒有花括號括起來(沒有*號),那么你在腦海中應該把它還原成有花括號的as語法。
本質上依舊是結構賦值呀,只不過我們寫的更為簡便,假裝花括號消失了罷了。
如何require一個ES6 Module
stackoverflow上有一個針對本文題目比較好的回答,原文如下:
Finally, the require and require.default... when dealing with ES6 imports (export default MyComponent), the exported module is of the format {"default" : MyComponent}. The import statement correctly handles this assignment for you, however, you have to do the require("./mycomponent").default conversion yourself. The HMR interface code cannot use import as it doesn't work inline. If you want to avoid that, use module.exports instead of export default.
我來翻譯下原文:
最后,require和require.default...當在node中處理ES6 模塊(export default mycomponent)導入的時候,導出的模塊格式為
{ "default": mycomponent }
語句正確地為你處理了這個問題,然而你必須自己執行
importrequire("./mycomponent").default
. HMR(熱更新模塊)不在inline
模式工作的情況下,接口代碼不能使用import
,如果你想避免,使用module.exports
而不是export default
;
上文提到過,export
關鍵字是導出一個對象,對象內存在一個屬性(我們要暴露的),export default
則是 export
語法糖,import
一個export default
暴露出來的模塊包含了解構賦值的步驟,所以在node中使用require
去請求一個export default
的模塊需要我們通過.
語法去取出對象中的屬性(因為require木有解構賦值),清晰明了。
換個說法,如果 require
的 commonjs規范的模塊,即:
導出:
// a.js module.exports = { a:'helloworld' }
導入:
// b.js var m = require('./a.js'); console.log(m.a); // helloworld
這樣就顯得非常清晰,我們 module.exports
的是啥,require
的就是啥。
但export default包裝了一層語法糖,讓我們看得不甚清晰:
const a = 'helloworld'; export default a;
其實導出的是
{ "default": a }
而並非 a
這個變量,這就是我為什么之前要強調語法糖了,如果你將 export default
還原為:
const a = 'helloworld'; export {a as default}
是不是就能清楚一點了呢。
我們的疑問也就迎刃而解了。
注:以上所有代碼都是在webpack開發環境(babel)中運行(保證ES6模塊語法可以被識別)。