ES6 In Depth is a series on new features being added to the JavaScript programming language in the 6th Edition of the ECMAScript standard, ES6 for short.
When I started on Mozilla’s JavaScript team back in 2007, the joke was that the length of a typical JavaScript program was one line.
This was two years after Google Maps launched. Not long before that, the predominant use of JavaScript had been form validation, and sure enough, your average<input onchange=> handler would be… one line of code.
Things have changed. JavaScript projects have grown to jaw-dropping sizes, and the community has developed tools for working at scale. One of the most basic things you need is a module system, a way to spread your work across multiple files and directories—but still make sure all your bits of code can access one another as needed—but also be able to load all that code efficiently. So naturally, JavaScript has a module system. Several, actually. There are also several package managers, tools for installing all that software and coping with high-level dependencies. You might think ES6, with its new module syntax, is a little late to the party.
Well, today we’ll see whether ES6 adds anything to these existing systems, and whether or not future standards and tools will be able to build on it. But first, let’s just dive in and see what ES6 modules look like.
Module basics
An ES6 module is a file containing JS code. There’s no specialmodule keyword; a module mostly reads just like a script. There are two differences.
ES6 modules are automatically strict-mode code, even if you don’t write
"use strict";in them.You can use
importandexportin modules.
Let’s talk aboutexport first. Everything declared inside a module is local to the module, by default. If you want something declared in a module to be public, so that other modules can use it, you mustexport that feature. There are a few ways to do this. The simplest way is to add theexport keyword.
<pre>
// kittydar.js - Find the locations of all the cats in an image.
// (<a href="https://harthur.github.io/kittydar/" target="_blank">Heather Arthur wrote this library for real</a>)
// (but she didn't use modules, because it was 2013)
<strong>export</strong> function detectCats(canvas, options) {
var kittydar = new Kittydar(options);
return kittydar.detectCats(canvas);
}
<strong>export</strong> class Kittydar {
... several methods doing image processing ...
}
// This helper function isn't exported.
function resizeCanvas() {
...
}
...
</pre>
You canexport any top-levelfunction,class,var,let, orconst.
And that’s really all you need to know to write a module! You don’t have to put everything in anIIFE or a callback. Just go ahead and declare everything you need. Since the code is a module, not a script, all the declarations will be scoped to that module,not globally visible across all scripts and modules. Export the declarations that make up the module’s public API, and you’re done.
Apart from exports, the code in a module is pretty much just normal code. It can use globals likeObject andArray. If your module runs in a web browser, it can usedocument andXMLHttpRequest.
In a separate file, we can import and use thedetectCats() function:
<pre>
// demo.js - Kittydar demo program
import {detectCats} from "kittydar.js";
function go() {
var canvas = document.getElementById("catpix");
var cats = detectCats(canvas);
drawRectangles(canvas, cats);
}
</pre>
To import multiple names from a module, you would write:
<pre>
import {detectCats, Kittydar} from "kittydar.js";
</pre>
When you run a module containing animport declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.
And those are the basics of modules. It’s really quite simple. ;-)
Export lists
Rather than tagging each exported feature, you can write out a single list of all the names you want to export, wrapped in curly braces:
<pre>
export {detectCats, Kittydar};
// no `export` keyword required here
function detectCats(canvas, options) { ... }
class Kittydar { ... }
</pre>
Anexport list doesn’t have to be the first thing in the file; it can appear anywhere in a module file’s top-level scope. You can have multipleexport lists, or mixexport lists with otherexport declarations, as long as no name is exported more than once.
Renaming imports and exports
Once in a while, an imported name happens to collide with some other name that you also need to use. So ES6 lets you rename things when you import them:
<pre>
// suburbia.js
// Both these modules export something named `flip`.
// To import them both, we must rename at least one.
import {flip as flipOmelet} from "eggs.js";
import {flip as flipHouse} from "real-estate.js";
...
</pre>
Similarly, you can rename things when you export them. This is handy if you want to export the same value under two different names, which occasionally happens:
<pre>
// unlicensed_nuclear_accelerator.js - media streaming without drm
// (not a real library, but maybe it should be)
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
</pre>
Default exports
The new standard is designed to interoperate with existing CommonJS and AMD modules. So suppose you have a Node project and you’ve donenpm install lodash. Your ES6 code can import individual functions from Lodash:
<pre>
import {each, map} from "lodash";
each([3, 2, 1], x => console.log(x));
</pre>
But perhaps you’ve gotten used to seeing_.each rather thaneach and you still want to write things that way. Or maybe you want to use_ as a function, sincethat’s a useful thing to do in Lodash.
For that, you can use a slightly different syntax: import the module without curly braces.
<pre>
import _ from "lodash";
</pre>
This shorthand is equivalent toimport {default as _} from "lodash";. All CommonJS and AMD modules are presented to ES6 as having adefault export, which is the same thing that you would get if you askedrequire() for that module—that is, theexports object.
ES6 modules were designed to let you export multiple things, but for existing CommonJS modules, the default export is all you get. For example, as of this writing, the famouscolors package doesn’t have any special ES6 support as far as I can tell. It’s a collection of CommonJS modules, like most packages on npm. But you can import it right into your ES6 code.
<pre>
// ES6 equivalent of `var colors = require("colors/safe");`
import colors from "colors/safe";
</pre>
If you’d like your own ES6 module to have a default export, that’s easy to do. There’s nothing magic about a default export; it’s just like any other export, except it’s named"default". You can use the renaming syntax we already talked about:
<pre>
let myObject = {
field1: value1,
field2: value2
};
export {myObject as default};
</pre>
Or better yet, use this shorthand:
<pre>
<strong>export default</strong> {
field1: value1,
field2: value2
};
</pre>
The keywordsexport default can be followed by any value: a function, a class, an object literal, you name it.
Module objects
Sorry this is so long. But JavaScript is not alone: for some reason, module systems in all languages tend to have a ton of individually small, boring convenience features. Fortunately, there’s just one thing left. Well, two things.
<pre>
import * as cows from "cows";
</pre>
When youimport *, what’s imported is amodule namespace object. Its properties are the module’s exports. So if the “cows” module exports a function namedmoo(), then after importing “cows” this way, you can write:cows.moo().
Aggregating modules
Sometimes the main module of a package is little more than importing all the package’s other modules and exporting them in a unified way. To simplify this kind of code, there’s an all-in-one import-and-export shorthand:
<pre>
// world-foods.js - good stuff from all over
// import "sri-lanka" and re-export some of its exports
export {Tea, Cinnamon} from "sri-lanka";
// import "equatorial-guinea" and re-export some of its exports
export {Coffee, Cocoa} from "equatorial-guinea";
// import "singapore" and export ALL of its exports
export * from "singapore";
</pre>
Each one of theseexport-from statements is similar to animport-from statement followed by anexport. Unlike a real import, this doesn’t add the re-exported bindings to your scope. So don’t use this shorthand if you plan to write some code inworld-foods.js that makes use ofTea. You’ll find that it’s not there.
If any name exported by “singapore” happened to collide with the other exports, that would be an error, so useexport * with care.
Whew! We’re done with syntax! On to the interesting parts.
What doesimport actually do?
Would you believe…nothing?
Oh, you’re not that gullible. Well, would you believe the standardmostly doesn’t say whatimport does? And that this is a good thing?
ES6 leaves the details of module loading entirelyup to the implementation. The rest of module execution isspecified in detail.
Roughly speaking, when you tell the JS engine to run a module, it has to behave as though these four steps are happening:
Parsing: The implementation reads the source code of the module and checks for syntax errors.
Loading: The implementation loads all imported modules (recursively). This is the part that isn’t standardized yet.
Linking: For each newly loaded module, the implementation creates a module scope and fills it with all the bindings declared in that module, including things imported from other modules.
This is the part where if you try to
import {cake} from "paleo", but the “paleo” module doesn’t actually export anything namedcake, you’ll get an error. And that’s too bad, because you wereso close to actually running some JS code. And having cake!Run time: Finally, the implementation runs the statements in the body of each newly-loaded module. By this time,
importprocessing is already finished, so when execution reaches a line of code where there’s animportdeclaration… nothing happens!
See? I told you the answer was “nothing”. I don’t lie about programming languages.
But now we get to the fun part of this system. There’s a cool trick. Because the system doesn’t specify how loading works, and because you can figure out all the dependencies ahead of time by looking at theimport declarations in the source code, an implementation of ES6 is free to do all the work at compile time and bundle all your modules into a single file to ship them over the network! And tools likewebpack actually do this.
This is a big deal, because loading scripts over the network takes time, and every time you fetch one, you may find that it containsimport declarations that require you to load dozens more. A naive loader would require a lot of network round trips. But with webpack, not only can you use ES6 with modules today, you get all the software engineering benefits with no run-time performance hit.
A detailed specification of module loading in ES6 was originally planned—and built. One reason it isn’t in the final standard is that there wasn’t consensus on how to achieve this bundling feature. I hope someone figures it out, because as we’ll see, module loading really should be standardized. And bundling is too good to give up.
Static vs. dynamic, or: rules and how to break them
For a dynamic language, JavaScript has gotten itself a surprisingly static module system.
All flavors of
importandexportare allowed only at toplevel in a module. There are no conditional imports or exports, and you can’t useimportin function scope.All exported identifiers must be explicitly exported by name in the source code. You can’t programmatically loop through an array and export a bunch of names in a data-driven way.
Module objects are frozen. There is no way to hack a new feature into a module object, polyfill style.
All of a module’s dependencies must be loaded, parsed, and linked eagerly, before any module code runs. There’s no syntax for an
importthat can be loaded lazily, on demand.There is no error recovery for
importerrors. An app may have hundreds of modules in it, and if anything fails to load or link, nothing runs. You can’timportin atry/catchblock. (The upside here is that because the system is so static, webpack can detect those errors for you at compile time.)There is no hook allowing a module to run some code before its dependencies load. This means that modules have no control over how their dependencies are loaded.
The system is quite nice as long as your needs are static. But you can imagine needing a little hack sometimes, right?
That’s why whatever module-loading system you use will have a programmatic API to go alongside ES6’s staticimport/export syntax. For example,webpack includes an API that you can use for “code splitting”, loading some bundles of modules lazily on demand. The same API can help you break most of the other rules listed above.
The ES6 modulesyntax is very static, and that’s good—it’s paying off in the form of powerful compile-time tools. But the static syntax was designed to work alongside a rich dynamic, programmatic loader API.
When can I use ES6 modules?
To use modules today, you’ll need a compiler such asTraceur orBabel. Earlier in this series,Gastón I. Silva showed how to use Babel and Broccoli to compile ES6 code for the web; building on that article, Gastón hasa working example with support for ES6 modules. Thispost by Axel Rauschmayer contains an example using Babel and webpack.
The ES6 module system was designed mainly by Dave Herman and Sam Tobin-Hochstadt, who defended the static parts of the system against all comers (including me) through years of controversy. Jon Coppeard is implementing modules in Firefox. Additional work on aJavaScript Loader Standard is underway. Work to add something like<script type=module> to HTML is expected to follow.
And that’s ES6.
This has been so much fun that I don’t want it to end. Maybe we should do just one more episode. We could talk about odds and ends in the ES6 spec that weren’t big enough to merit their own article. And maybe a little bit about what the future holds. Please join me next week for the stunning conclusion of ES6 In Depth.
About Jason Orendorff
Thanks! Please check your inbox to confirm your subscription.
If you haven’t previously confirmed a subscription to a Mozilla-related newsletter you may have to do so. Please check your inbox or your spam filter for an email from us.
33 comments
- Filipe Silva
August 15th, 2015 at 02:06This has been a great series of articles. Thank you for doing it.
I’ve been following every week and am now in the process of updating part of my company front-end to es6, and reading all of these insights really helps.
- Vincent
August 15th, 2015 at 02:20Thanks for the series! Looking forward to the conclusion :)
- voracity
August 15th, 2015 at 05:52So ‘class’ brings us into the realm of static, but really it just codifies what everyone was messily doing before with new . (And everyone has been thoroughly warned about overuse of ‘extends’, so hopefully no hierarchy nightmares.)
This looks different. I hadn’t given much thought to it when I first saw it years ago, but if the ‘class’ keyword is a little bit anti-JS (where in the past I used JS as a synonym for “dynamic almost-everything”), this looks completely anti-JS.
What we critically needed was a simple in-built import() function that worked mostly like require(), maybe coupled with decorators (for exporting and/or privatising things) and destructuring to pick what we want to import. (Of course, we already have destructuring for exactly this kind of thing, but for some reason the module destructuring has a subtly different syntax to the standard ES6 destructuring! Why?) Anyway, the whole thing could have desugared into pretty straightforward ES6 minus modules. No DSLs required. (Didn’t I express concerns about these recently on here? :\ ) What we have got now instead is a lot of module-specific syntax, encoding some people’s *static* hunches (static in more ways than one) about what will work best for the web, which will probably be irrelevant in 10 years time or so.
Sorry, that comes across a little negative. On a (mostly) more positive note: I’ve really enjoyed the ES6 articles and am disappointed to see them come to an end.
- Jason Orendorff
August 15th, 2015 at 08:42I wish I’d had your response while I was writing the article. It would have been a better article.
A “simple”, synchronous
require()was never in the cards for ES, because ES has to work in the browser. Loading a module may involve fetching code off the internet, which can’t be done synchronously. Something asynchronous, like therequire()function thatrequire.js provides, will be standardized. But that kind of API isn’t pretty to use in every single file you write. Node users were not going to migrate to an asynchronous API; they’d stick withrequire(). Newimportsyntax was a way—maybe the only way—to provide a standard module system that both client and server JS code would actually use.It’s unfair to call the static parts of the design “hunches”, because that ignores a decades-long history of programming languages with modules. Looking at systems designed 10 and 20 years ago, it looks like what’s in ES6 is stuff programmers need, and it’s hard to see those very basic needs being radically different in 10 years’ time. We’ll see how it plays out. I think the designers were influenced by Racket and maybe Python (both dynamic languages with good module systems). They also benefited from the experience of the JS community, particularly Yehuda Katz.
I really like this system. It’s great to use in practice, and a big improvement over what we’ve been doing.
I’m also impatient for the dynamic APIs to be standardized. I’ll poke the standard committee a little bit on this topic. While writing this, I realized it’s possible to standardize some of those APIs in the short term, while the details of the loading process (particularly user configuration and customization of loading) are still being worked out.
- voracity
August 16th, 2015 at 06:21“New import syntax was a way—maybe the only way—to provide a standard module system that both client and server JS code would actually use.” Ah, OK. Thanks. So a new syntax was used so that the syntax for importing could be agnostic between synchronous and asynchronous loading?
It doesn’t seem particular agnostic, though. As you mention below, a JS block with this import syntax has to block (and everything after it) until the import is finished. So it’s not possible to use this syntax on the client-side (as it stands) as PhistucK mentions. In fact, the syntax is pretty clearly not async friendly.
In any event, as per David Mulder, yield and async/await would be great solutions on the server-side, no? I’ve already been using yield to de-uglify database code in node.js. In fact, I’m starting to think that every JS block should be implicitly treated as async. (Maybe that’s too expensive?)
Sorry, “hunch” was a little heavy. It was in reference to the relatively new territory of modules for web-apps (in particular, network based module loading), which would require *anyone* to make guesses about what’s needed, not to modules in traditional software. One of those guesses is how the async story will play out. On reflection, I think this may be why it’s so hard to get HTML imports/web components/overlays or anything similar going — i.e. because nobody really has a good idea of what we need/want.
I suppose the syntax will come in handy, but it just doesn’t integrate conceptually in any way with anything else in JS. :) But I eagerly look forward to seeing the dynamic APIs. System.import (if that’s what we get) looks quite nice.
- Jason Orendorff
August 17th, 2015 at 13:42> So it’s not possible to use this syntax on the client-side (as it stands) as PhistucK mentions.
People are already using it on the client via compilation. You should try it out; a test drive might be more enlightening than further conversation here.
Future standards will make it possible without compilation.
> In fact, the syntax is pretty clearly not async friendly.
I have to disagree. The module system works very much like require.js in environments, like the browser, where all I/O is async. This is exactly what the CommonJS
require()function cannot do because it’s a synchronous function.
- Jason Orendorff
- voracity
- Jason Orendorff
- PhistucK
August 15th, 2015 at 07:32What if I mix modules and global code?
Do modules have access to developer-defined global variables and objects?For example –
// module.js
export function bar()
{
return foo;
}
// Regular external script
import * from “module.js”
var foo = 8;- PhistucK
August 15th, 2015 at 07:33More accurately –
// Regular external script
import { bar } from “module.js”
var foo = 8;
console.log(bar());Should it print 8?
- Jason Orendorff
August 15th, 2015 at 09:14Modules do have access to global variables, yes. Like a function’s scope, they’re nested inside the global scope.
But plain
<script>scripts can’t useimportsyntax. One reason is that a normal<script>runs synchronously and blocks the web page. If you think about it, withimport, each<script>might load not just a single file but a whole dependency tree. Definitely not what you want.This leaves the issue: modules can access globals easily, but how do global scripts access modules? One way is for the script to use anAPI provided by your module loader. That API is just like require.js, so this isn’t hard or unpleasant to do. It’s just not as nice as real
importsyntax.In the long term, you’ll be using modules for everything, so you’ll have
importeverywhere.- PhistucK
August 15th, 2015 at 12:23Huh… While you answered my question, you just made things less understandable…
So, what is the entry point to modules, if you cannot use it in a (script src=”stuff.js”) where stuff.js has “import { bar } from ‘baz.js'”?
Where does it start? Module loaders (what, require.js for example?) still load stuff using either XMLHttpRequest (and then eval, which cannot be used for modules, right?), or script elements (which you say cannot be used for modules as well).- Jason Orendorff
August 17th, 2015 at 01:02Sorry about that!
The entry point to modulesfor now is that all your modules just compile down to ES5 scripts. Then it’s simple. You just load them like any other script, using
<script src=...>.Once browsers get native support for modules, the entry point will be something like
<script type="module" src=...>.- PhistucK
August 17th, 2015 at 01:22Hm. I do not understand. Suppose browsers implement the ECMAScript 2015 version of modules. How will I be able to use modules?
Is script type=”module” standardized? If not, then this is not a way. What is the standard way of using modules in browsers, assuming browsers implement the entire ECMAScript 2015? - Jason Orendorff
August 17th, 2015 at 13:51> Is script type=”module” standardized?
No, not yet.
> What is the standard way of using modules in browsers, assuming browsers implement the entire ECMAScript 2015?
There isn’t one yet.
ES6 only specifies the JS language parts, not the HTML parts. Those will have to come later. In the meantime you can use compilation.
- PhistucK
- Jason Orendorff
- PhistucK
- PhistucK
- David Mulder
August 15th, 2015 at 09:03Already played around with modules in the past, but reading through this article I realized that I will probably stick to using `require` even once ES6 is generally available. That list of weaknesses (“Static vs. dynamic, or: rules and how to break them”) really hit home hard. I mean, not like I dislike static languages, but it doesn’t make any sense whatsoever in a language like Javascript. Why couldn’t an import simply return a promise and if you wanted the current behaviour you would simply write `await import x` (true that’s ES7, but that’s not going to take *that* long). I mean, the export side of things looks fine enough (don’t see why it had to be so strict, but don’t expect any real problems with that either), it’s just the import side which is crap. Something like ‘you can’t use import in a try catch’ should be enough reason to not use imports at all… :S Ach.
And that’s quite a waste@next article being the last already, but thinking back you did cover mostly everything there was to cover yes.
- Thodoris Greasidis
August 15th, 2015 at 09:41No comment about es6-module-loader
and System.import :(
There was a time that it was part of an ES6 draft.
http://www.2ality.com/2014/09/es6-modules-final.html- Jason Orendorff
August 17th, 2015 at 01:05I did link to the ongoing work on that (theLoader Standard).
It’s just not part of ES6, that’s all!
- Jason Orendorff
- Eduncle
August 17th, 2015 at 05:05thanks for this article about ES6. In your next article I am expecting some more details about code splitting & modules for which it is required.
- Vladimir Starkov
August 17th, 2015 at 05:48Can anybody explain will transpiling be required in the future, when browsers will support es modules natively?
- Šime Vidas
August 17th, 2015 at 10:39Can a module import both the default export and other named exports from another module, at the same time?
// e.g.
import { each, map, default as _ } from ‘lodash’;- Vladimir Starkov
August 17th, 2015 at 10:59You can do it like this:
import _, { each, map } from ‘lodash’;
- Jason Orendorff
August 17th, 2015 at 12:18Yes. Your example and Vladimir’s example are equivalent.
- Vladimir Starkov
- Rich Brown
August 18th, 2015 at 04:34Great article, and I have a follow-on question about ES6 modules:
I want to write a communication “module” that takes care of establishing a TLS connection, authentication to the other end, converting JSON into XML to send on the wire, tracking all kinds of internal state, etc, while exposing a pretty simple Open/Close/Set/Get API to the rest of the program.
I strongly feel the urge to break this “module” into separate (short) files so the TLS connection handling can live separate from the JSON XML code, etc.
What’s the best way to do this? Many thanks!
- Jury Xiong
August 18th, 2015 at 06:02I’ve translated this article into Chinese, I wish I have your permission.
- Cédric
August 19th, 2015 at 02:35Great article and great series, thank you!
Quick question.
AFAIK, we have to do:import * as Rx from ‘rx’;
Rx.Oservable.fromArray();What is the reason for not having?
import * from ‘rx’;
Observable.fromArray();We could do “import {Observable} from ‘rx'” but that is quickly painful when the module is exporting a lot of things…
- Jason Orendorff
August 21st, 2015 at 07:17This kind of
import *makes it so you can’t tell just by looking at the source code what names are imported.If you have two
import *declarations in the same file, there can be a naming conflict (both modules export the same name).If an
import *declaration imports something with the same name as a global, likePromise, it will simply shadow the global, and it’ll be unclear what’s going on.Worse yet, the imported names can changewithout any changes to your code. So if everything works out now, it can always break later if any of those imported modules ever add a new feature with the same name as something else in your module. (I really think adding features shouldn’t have to be a potentially breaking change!)
For all these reasons,
import *is somewhat frowned upon in languages that have it, likeHaskell andPython. Fortunately, in JS, we already (as a general rule) try to keep our namespaces tidy by using namespace objects likeRx. We have good habits. So althoughimport *was discussed, the designers decided it wasn’t needed.(Interestingly, Python’s style guide says “There is one defensible use case for a wildcard import, which is to republish an internal interface as part of a public API.” ES6 modules do support this, but it’s spelled differently:
export * from "blah";.)
- Jason Orendorff
- Caridy Patiño
August 20th, 2015 at 16:32Late to the party here, but for what It’s worth, some clarifications:
@voracity we are definitely looking into async/await for the import declarations. The problem with node in general is that top level await is problematic in many ways, and something will have to change I’m afraid, it is just the fact that the consumer of any module with a top level away will mess up the entire sync loading process in node (even if you stick to `require`). We will see.
@David Mulder, you cannot do a try/catch today with regular script tags unless you load and eval (which is subject to other restrictions) manually, nothing has changed in that regard. The fact that you can do try/catch in a systems like node (e.g.: try/catch a require call), does not help in a browser env. Keep in mind that you will be able to use the imperative API to have more control at the top level: `System.loader.import(‘foo’).catch(…)`.
@Thodoris Greasidis, `System.loader.import()` will be the imperative form to load and execute modules from a regular script (working on the spec for that as we speak).
@Vladimir Starkov, we will continue using transpilation for the foreseeable future, but lets not confuse transpilation with bundling. Transpilation from JS to JS will still be useful as a way to use new features of the language, extensions of the language on the user-land (a la JSX), optimizations (a la uglify), etc. On the other hand, bundling is just allowing us to load and execute various module formats as single scripts. That’s probably the part that will get reshaped once browsers ship modules and the loader, maybe focusing more on folding modules rather than bundling them into scripts.
@Cédric, that’s ambiguous, now the importer has to know two things, the module specifier (`rx`) and the identifier `Observable` which somehow have to be defined in the exporter module. This will be a refactor hazard as well.
- Christian Bankester
August 21st, 2015 at 09:36Class declarations are not hoisted, so your example above:
“`
export {detectCats, Kittydar};// no `export` keyword required here
function detectCats(canvas, options) { … }
class Kittydar { … }
“`wouldn’t work, right?
- Jason Orendorff
August 21st, 2015 at 10:01The class name is visible throughout the module—thename is hoisted to the top of the enclosing scope. But the actualcreation of the class isn’t hoisted. That only happens when the class declaration runs.
So the example will work, as long as we don’t somehow call
detectCats()before the class declaration runs.And if you import this module, this class declaration will run before your code, so it should be fine.
- Jason Orendorff
- Owen Densmore
August 21st, 2015 at 11:44I love modules. But there are problems.
1 – Interoperability w/ es5. When you use modules in babel, it produces dependencies. Drop this into the babel repl
import foo from ‘foo.js’
var lib = {}
export default lib2 – import exposes file locations for modules, unless you do a lot of webpack/jspm configuration. Yes, es5 has tag hell, but at least we understand it!
3 – Webpack/jspm: God, not MORE workflow!! Both attempt “bundling” but I’m not sure if that works.
4 – All this has lead my project-mates to avoid modules, just babel & standard es5 modules and tags galore. Hardly progress!
I’d love an article on just module loading.
- Filipe Silva
August 21st, 2015 at 12:01I’ve been converting a project to webpack+babel and I’ve found the module loading a godsend really. Paths being relative helped a lot keeping stuff modular and sharing them between projects.
- Jason Orendorff
August 21st, 2015 at 20:14I won’t deny that there are problems. We’re using the half of the system that’s done (the syntax). The other half (the standard dynamic loading API, plus loader customization) isn’t ready yet.
The fact that compilers, libraries, and tools built by the community have been able to pull everything together into a coherent system is actually really impressive, given the halfway state of things.
But let me take your issues point by point.
1 – I guessthe online Babel playground is configured to produce CommonJS modules. That’sconfigurable, though.
If you don’t want to configure it yourself, use webpack, and everything will Just Work. webpack tells Babel what kind of module format to output and automatically bundles it with the 20 or so lines of support code it needs.
2 – This still isn’t standardized, so right now, each ES6 module compiler can do whatever it wants to do.
The final standard could go either way. I guess I expect filenames to win at this point.
I want to import packages by package name, not filename. But almost everyone I’ve talked to about this finds filenames more intuitive. Andwithin a package, I have to admit filenames make a lot of sense.
However, if you insist on not having filenames, I can’t understand also not wanting to configure your loader. That mapping has to be generated somehow. Languages like Java and Python (and Node is this way too) can get away from searching umpteen different directories every time you want to import something. The web is not like that. When you decide to fetch a URL and wait for it to come in, it better be the right one.
3 – I know – but it worked for me, and I’m sold. If you reluctantly pick up one tool this year, make it webpack.
Larry Wall used to call laziness one of the three virtues of programming. But he warned against the vice of False Laziness. The truly lazy are unstinting in their efforts to eliminate work.
4 – Progress is slow because the standardization effort has been slow. That’s the root problem we have to fix.
- Owen Densmore
September 1st, 2015 at 10:55Thanks, Jason, for the clear, thoughtful reply.
- Owen Densmore
- Filipe Silva
- Brian De Sousa
August 28th, 2015 at 16:26Great article! I just came across this series for the first time… Defitnely going to have to go back and read the previous articles I’m the series. Thanks!
Comments are closed for this article.