The full video and many others like it are all available as part of our Frontend Masters subscription.

Kent C Dodds

Kent C. Dodds works at PayPal as a full stack JavaScript engineer. He host JavaScript Air and co-hosts React30 podcasts. Kent is an instructor and Google Developer Expert.

Kent C Dodds


In this exercise, you will use the ES6 System.import API to implement code splitting. This new API will lazy-load code only when it’s needed in the application rather than during application startup. Kent gives a few hints and then lets the audience work on the exercise. Note: this exercise begins on the FEM/05.0-code-splitting branch

Get Unlimited Access Now

Kent C Dodds: Let's just quickly take a look at the code, and then I will have an exercise for you to do, code splitting yourself. So, what we have now are a couple of things. We now have a CSS file for that. SVG, we have kind of restructured things in our app a little bit.

So now, on load, we'll call this update to do, so I moved most of the to do code into the todo.js file, and it also will add an event listener on the tall graph button. That listens to click, so that it will toggle your graph on and off.

And then that toggle graph is new, whoops. If we go to graph index, and this is where I say, again, that it's important to understand how things are resolved. With a comma JS, or ES6 imports as well, at least, the way that it's resolved with Webpack is the same as comma JS.

So, it will resolve. If you point to a directory, it will resolve to the index JS of that directory, or if it has a packaged JSON, it will resolve to the main property of that package JSON, what? That's how actually you know modules work most of the time.

But yeah, so here in this graph, we have this rendered graph. Or, sorry, we have this toggle graph function that will take care of a whole bunch of stuff that I don't even want to talk about. Because it's a little bit on the hacked together side. Just to show you how to use this stuff.

But then there's this render graph function. So far, we haven't seen any D3 or React or anything. But it's the render graph function that brings in all the heavy stuff. So, this is the stuff that we want to load asynchronously. And here, we have our JSX that everybody loves so much.

How many people love JSX? Yeah, I love JSX.
Kent C Dodds: Okay, now, it's great. Okay, so, this is pretty much, this file, if we could lazy load this file, then we lazy load all of the other stuff as well. Because nothing else in our entire app depends on these things.

And so, this would be, another really common case was, as if you have a router, and you can lazy load the different parts, or the components for each route. And so, once the user gets there, then load the stuff and show the user. And so, this is, in this application, this is the one file that we want to have is lazy loaded.

And if we look at this, there's only one place where this file is actually being used, and it is in this graph/index. So, what we need to do, I'm going to give you some sudo code, is ultimately, we need to get rid of render graph. But we still need to be able to call render graph in these three places that it's being called.

But we're going to call it a little bit differently, we're going to say, let me just look at what I called it in my notes, sorry. Yeah, I call it loadAndRenderGraph. And in here, you're going to use the system API. So, I'm going to just show you down here.

The way that you lazy load code, and this is this is actually ECMAScript standard. So, there's a new system API in ES6 where you can say system.import. And this is how you lazy load stuff, as far as the standard is concerned, and Webpack just understands the standard. And so, we're going to take the thing you want imported, so import what you want to import, whatever it is, if it's relative, whatever, and what this does is return a promise, how many people are familiar with promises?

ES6 promises. Okay, so you can say var promise equals that, or you can, and then you can say do stuff on promise.then whatever. I just normally do it like this, just right off of the promise itself. And then you're going to get your, the thing, whatever you would get it from a normal import, that's what you're going to get here, except it's going to behave more like a common JS import, unfortunately.

I kind of wish that it didn't. So, what I want you to do, as you're debugging, I would recommend that you console about log(thing) right here, and pull that up in your browser and see what that looks like. Pro tip, you're probably going to want thing.default. Because it's the default export, I can explain that a little bit later after the exercise.

But that's probably what you're going to want, that's just a hint. And then you can use that thing to do whatever you were doing before. So, basically copy this and put it down here. Pretty much, so yeah, that's the exercise. And then you would replace, in here, you would replace all of the render graphs with load and render graph.

Does that make sense?
Kent C Dodds: Okay, let's see how things go, I am available for questions.
Speaker 2: Did you cover that export default question?.
Kent C Dodds: Yeah.
Kent C Dodds: Yeah, so Henry is asking, feel free to continue working on the workshop, but right here, Henry is asking why is this all the way up here, shouldn't that be the last thing?

And that's a good point that could be the last thing. I'm leveraging a feature of JavaScript called function declaration hoisting. So, if you're familiar with hoisting of variables, it's this, really, sometimes confusing and frustrating feature, which is actually solved by a let and const, so I can say var foo, and I can actually reference for you up here.

And that totally works, and it's really kind of one of the confusing parts of JavaScript. There is one exception to this and that functions. So, actually, sorry, I didn't explain that fully, so var foo equals bar. So I can reference foo up here, but it doesn't actually equal bar until this line hits.

And what actually, what JavaScript is doing under the hood, it's hoisting all of your declarations to the top, and then making the assignments where you make them. So yeah, that's one of the confusing parts of JavaScript. With function declarations though, not only is the declaration hoisted, but also the definition, whatever you're assigning it to, or what its value is.

