前幾天團隊小夥伴問我一個問題,我在ts文件中使用解構導出了一個變量,在其餘地方 import
進來發現是 undefined
,相似這樣node
//a.ts export const a = { a1: 1, a2: 2 } export const b = { b1: 1 } export default { ...a, b }
// b.ts import { a1 } from 'a'; console.log(a1): // undefined
這裏拋出一個疑問?webpack
babel-plugin-add-module-exports
兼容了 export default
,可是就是取不到?接下來咱們從 export defalut -> babel -> add-module-exports來逐步的瞭解下爲何git
export default
命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,所以export default
命令只能使用一次。本質上,export default
就是輸出一個叫作default
的變量或方法,而後系統容許你爲它取任意名字。es6
相似這樣
導出函數github
//a.js export default funcion() { //xxx } //b.js import foo from 'a';
導出對象web
//c.js const c = { c1:1, c2:2 } const d = { d1:1, d2:2 } export default { c,d } //d.js import obj from 'c' console.log(obj); // {c:{c1:1,c2:2},d:{d1:1,d2:2}}
導出defaultexpress
//a.js function foo(){} export { foo as default}; // 等同於 // export default foo; // b.js import { default as foo } from 'a'; // 等同於 // import foo from 'a';
到這裏看起來一切很美好,有一個新問題:在d.js
裏,我想直接拿到 obj
裏的 c
屬性,能夠嗎?segmentfault
const c = { c1:1, c2:2 } const d = { d1:1, d2:2 } export default { c,d } //d.mjs import {c} from 'c' console.log(c); // 報錯了 // terminal node --experimental-modules d.js /* import {c} from 'c'; ^ SyntaxError: The requested module 'c' does not provide an export named 'c' */
其實這樣寫是錯的,由於ES6的import並非對象解構語法,只是看起來比較像,能夠參考MDN對import的描述MDN import。因此import並不能解構一個default對象。瀏覽器
既然import不支持解構一個default對象,那麼咱們心中又有一個疑問,爲何咱們在項目中可以隨意的去寫 export default, 而且經過解構能夠取的到呢?babel
export default
屬於 ES6 的語法,爲了兼容不支持 ES6 的瀏覽器,因此須要 babel 編譯。接下來咱們看看通過babel編譯以後,export default
變成了什麼。
在使用babel5的時候,下面代碼
//a.js const a = {}; const b = {}; export default {a,b} //b.js import {a} from 'b' console.log(a)
會被打包爲
//a.js ... let _default = _objectSpread({}, {a, b}); exports.default = _default; module.exports = exports.default; //b.js "use strict"; var _const = require("./a"); console.log(_const.a);
babel 把 esm 解析成了cjs,咱們執行 b.js,發現能夠取到值,可是在瀏覽器環境require語法,咱們還須要webpack,由於webpack 簡單來講是對babel轉換後的文件作了一層 require 的包裝,因此這裏具體不談webpack作了什麼,只討論babel, webpack具體作了什麼能夠戳這裏查看
webpack啓動代碼解讀
webpack模塊化原理
項目升級babel 6 以後,發現以前寫法取不到值了,上面的 a.js 和 b.js 打包後變爲
//a.js ... let _default = _objectSpread({}, {a, b}); exports.default = _default; // babel6 去掉了 module.exports = exports.default; //b.js "use strict"; var _const = require("./a"); console.log(_const.a);
這個時候 _const
的值爲 {default: {a:{},b{}}}
。
出現這個的緣由是由於 Babel 的這個Issue Kill CommonJS default export behaviour,因此 Babel 6經過再也不執行module.exports = exports['default']
模塊轉換來更改某些行爲。Babel5 是支持export 一個對象,可是到 Babel6 的時候去掉了這個特性。這個時候咱們爲了兼容老代碼,須要一個解決方案,這個時候 babel-plugin-add-module-exports 入場了。
babel-plugin-add-module-exports
主要做用是補全 Babel6 去掉的 module.exports = exports.default;
問題來了,項目中配置了babel-plugin-add-module-exports
爲何前沿中的代碼會有問題呢
答案很簡單,咱們發現 babel-plugin-add-module-exports 失效了,深刻源碼打個log發現會判斷是否有 name export,若是有 name export,就不會補上 babel5 export default object的特性。
// hasExportNamed 一直是 true ... if (hasExportDefault && !hasExportNamed) { path.pushContainer('body', \[types.expressionStatement(types.assignmentExpression('=', types.memberExpression(types.identifier('module'), types.identifier('exports')), types.memberExpression(types.identifier('exports'), types.stringLiteral('default'), true)))\]); } ...
解決方案:
咱們只須要改動代碼爲
//a.ts const a = { a1: 1, a2: 2 } const b = { b1: 1 } export default { ...a, b }
// b.ts import { a1 } from 'a'; console.log(a1): // 1
咱們只須要去掉 export const, 只保留 export default 便可解決這個問題。
有好奇的同窗問,爲何有了name export,就不會去補全module.exports = exports.default
。看到下面的例子你就明白了
//a.ts export const a = { a1: 1, a2: 2 } const b = { b1: 1 } export default { b }
// b.ts import { a } from 'a'; console.log(a);
打包後手動加一個 module.exports = exports.default
,
//a.js ... let _default = _objectSpread({}, {b}); exports.default = _default; module.exports = exports.default;
結果可想而知,在b文件require進來的時候,a 找不到了
//b.js "use strict"; var _const = require("./a"); console.log(_const.a); // undefined
export default
配合babel-plugin-add-module-exports
給了咱們很好的開發體驗,可是仍是要遵照export default
的基本規則:
雖然es6 export default
導出的內容有工具幫你處理,可是 es6 import
不是解構語法。須要注意的是,在引入一個有默認輸出的模塊時,這時import
命令後面,不使用大括號,不要對引入的內容進行解構。
幫助連接:
關於 import、require、export、module.exports