• It’s just a matter of days, but it’s just a matter of days
  • By Kent C. Dodds
  • The Nuggets translation Project
  • Permanent link to this article: github.com/xitu/gold-m…
  • Translator: Starrier
  • Proofreader: SinanJS, CAoyi0905

Misunderstanding ES6 module, a solution to upgrade Babel (tears)

Say many are tears…

On October 29, 2015, Sebastian McKenzie, James Kyle, and the rest of the Babel team released a massive release for front-end developers everywhere: Babel 6.0.0. That’s great, because it’s no longer a translator, but a pluggable JavaScript tool platform. As a community, we’ve only scratched the surface of what it can do, and I’m excited (cautiously optimistic) about the future of JavaScript tools.

All of which means that Babel 6.0.0 is a very significant release. It might be a little shaky at first. So upgrading is not easy and requires learning. This article doesn’t necessarily discuss how to upgrade Babel. I just want to discuss what I learned from my own code — when Babel fixed my heavy dependency problem… Before attempting to upgrade Babel 5 to Babel 6, I want you to read the following:

  • Clean up the Babel 6 ecosystem: With the recent release of Babel 6, each release has changed dramatically from its predecessor…

  • Quick Guide: How to Update Babel 5.x to 6.x: Babel 6 will be released soon.

ES6 module

If I can understand the ES6 module specification correctly, it won’t be that difficult for me to upgrade. Babel 5 allows the abuse of export and import statements, and Babel 6 solves this problem. At first I thought it might be a Bug. I asked this question on Stack Overflow and Logan Smyth, and the feedback told me that I had a fundamental misunderstanding of the ES6 module, and that Babel 5 contributed to that misunderstanding (writing a converter is hard).

The current crisis

At first, I wasn’t sure what Logan meant, but when I had time to focus on my app upgrade, here’s what happened:

Am I crazy? Is this an invalid ES6? export default { foo: ‘foo’, bar: ‘bar’, }

– the @ kentcdodds

Tyler McGinnis, Josh Manders, and I tested this thread. This may be hard to understand, but I realize that the problem is not getting the object exported by default, but how it can be imported as expected.

I can always export an object as the default and then deconstruct it to get the parts (fields) I need, as follows:

// foo.js
const foo = {baz: 42, bar: false}
export default foo

// bar.js
import {baz} from './foo'
Copy the code

Because Babel 5’s transformation exports the default statement, it allows us to do this. However, according to the specification, this is technically incorrect, which is why Babel 6 (correctly) removed this feature, as its ability to actually break the 200 + modules of my application at work.

As I reviewed Nicolas Bevacqua’s blog, I finally understood how it worked.

And of course, thanks to @NZGB for 350 amazing bullet points on ES6 as it’s very clear ponyfoo.com/articles/es… @ rauschma.

– the @ kentcdodds

When I read Axel Rauschmayer’s blog, I found out why I’ve been doing content that doesn’t work.

I want to thank @Rauschma for saving me from an early mid-life crisis with ES6 modules. I might have been too focused on this…

– the @ kentcdodds

The basic idea is that an ES6 module should be statically parsable (the export/import cannot be changed at run time) and therefore cannot be dynamic. In the example above, I could change foo’s object properties at run time, and then my import statement could import the dynamic properties like this:

// foo.js
const foo = {}
export default foo
somethingAsync().then(result => foo[result.key] = result.value)

// bar.js
import {foobar} from './foo'
Copy the code

We will assume result.key is’ foobar ‘. In CommonJS this is fine because the require statement occurs at runtime (when the module is needed) :

// foo.js
const foo = {}
module.exports = foo
somethingAsync().then(result => foo[result.key] = result.value)

// bar.js
const {foobar} = require('./foo')
Copy the code

However, because the ES6 specification states that imports and exports must be statically analyzable, you can’t accomplish this dynamic behavior in ES6.

That’s why Babel made the change. It’s unlikely, but that’s a good thing.

What does that mean?

It’s really hard to describe this problem in words, so I hope some code examples and comparisons will be instructive.

The problem I ran into was that I was combining ES6 exports with CommonsJS Require. I would do this:

// add.js
export default (x, y) => x + y

// bar.js
const three = require('./add') (1, 2)Copy the code

After Babel changed, I had three options:

Option 1: default require

// add.js
export default (x, y) => x + y

// bar.js
const three = require('./add').default(1, 2)
Copy the code

Select the 2:1000% ES6 module

// add.js
export default (x, y) => x + y

// bar.js
import add from './add'
const three = add(1, 2)
Copy the code

Choose CommonJS at 3:100 percent

// add.js
module.exports = (x, y) => x + y

// bar.js
const three = require('./add') (1, 2)Copy the code

How do I fix it?

A few hours later I was running the build and passing the test. I have two different methods for different scenes:

  1. I changed the export to CommonJS (Module.exports) instead of ES6 (export Default) so I could continue requiring as I always do.

  2. I wrote a complex regular expression to find and replace (should use a codemod) changes that shift other require statements from require(‘./thing ‘) to require(‘./thing ‘).default**.

It works perfectly, but the biggest challenge is to understand how the ES6 module specification works and how Babel translates it to CommonJS for interactive operations. Once I figured it out, following this rule to update my code became super easy.

advice

Try to avoid mixing ES6 modules and CommonsJS. Personally, I will try to use ES6. First, one of the reasons I mixed them together was that I could perform a single-line require and immediately use the required modules (such as require(‘./add ‘)(1, 2)). But it’s really not a big enough benefit (in my opinion).

If you feel you must combine them, consider using one of the following Babel plug-ins/presets:

  • babel-preset-es2015-node5: NPM is the package manager for JavaScript

  • babel-plugin-add-module-exports: NPM is the package manager for JavaScript


conclusion

The real lesson of all this is that we should understand how things work. I could save a lot of time if I understood how the ES6 module specification actually worked.

You may benefit from this egghead. IO lesson where I demonstrate how to upgrade from Babel 5 to Babel 6:

Egghead. IO/lessons/ang…

Also, remember that no one is perfect and we’re all here to learn 🙂 See you on Twitter


The appendix

More examples:

Before making changes to Babel, there is a require statement like this:

import add from './add'
const three = add(1, 2)
Copy the code

But after the Babel change, the Require statement now looks something like this:

import * as add from './add'
const three = add.default(1, 2)
Copy the code

I think the reason for the problem is that the add variable is no longer the default export, but an object with all named exports and default exports (under the default key).

Named export:

It is worth noting that you can use named exports, and my recommendation is to do so in the tools module. This allows you to perform destruction-like syntax in import statements (warning, although it doesn’t look like a real destructor because of the previous static analysis). So, here’s what you can do:

// math.js
const add = (x, y) => x + y
const subtract = (x, y) => x - y
const multiply = (x, y) => x * y
export {add, subtract, multiply}

// foo.js
import {subtract, multiply} from './math'
Copy the code

In the case of Tree shaking, this is exciting and great.

Personally, I usually recommend using default Export (pending content you know you want to import, single file, single component 😀) for components (like React components or Angular services). But for tool modules, there are usually various pure functions that can be used independently. This is a good use case for named exports.

One more thing

If you think this is fun, then you might like to check out the rest of my blog and subscribe to my latest content 💌 (the information will be posted to my blog 2 weeks after it is sent to email).

TestingJavaScript.com can learn better and more efficient ways to test any JavaScript program.

Thanks to Tyler McG

If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.


The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.