前几天团队小伙伴问我一个问题,我在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