Babel 6如何导出默认项发生了改变

205

以前,babel会添加这一行module.exports = exports["default"]。现在不再这样做了。这意味着以前我可以这样做:

var foo = require('./foo');
// use foo

现在我必须这样做:

var foo = require('./foo').default;
// use foo

这并不是一个大问题(我猜这才是一开始就应该的方式)。 问题在于,我有很多代码依赖于以前的工作方式(我可以将其中大部分转换为ES6导入,但不是全部)。是否有人可以给我一些提示,让旧方式能够正常工作,而无需遍历我的项目并修复它(甚至提供一些有关如何编写codemod的说明也会很棒)。

谢谢!

示例:

输入:

const foo = {}
export default foo

Babel 5的输出

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var foo = {};
exports["default"] = foo;
module.exports = exports["default"];

Babel 6 输出(配合 es2015 插件):

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
var foo = {};
exports["default"] = foo;

注意输出结果中唯一的不同是 module.exports = exports["default"]


编辑

如果你想了解我解决特定问题后写的博客文章,可能会感兴趣:误解ES6模块,升级Babel,眼泪以及解决方案


我很好奇,如果你在一个使用Babel的代码库中工作,什么情况下需要使用require?很有可能,还有其他方法可以避免使用它。 - loganfsmyth
我正在利用Webpack的一个功能,如果在死代码中发现这样的代码:if (false) { require('./foo') },Webpack将不需要包含foo.js文件在生成的捆绑包中。 - kentcdodds
你在那里最终得到的false开关是什么?如果它是在你的webpack配置中可用的条件,可能还有其他选项。 - loganfsmyth
1
在我找到这篇文章之前,这个问题困扰了我好几个小时。最终,我用module.exports = {foo, bar}替换了所有的export default {foo, bar}。我非常喜欢这种现在不再支持的“错误”方法。 - stumct
@loganfsmyth 这对于传递整个模块而不需要在代码中重复很有用。请查看此要点 https://gist.github.com/loopmode/3eeaf0764c30439add1d8008e39d0267 - Jovica Aleksic
4个回答

109

如果您想要CommonJS导出行为,您需要直接使用CommonJS(或使用其他答案中提到的插件)。此行为已被删除,因为它会导致混淆并导致无效的ES6语义,这是一些人所依赖的。

export default {
  a: 'foo'
};

然后

import {a} from './foo';

这是无效的ES6代码,但由于您所描述的CommonJS互操作行为而起作用。不幸的是,支持两种情况是不可能的,允许人们编写无效的ES6代码比让您执行 .default 更糟糕。

另一个问题是,如果将来用户添加了命名导出,例如

export default 4;

然后

require('./mod');
// 4

但是

export default 4;
export var foo = 5;

那么

require('./mod')
// {'default': 4, foo: 5}

我同意你的观点(并已注意到)之前的行为是不正确的,但我的问题是如何解决这个问题。我过于依赖错误的行为(直到今天早上才意识到它是错误的)。我希望不必一次更新所有内容... - kentcdodds
唯一的解决方法是将您的代码直接切换到使用CommonJS,或者在有时间更新之前继续使用Babel 5。 - loganfsmyth
4
我们可以编写一个Webpack加载器来保持其工作(或者一个Babel插件)。我很惊讶他们没有提供一个(或更加强烈地宣传这个变更!) - Jamund Ferguson
我对此感到困惑...如果我在模块A中执行export default function() {},然后在模块B中执行import a from 'a',使用Babel 6,a将是{default: function() {}}...从我所理解的http://exploringjs.com/es6/ch_modules.html#sec_mixing-named-and-default-exports来看,这应该可以工作,并且我应该在B中获得导出的函数,而不是对象。 - mamapitufo
@mamapitufo 那应该可以,但如果没有示例很难说出问题在哪里。如果您想聊天,请随时访问Slack上的Babel支持频道。 - loganfsmyth
显示剩余3条评论

94

你还可以使用此插件来恢复旧的export行为。


1
我知道迟早会有人写一个插件。谢谢! - kentcdodds
遗憾的是,babel-plugin-add-module-exports目前还不支持AMD风格的模块。 - zowers
3
我在我的项目中使用了babel-plugin-transform-es2015-modules-simple-amd,以解决与AMD模块相关的同样问题。 - Tom Wayson
我认为使用UMD和这个插件是正确的选择!谢谢 - electronix384128
非常非常有帮助。 - Jovica Aleksic

34

对于库的作者,你可能能够解决这个问题。

我通常会有一个入口点index.js,这是我从package.json中的主要字段指向的文件。它除了重新导出实际的库入口点之外什么也不做:

export { default } from "./components/MyComponent";
为了解决babel问题,我将其更改为import语句,然后将默认值分配给module.exports:
import MyComponent from "./components/MyComponent";
module.exports = MyComponent;

我的所有其他文件都保持纯ES6模块,没有任何变通方法。因此,只有入口点需要稍微更改 :)

这将适用于commonjs requires,并且也适用于ES6 imports,因为babel似乎没有丢弃反向兼容性(commonjs-> es6)。Babel注入以下函数以修补commonjs:

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 

我花了数小时来解决这个问题,所以我希望这可以为其他人节省时间和精力!


由于某些原因,我从来没有完全理解module.exportsexport default的东西。现在我们又回到了起点? - windmaomao
@windmaomao 你是什么意思?这是一个技巧,以便 commonjs 用户不必 require("whatever").default。如果你不是一个库的作者,那么这可能与你无关。 - WickyNilliams

2

我曾经遇到过这种问题。这是我的解决方案:

//src/arithmetic.js

export var operations = {
  add: function (a, b) {
      return a + b;
  },

  subtract: function (a, b) {
      return a - b;
  }
};

//src/main.js

import { operations }  from './arithmetic';

let result = operations.add(1, 1);

console.log(result);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接