And so, because of that, I can export the function itself, just like this above where it's being used. I like to do that because I like to keep all my imports and exports toward the top of the file, so that when I open up the file, I see everything that I need to see for that file.

And I don't have to search around the file. One thing that you'll see in lots of code bases is a bunch of named exports throughout the file base right next to functions and stuff. What that results in is you having to traverse the file to figure out what does this module export?

Whereas, if you put everything, actually a good example of this would be helpers. Here I have my exports all right up here at the top. I could just as easily say export and export and so on and so forth, both are valid. But only one of those can I see everything right at the top.

So anyway, that's kind of a long story. Either way, you can do it at the top or bottom. Just make try to make it consistent, and only have one place, and I'll be happy. Yeah, you had a question or comment?
Speaker 3: No, just stretched.
Kent C Dodds: I have one too.

Cool, awesome. And Martin is asking is this Webpack too exclusive? I only know require ensure. Yes, very good point, so, you could with Webpack one, I don't believe that Webpack one will ever get System.import, so if you're on a Webpack one, you can say require. UPS require ensure.

And actually, I don't even remember this API, because I never successfully used it. But you can look up in the docs for require ensure. One drawback for using require ensure is yes, it does basically the same thing as System.import, but there are actually two drawbacks. One, require ensure is not ECMA standard, and so, it's never going to end up in browsers.

It does have a standard, but that standard doesn't have a way to handle if there's a failure loading the module. Whereas, with System.import, because it uses promises, that actually just comes naturally, because then you can just say function onError and do stuff there. Or if you want to .catch, which would actually catch any errors here.

So yeah, and so anybody get it working?
Kent C Dodds: Okay, you can continue working on it.
Kent C Dodds: And actually, Tobias said, I'm pretty sure he basically said, if you use only one feature of Webpack, make it code splitting. He really likes code splitting a lot. That's why he created it.

Kent C Dodds: Yes, so Scott is asking, so, we have a default export right here, is it possible for us to also have like named exports as well? Yes, that's totally possible, and this is kind of weird, because I'm exporting toggleGraph as default and as a named export. Normally wouldn't do that, but yeah, the way that you would use that, if I wanted to import it, I would say import toggle, whoops, toggleGraph from render, and I could, incidentally, I could import the toggleGraph as well.

Although, that would totally fail because of the same name, but I could do as foo, and then that would work. But this kind of contracted example. So yeah, you can totally do default and named exports. Just like you would with common JS, but most of the time you're going to do one or the other, not both.

And the best use case for named exports is a utility function, our helpers, and pretty much everything else is probably a default export. Makes it easier that way.
Speaker 3: I do have a question.
Kent C Dodds: Yeah.
Speaker 3: Where does system come from?
Kent C Dodds: System will be a global as soon as it's implemented in browsers.

Speaker 3: Okay, but currently-
Kent C Dodds: Currently it's Webpack. So Webpack will transpile it.
Speaker 3: Okay, but is it part of ES2016?
Kent C Dodds: Yeah, 2015, yeah.
Speaker 3: It is?
Kent C Dodds: Technically, yeah, so, this is kind of the funny part of the whole ES6 thing. So you probably heard that Safari announced that they're totally 100% ES6 compliant, which is really exciting, except they can't load modules yet.

And so, yes, they do have implementation for the syntax. I'm not sure how valuable that is though, because there's no specification for loading modules. And so, there is a specification for System.import, but no browser is capable of actually doing anything with that, because part of the question, that's still kind of in the air is what does the browser do when it sees this?

And how does it resolve that? Because that's a relative path to probably a file that doesn't have an extension, right? So, you would have to write it like this, or you would have to be an absolute URL to a specific file somewhere that's on on your file system.

But if you wanted to do this with no module, for example, like react, what does the browser do to that? Well, like in node, it is going to do its resolve stuff and find the known modules and stuff. The browser can't do that, because it doesn't see your file system.

And so, it's just going to go make a request for react, and nothing's going to come back. So, there's still a bit of a question about whether how to handle these module loading, and hopefully it, in some way, resembles common JS, and that somehow smart people figure out how to do that, because it's really convenient.

Speaker 3: I'm not finding it in the specification.
Kent C Dodds: Okay, let's go.
Speaker 3: We can do this offline.
Kent C Dodds: Yeah, yeah, so, if you go to ES6 features, there is a module loader specification. Let me see if there's a link here, no. System.import is six spec. Yeah, so, this, actually, this is a fantastic resource.

If you go to, all one word, no capitals, that will take you to a repo that has all the features in ES6. I believe tail calls was actually ripped out of the ES6 spec last minute. But yeah, that this includes examples for everything. It's awesome. But yeah there's, yeah, we can look at this later.

But yeah, it was originally in the specification. I believe it's still in there.

Ready to take your code to the next level?

Intense courses with world-class teachers and unlimited access to our growing library of videos for the great price of $39 per month.

Get Unlimited Access Now