 Have you ever done this? Or seen this in your code? Do you use a tool like Webpack to handle these things and also bundle your code so you end up with one big giant JavaScript file? Is that really necessary? In this video, we'll explore the modules that are built into your web browser and don't use bundling. You'll learn how they work and about the trade-offs of using these ECMAScript modules versus other approaches. I think this is a great topic because I feel like a lot of people, me included, have been very confused about what is actually a module, what constitutes a module, and then there's been this evolution of modules that is just very confusing. And so I think it would be awesome if we can go back to the beginning to find what a module actually is and then sort of describe what the evolution has been so that we can level set on what actually is a module and what does it mean to us as developers? Totally. That's a great idea. Let's go ahead and do that. I mean, these were my four reasons why I think we have modules. So feel free to jump in or if anyone has comments, add it to the comments. The first is to make it possible to reuse your code. So if you have to copy and paste code between files that's not really reusable, you're not going to get things updated properly. So you want to be able to work in different files and then stitch them together in different ways. And then handling dependency resolution. So if you have one piece of code depending on another piece of code, something has to kind of decide where are these pieces and plug them together correctly. And so that's been the really important part for allowing us to minimize the amount of code that is bundled. If we are bundling, they're using that dependency resolution to help the bundlers decide which code to include and which code not to include. That's right. And the other actually wasn't that long ago that we were working on a problem together and there was a situation like that. And it turned out there was two copies of a module getting loaded through some mysterious means, which eventually figured out. But yes, and all of a sudden that's certainly extra stuff for the browser to deal with for no benefit. And another reason is to isolate the code. So we saw this in the little code that I put at the beginning, which is just by default in a browser, if you just say var foo, you are putting a variable called foo into the window object, which is the global object. If you're a node, it's the global object. But it's kind of like, globals are generally bad if people are not aware. It's not a religious thing. It's just because other people could come along and use the same name again, and things will break in very mysterious and bizarre ways. If you start overwriting somebody else's variable, or they overwrite yours. And then finally, as I mentioned, sort of before around the dependency resolution is just managing multiple files. So that, you know, if you have a bunch of people working on a project, it's bigger than one person, you're going to want to be able to edit these files and check them in and merge them later, independently of each other. So yeah, this is something that's been happening for a long time. And so I tried to sort of make a little history chart here. And it kind of started with what was called the revealing module pattern, which is some of the code that I showed at the beginning is sort of approaching that revealing module pattern. So it was like so many things in JavaScript. It was a language that was developed very hastily. And, you know, it was missing some features. And this was one. And so people found a way to work around it. That led at the same time to something called CommonJS, which is the server side was originally intended for NodeJS, where your JavaScript is running on a server or your desktop computer doing development or something. And CommonJS led to a set of notation for importing and exporting things. And, and, you know, and it was synchronous, because you could do that on a server, because file access is pretty fast on a server, chances are the files get cached in memory very soon. And next thing you know, you're able to call these things in at runtime, it loads it and it's very efficient. But that didn't work very well on the browser, where if it had to go fetch the file off the internet, it you kind of wanted it to be asynchronous, because as most viewers probably know it, your whole browser will freeze if you do synchronous network calls, because JavaScript only has one thread really for rendering stuff. So that led to AMD, the asynchronous model definition and require JS, which handled that. But it was sort of an extra pain in programming, I think, to manage those things. And so if you see examples of code, maybe older, but maybe newer, where it, for the browser, but it uses the term require, like require something that's using that kind of pattern or that CommonJS async module definition require JS, it's one of those types of things where it's saying, I require this module. And usually it's like var something require equals require, and whatever the module is. And you're getting bringing something in. And then that sort of led to bundling. And so there were other advantages to bundling, we're going to get into this a little bit more later. But we had tools like Webpack and Browserify that would take modules in lots of little files with imports and exports in them, and stitch them together during a bundling step. So typically you're kind of almost like you're compiling your code. Maybe you are if you have TypeScript, and you're actually doing a language translation. But you might be also minifying and obfuscating the code and doing other things, but you build this giant file. And then, of course, during debugging, you end up using other tools to sort of take the file back apart so you can understand what you're doing. Right, like maps and stuff. Maps and stuff like that, right? Yeah. So this new kind of kit on the block is ECMAScript modules. I guess they're not super new, but I think they're new to a lot of people, which is why we're doing the video. And yeah, they basically are handled completely in the browser. So you get some of the advantages of these different approaches spread across them. Right. And that notation is the import and export notation. And sometimes a lot of the samples that you'll find online mix the two. You'll see import statements in the code, and then you'll see some require statements in the code. And it's essentially mixing the two different things together. And it could be very confusing if you don't understand the background or the basis for where they're coming from. Right, exactly. So let's go ahead and take a look at a sample. And we'll start with the HTML. And it's pretty simple stuff. We're going to actually write a poem here by stitching together different modules that have different lines of the poem. So you can see down at the bottom is a script tag. And this one is a little different because it says type equals module. So that's telling the browser, don't just load the script in the way that scripts have worked since time immemorial. And load it as a module, which means that the browser is going to do isolating the namespace for us. So we don't need to do that at all. And you could do that, you know, and not really use the rest of the system as long as the script is pretty much self contained. So now here in index.js is where I'm going to bring all these different modules together. And so let me and then you'll see the poem come out at the end. So let's kind of look at these one at a time. The first scenario is multiple exports. So we're going to export multiple things from here and give them each a name. So it turns out you can, you were talking about the confusion of classes and things, right? It's really the symbol or the, I don't want to use the word variable because it could be more than a right, but it's a symbolic language, the symbol that you're exporting and making available in to be imported into other modules. So it could be a function name, a class name, a variable name. There's probably other like almost any symbol that's there right in the language. So the first one I'm going to do is I'm going to export a variable, which is just the first line of the program, which is the first line of the poem. And then the second one is a function that returns the second line of the poem, right? So when we import those, notice that there's squigglies. So it's going to be import line. And that means it's going to go after that by name. And it's also going to be called line inside of index.js. Whereas the second one, just to sort of show a lot of variety, I'm going to, I'm going to import it twice once with a different name. So the get line function, I'll import that as my line. And then I'll also import it as get line. So I could use either of those to access that function. And then later on down here, I'm going to go ahead and start building up the concatenation of the lines and calling those, you know, grabbing that variable and that function. Yeah, very clever. Yeah. So very, very simple. But this is sort of one of the import-export approaches is to give everything a name. And you'll see that in, especially if you bring in a big library that's in a module. The second one is going to use something called a default export. So the idea here is that this module is kind of completely sealed up. It's not exposing any of its symbols or names, but it is going to export one default thing. And in this case, that is a class. Right. So it just happens to be a class, could be anything. And it's called the line provider service. And that service implements one function called get line, which gives us the next line. So if we look at that, you'll see it's going to import it. We say import, and then we give it a name, which could be anything, any valid name. It doesn't have to match the one in module two, because what we're doing is we're just saying, hey, whatever the default export is for module two, use that. Yeah. And you can mix these. You could have a module that exports one, or you can only have one default, but you could have a default and a couple of named ones. And your import statement ends up looking a little bit funkier, where you would have like line service case, comma, and then some squiggles with some more names in it. But you can mix and match them. Yeah. It's one default per file. Per file. Right. And it's also one module per file, which is worth mentioning if we have some C sharp developers and where they have partial classes. Other some languages have the idea of a partial class. That would be kind of nice. But you know what, we were happy to just get more than one file at all here in the JavaScript land. We're just, wow, I can have two files. Wow, that's so nice. So you know, whatever. And so we can new up that class and go ahead and grab that wine. And then the last one, I just kind of wanted to show it as a pattern, I guess. I love this pattern. It's not really another one. But yeah, so this is something I think you talked about is the idea of, it was in the React discussion, I think you had with Andrew, the frameworks discussion that you had with Andrew. And you're talking about that you don't have to maintain as much state in your React apps. Because I do a lot of work in a service. In a service. Right. And so what you end up wanting to do is if you've got state that relates to whether or not somebody's done a certain drop down, made a selection on a drop down, then of course that belongs in the UI. But if you've got state of have I connected to some service yet, or have we finished authentication, or have we, you know, is there, maybe you're caching some information, so you don't have to go back to the service each time. That stuff probably shouldn't be in your UI. It's just going to really complicate your UI. And the pattern for updating it is going to be really different than it is for the service versus the UI. 100%. So this is the pattern for that, as you said, is to make a singleton, which is just, it's like the simplest design pattern. If a point is the simplest shape, it's like zero dimensions. This is like that zero dimensional design pattern. It just singleton is fancy word, because as computer people, we like fancy words because we impress people. It's really, it's really all BS, but boy, it's impressive. Anyways, no, it's not BS. It's important for people to understand there's going to be one instance of this class, period, no matter how many other modules consume this thing, they're all going to get the same instance so that it can have state inside of it that is on behalf of the whole application. And it's really simple. I just made a class, didn't export the class. I just, I said export default new. Right. I could have also said export const variable name equals new, if I wanted to give it a name. But I said export default new, and the new is now going to run inline. It's going to instantiate that class the first time that this module gets pulled in, which by the way, the browser is doing all this when you do the import statement. It's very efficiently looking in its cache and looking to see if some other modules already loaded this one and all that stuff is happening. And you're going to get the same new, that one same instance of the object. So the first time it runs, it makes a new object and then it just, anybody who imports is going to import that same object. Exactly. And oh yeah, there's a result, right, which is, well, let me go back and show how that, we're just taking that. So line service singleton is already there, right? It's, we just did an import of it. Yep. And it was the default. And then we just use it. We don't have to new it up because it was already newed up in the module. Yeah, cleaner too. Of course, you know, proving that Lewis Carroll even sometimes used semicolons at the end of his lines. Here is the, here's the poem. So, right. Sure, everyone's seen it once or twice. So anyway, that's, that's the deal on how to use these modules. I love it. Okay. So this is awesome, but I think there's still maybe an argument for bundling. So what, where, where are we left with bundling? So yeah, so bundling, there's kind of a trade-off of bundling and not bundling. Bundling fundamentally solves this, this initial big problem that it solves is called the head of line blocking problem. So the idea here is that originally browsers could only load four to six files at a time. And so if you had a large number of modules coming down, you would sit there and wait for, you know, you might start four of them, the browser might start loading the first four. And it's not just modules to its images and CSS files and all kinds of other stuff. And it would, it would say, okay, I'm downloading the maximum number now because I only have so many threads. And then it would wait until one of them had finished loading to start the next one. So there's this latency of the internet, which is kind of the slowest part in a lot of cases, would get added throughout the process. So one way of handling that is to just bundle everything. So you have one giant file. So it's really good for efficient download. It's also nice that it allows preloading the modules. So you can kind of anticipate these are the modules I'm going to need and put them all in the bundle. And they're already going to be there when you need them, which is great. The downside is, well, this isn't necessarily efficient. What if I didn't use all the modules? What if I have certain modules that are only used in this one rare use case that somebody goes and changes the settings on the app, let's say, and then they do that once every six months, and then all of a sudden it has to bring in more stuff? Why am I loading that every single time they open the app? Especially since a lot of times there's an 80-20 rule and the 80% of the code is handling those 20% cases. And then the other thing is just caching. If I had a copy of, say, React or some other library, and I was going from site to site, that cash copy of React doesn't do me any good if everybody has bundled it into their app and I'm loading it again and again and again. So that's kind of the trade-off. And I think in practice on a big production app, there would probably be a whole bundling strategy where you... Yeah, I was just going to say there's middle ground. We're talking about both ends of that spectrum. And then there's that middle ground where you have vendor packages, maybe differently, and you separate some of your code out that isn't used a lot and you'll lazy load it after or asynchronously load it later or whatever it might be. But yeah. So there's a whole set of strategies in between, but it's still valid, right? Right. And one thing that really helps a lot is HTTP 2.0. When the internet first came out or for a surprisingly long time after this, everything was very simplistic that it would... The browser, first of all, can only download so many things at a time. Secondly, it would make a brand new connection every time. So as we've moved into a world where we can't trust HTTP and we really want HTTPS for everything, it's going through this multiple handshake to reach out, create the connection, negotiate the cryptographic key and all that kind of fun stuff for every single file. So if you had too many files, it would really kill you. There's this fun little demo that we'll put the link in the comments that actually shows the advantages of HTTP 2.0. So here on the left, it took 1.4 seconds to render this image. Obviously, you could see was actually a checker board of little baby images. It took 1.4 seconds to do that. And then it was like a third of the time to do that with HTTP 2.0. So what HTTP 2.0 can do, the browser can request more than one file. So the browser might look at your HTML and say, oh, there's five scripts and three images and a CSS file and a partridge and a paratree. And it will request all that stuff at once and it'll all come down. However, it doesn't completely solve the problem because if one of those modules references a second module, then it can't request the second module until it's at least read the first one, right? So there's going to tend to be these chains. And there's a solution to that in HTTP 2.0, which is called HTTP push. And to do that, you have to program the server and say, hey, look, when somebody requests this, give them all this other stuff because I know they're going to need it. And that requires changing the web server and not just updating your code, which to me kind of means I'm not sure if that's ever really going to happen. Maybe there'll be a standard for it, or maybe people will find a way to make it really easy, but right now it's not necessarily. Yeah, and you also have to have full control of the chain that you have to control your own web server. And in certain scenarios, especially in our ecosystem, we don't necessarily always control that whole chain depends how your app is being built, but still. So it's a consideration. That's absolutely right. And in fact, the sample site that we're using is static web pages on GitHub. So I mean, it's didn't cost anything. So I guess beggars can't be choosers, but no HTTP push for us. Can't demo that one. Awesome. Well, this is really good and insightful. So I am really glad we covered this because I think a lot of people will be excited, you know, happy to have that that knowledge. I think it's it's really useful. So did you like this episode of Browser Native? We hope you did. It was a really great one covering all the module, ES modules and their history. If you liked it, please give us a like and subscribe to this channel so that you can be notified whenever there is a new episode Browser is our neighborhood. And so thank you for joining us today. See you next time.