Add InstantSearch and Autocomplete to your search experience in just 5 minutes
A good starting point for building a comprehensive search experience is a straightforward app template. When crafting your application’s ...
Senior Product Manager
A good starting point for building a comprehensive search experience is a straightforward app template. When crafting your application’s ...
Senior Product Manager
The inviting ecommerce website template that balances bright colors with plenty of white space. The stylized fonts for the headers ...
Search and Discovery writer
Imagine an online shopping experience designed to reflect your unique consumer needs and preferences — a digital world shaped completely around ...
Senior Digital Marketing Manager, SEO
Winter is here for those in the northern hemisphere, with thoughts drifting toward cozy blankets and mulled wine. But before ...
Sr. Developer Relations Engineer
What if there were a way to persuade shoppers who find your ecommerce site, ultimately making it to a product ...
Senior Digital Marketing Manager, SEO
This year a bunch of our engineers from our Sydney office attended GopherCon AU at University of Technology, Sydney, in ...
David Howden &
James Kozianski
Second only to personalization, conversational commerce has been a hot topic of conversation (pun intended) amongst retailers for the better ...
Principal, Klein4Retail
Algolia’s Recommend complements site search and discovery. As customers browse or search your site, dynamic recommendations encourage customers to ...
Frontend Engineer
Winter is coming, along with a bunch of houseguests. You want to replace your battered old sofa — after all, the ...
Search and Discovery writer
Search is a very complex problem Search is a complex problem that is hard to customize to a particular use ...
Co-founder & former CTO at Algolia
2%. That’s the average conversion rate for an online store. Unless you’re performing at Amazon’s promoted products ...
Senior Digital Marketing Manager, SEO
What’s a vector database? And how different is it than a regular-old traditional relational database? If you’re ...
Search and Discovery writer
How do you measure the success of a new feature? How do you test the impact? There are different ways ...
Senior Software Engineer
Algolia's advanced search capabilities pair seamlessly with iOS or Android Apps when using FlutterFlow. App development and search design ...
Sr. Developer Relations Engineer
In the midst of the Black Friday shopping frenzy, Algolia soared to new heights, setting new records and delivering an ...
Chief Executive Officer and Board Member at Algolia
When was your last online shopping trip, and how did it go? For consumers, it’s becoming arguably tougher to ...
Senior Digital Marketing Manager, SEO
Have you put your blood, sweat, and tears into perfecting your online store, only to see your conversion rates stuck ...
Senior Digital Marketing Manager, SEO
“Hello, how can I help you today?” This has to be the most tired, but nevertheless tried-and-true ...
Search and Discovery writer
My first foray into programming was writing Python on a Raspberry Pi to flicker some LED lights — it wasn’t much, but it taught me the basic guiding principles of software development. Right from the beginning, they were summarized in the Zen of Python:
Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Complex is better than complicated. Flat is better than nested. Sparse is better than dense. Readability counts. ...
These principles are so logical and sensible that they’ve started to guide the development of other languages. Now as a JavaScript developer, I can see how they’re reflected in standards like ES2015. Take the import
and export
statements for example: they’re simple, explicit, practical, and easy to explain. But why is that? Why not stick with the old require
? Why this syntax instead of something more powerful? Why are imports so important in JavaScript? Let’s tackle those questions in this article.
The first reason why imports are so important in JavaScript is because they allow us to split code into different modules and reuse it everywhere. It’s much simpler and easier to understand what’s going on when we organize our code this way.
Imagine if you wanted to use GraphQL in your Node.JS server, but the docs told you to copy and paste the GraphQL code directly into your main app.js
! Without a way of compartmentalizing that GraphQL code into its own separate place, it would be too impractical to even exist. Frankly, that logic applies to most of our modern web development tools — they’re large and clunky and require massive teams to create and maintain, so if they didn’t have their own space to work in, it wouldn’t be practical to build them at all. So next time you load in the newest tool or framework, you have the underlying hero import
to thank.
The import
statement and its associated syntax from ES2015 is also known for being much easier to read than its predecessor require()
. See for yourself:
import {tom} from './heartbreakers.js';
var {tom} = require('./heartbreakers.js')
Which of these seems more readable to you? Read them out loud — which sounds more like an English sentence? Import Tom from Heartbreakers JS
or Var Tom equals require Heartbreakers JS
?
We can still rename those imported variables just like if we had used require
:
import {singer as jeff} from './electricLightOrchestra.js';
This is still just as readable as before, while still respecting the name of the variable that the file we’re importing is sending us. If electricLightOrchestra.js
exports a variable called singer
, which doesn’t fit the general scheme that we’re going for with our variable names, we can just explicitly rename it to jeff
.
Sometimes a module only has one logical export. While we generally avoid this, it’s possible to just import the default export from that file. For example, if we had a JS file where we defined Bob Dylan, it wouldn’t really make sense for anybody else to be defined there, so we could just mark him as the default export and import him in our main file like this:
import './bobDylan.js';
Imports work exactly how you’d expect them to work.
It also matters that our code is easily readable by machines — if our build tool has a solid understanding of what files we’re importing and where, it can precompile all of the necessary code (and no more), shipping only the code we’ll actually use. For example:
import {george} from './beatles.js';
Our build tool will scan for this line, copy just the definition of george
into the current file, and ditch whatever else was in beatles.js
(presumably the definitions of john
, paul
, ringo
, and depending on your taste, otherGeorge
or brian
). Assuming those definitions are all the same size and you’re only canonizing four of the Beatles, we just cut the size of the code in this file to 25% of what it was. The bigger the file, the more you save by specifying which piece of it you actually need. This is called “tree-shaking”, since it’s basically the software version of shaking a fruit tree so the useless ones fall off.
That’s not to say that everything about the import
syntax is easy to read; a few compromises have been made in the name of practicality. For example, those brackets aren’t intuitive to everyone, and can seem a little arbitrary. But it lets us treat our module like an object, and the things it exports as the object’s properties. If we think about it that way, then the bracket syntax might become slightly more intuitive to those who already make heavy use of concepts like object destructuring, and it lets us imagine the module exports as if they already have names.
Another sacrifice made to be flexible is the *
. This is another thing we like to avoid because it defeats the whole point of tree-shaking, but you can do something like this:
import * as roy from './royOrbison.js';
All of the exports from ./royOrbison.js
will be bundled into one object called roy
. This is generally frowned upon because our build tool won’t know which of the exports from that file we’ll actually use — now it has to bundle all of them with our code just to be safe. Luckily, ./royOrbison.js
only exports the one object; we’ll just have to refer to it as roy.roy
now because that export was stuffed into an object also named roy
. That potentially confusing and misleading result is why we don’t use this much: it’s hard to explain, and if the implementation is hard to explain, it's a bad idea.
If we’ve given up on tree shaking, then we could also use dynamic imports. Our build tool definitely isn’t going to be able to tell what it needs to bundle with our code, so this is going to break that whole process, but on rare occasions it can be useful to do this:
// define the variable youWantToIncludeTheDrummer dynamically above
if (youWantToIncludeTheDrummer) {
const jim = await import('./sessionMusicians.js').jimKeltner;
// note the "await" - import() returns a promise
// do this either in an async function, or use the .then and .error functions
}
This is a nice-to-have when we need to import something dynamically, but it’s rarely useful enough to overcome the lack of tree-shaking. As a general rule of thumb, special cases aren't special enough to break the rules.
You might’ve noticed that our variable naming scheme is a little rudimentary — surely there are other george
s and bob
s in the world of music, right? You’re right that they exist (we had an otherGeorge
in the ./beatles.js
file above, for one example), but in advance we’ve determined that this particular file isn’t going to contain any duplicate names. We actually can form our lineup without any overlap here:
export {
george,
bob,
jeff,
roy.roy as roy, // remember, we imported roy using the weird asterisk syntax
tom,
jim // assuming we actually did import jim, this is the tricky part to dynamic imports
};
We never have to worry about Jeff Beck or Bob Seger muddying up our namespace because we never imported them in the first place. Our five or six Traveling Wilburys are isolated to this file with the names we gave them here, removing any possible confusing duplicate variable errors.
This might seem like a given when we’re talking about bands (which generally function as independent units), but when we’re talking about software functionality, the lines between the modules sometimes get blurry. It’s not uncommon for what once was a small encapsulated file with a single function to mushroom into a 4000-line mess just because all of those additions felt logical as we made them.
In that case, it’s important to go back and decide what really belongs in that file, what we can move into their own files (and namespaces), and what really needs to be imported and made available to the rest of our program. Keeping the file size down reduces the time spent adding new features, fixing old bugs, and optimizing our code for performance and maintainability because we can much more easily grasp the space we’re working in when the file is only a handful of related functions as opposed to a third of our company’s infrastructure.
While the Zen of Python isn’t a direct set of guidelines for JavaScript, it definitely lays out what good software architecture looks like, and the fact of the matter is, JavaScript’s modern import
and export
syntax checks all the boxes. It’s explicit, readable (for us and the computer), practical, unambiguous, and it encourages proper use of namespaces. It’s a win-win-win-win-win.
And if by chance, you’re looking for something fun and useful to import, check out Algolia’s JS client. Now is better than never
, so don’t hesitate to reach out on Twitter if you’ve got questions.
Technical Writer
Powered by Algolia Recommend