Modularity in web application code has been a topic of much discussion for a long, long time. Additionally, implementing solutions that provide a useful approach have consumed many development hours that quite a few of us will never get back. Thankfully, we are mostly converging on a single solution now in the form of ES6 modules.
In this talk, we’ll explore the practical aspects of both consuming and creating those ES6 modules. We will also take a look at other current widely used module patterns, and also those that are quietly fading away. Finally, we will investigate the composition of web application code in general and how that relates to the code we ship to production.
(nerdy electronic music) – So topic today is Modules In Motion.
We’ve been grappling with this problem for some time. From basically including different script tags, which is a fairly poor approach to trying to implement different patents which look at different things.
As John mentioned I’m an app developer.
I’d probably be known as a fairly short attention span. Web ITC was super cool.
I was doing that for two years and then it stopped progressing as quickly as I’d liked so I was like, “Oh this is getting boring.” Work at Canver at the moment.
Fantastic place to work.
This presentation will contain trace amounts of bias because I still love Browserify.
But the focus of the talk is definitely ES6 modules. I actually personally never moved to Webpack. People using Webpack in the audience, just a show of hands? Yeah so it’s a very popular tool and it works actually really really well.
So we’re using it at work, very common.
I can’t not have trans-polation and have types. And you’re hearing great types talk after this. We’re gonna go through a little bit of a history of what we had in modularity.
We’re gonna have a look at ES6 modules, what they look like and how they look in CommonJS. Are people familiar with CommonJS as well? Yep.
Okay maybe not, I don’t know.
We’re gonna talk a little bit, it’s not kind of directly related to ES6 modules but there’s a concept called tree shaking and we’ll look into that a little bit.
And then we’ll look into the future.
So if I was to define my own definition for what I want a module to be, I want it to implement functionality and keep it contained.
I don’t want it leaking things all over the place. We all know that when we were doing web dev back in the day and something leaked into the global space, all hell broke loose. Then also obviously when I’ve got a module there’s useful stuff in there.
I wanna be able to export that stuff so it can be consumed.
As I put here, extra points for interoperability. One of the big problems we’ve had with module systems is typically they like to only talk to themselves. They’re quite, I can’t remember the word.
And also to be quite well tested.
Why do I care? I guess I find myself siting in the place where I find I’m using all three roles.
I actually build modules, like John eluded to I do a lot of Web RTC stuff and that was all open source.
I like to use other people’s modules.
I don’t believe in reinventing the wheel.
I find that to be a very arduous and boring task. And I like to build applications that have sane structure. Like having an application that is all over the place is absolute pain.
So let’s go with a little bit of history.
I made this term up, maybe it’s a real thing, but this is a little bit of context.
We do obviously code reviews at Canver, or code submission-like challenges when people are signing up for jobs.
And we kind of still see all the patents coming through. And this is one that you still see where people will still make this object and add methods to it and make this thing.
Honestly, it’s not something I don’t think anyone should be doing anymore.
I’m sorry if you’re still doing it but I think you should move on.
Then we began to get immediately invoked function. And this guys designed not to fill in the slide because it is quite large.
Now the fact is that everything’s using this. This is common when Webpack’s basically bundling everything up, it’s all bundling to an IIFE.
And this is an excellent way of making sure nothing leaks into the global scope.
Interestingly something I notice from when, I’ve never been a Ruby person but when I was grappling with the fact that I didn’t want this and I also didn’t want to concatenate all these files together because I found that to be a terribly ugly thing.
Hands up if you’re still using a ComCat.
Oh sorry! Are you okay with that or you? No.
Okay not by choice, that’s good.
So there was this syntax that I’d seen in Ruby with a pipeline thing called Sprockets and I felt that that was a definite improvement where it basically would say, “Oh look if there’s this syntax or comment syntax, “slash slash equals, it basically says to the processor “move through and just include that file inline.” And I felt that was a good improvement.
I was gonna downplay AMD in a pretty significant way but in having a beer with Marcos last night, he was like, what is it? “AMD is back man!” And he’s pretty much right.
And we’ll see why a little bit later on.
But AMD is a very simple system.
And where it defines a module, it says what its requirements are and then it brings these requirements in.
Actually it’s very easy to read.
The only thing I don’t like about it is I don’t like that overhead of writing a module. So that’s a bit lazy on my part but in terms of how it executes in terms of a web way, it’s actually very good. So this is one patent that would avoid you having to have a trans-polar apart from any lovely stuff you’d like to use coming down the pipe. CommonJS, this is my preferred if I’m doing my own projects. And I’ve thrown a little bit of ES6 destructuring in there.
The reason I like this is it all reads quite lineally which is like the A synch in White Coat. You can read through and you can kinda go, “Oh I’m grocking this.
“As I’m reading through this I know what’s happening. “I’m requiring these modules inline “and then they get brought in.” And I’ve noticed that I’ve said inline there, that’ll become important later.
So without spending too much time on the history let’s go to the ES6 Import Syntax.
Now, this is legitimately straight from MDN. If you do a search on ES6 module syntax on Google, who can tell me what the first result’s probably gonna, what side it’s gonna come from? Yeah? W3 schools.
(laughter) Please Google demote that site.
(brief applause and laughter) It’s terr–.
– [Audience Member] Also stop using our trademark. (laughter) – Yeah it’s true.
In Duckduckgo, I don’t use Duckduckgo all the time but they actually get it right.
They have MDM out first.
So you can see all the variations of the import statement. And there’s quite a lot.
You’ll probably find that you settle on a few patents. I find that one of the most readable is kind of the selective import.
So like import member from module name.
And then you can do multiple and then you can give them alias as well.
I think that from a coding perspective feels quite nice. Very readable and as you can see, from what I like to do, once we had de-structuring, I felt like I could use that in a very similar way to what I do in CommonJS.
I have no idea why anyone would do this.
I’ve done it when I’ve been writing node modules but from a perspective of doing something sane I can’t think why you should just import a module without requiring something from it. It’s basically just saying, “Please can I have side effects?” And side effects are bad.
It should just be import side effects please. (laughter) The export syntax is a bit like, there’s not as many options which is quite nice. But it can get a little bit complicated.
So export basically the names, very similar again. Default exports.
So this is saying when there’s a module and you want to basically say this one only exports one thing.
Or you can see there’s another case down the bottom where there is export specific names default and then export other things.
And one that I didn’t find to be documented that well was when you actually wanna pass through an import as export.
The first three are well documented on MDN but there is a case where you want to re-alias something, like you wanna collect it up.
So there’s examples that I’ll have later on where basically I’ve got an API and I export three methods from that API, like a customer API, and I want to pass them through from the API module and say that they’re called customer.
So you go customer.getcustomerdata.
Those top forms don’t support that.
At least I couldn’t make them go.
So just a quick little analysis of ES6 verses CommonJS. Import all the things.
Lodash is getting quite a few mentions today which is nice.
It’s a good library.
Kudos to Underscore for coming up with it first. (audience laughs) It did.
But what we can see here is this is basically saying, “I want the underscore, I want everything in the kit, “just give me everything that’s there.” This is import some of the things which is starting to be the way we tend to go with our coding.
And I think it feels quite natural.
It expresses our intent probably more closely. Like when we’re basically going through and writing code, we’re writing a module, if we’re writing something and so we’re expressing what it is that module specifically depends on. That’s that format and then, which is probably not as well known is that you can actually reach in if there’s a specific file in the file structure and require it with a file path.
The CommonJS, I actually did a Common JS version a lot and it’ll become interesting as to why that’s actually useful.
I’ll try to flick back to this soon.
Hands up, who’s heard of tree shaking? Hands up, who’s looked into how effective tree shaking is? Okay.
So there’s lots of different systems now bringing tree shaking to the table.
Originally the first one and the original is pretty much Closure Compiler.
We use that at work and it does a bang out job. Rollup has surfaced it in terms of a more popular context. And then Webpack has followed suit with tree shaking in its thing.
Now tree shaking for those who don’t know is a very simple kind of concept and basically says, “When I’ve got stuff in my application “or in my sort of pathways of modules that I’ve got, “if I don’t use it, please throw it away.” So it’s very simple to understand at a high level. Whether it delivers on that promise or not is another different thing.
So just comparing these two formats that we saw before, would your impression be that these two things, like a tree shaking kind of thing mean the same thing? Yes, yes, okay.
That was my impression too.
Not so much.
So if you work through and this is a cross, these results are not exactly the same numbers but that’s consistent with Rollup and Webpack. If you basically do the selective import from a module, you will not have a reduction in size, not a great reduction.
If you reach indirectly and pull it out, you will see that. So that’s a little disappointing and I think for me that debunks a little bit of a promise of what tree shaking’s offering now and I think there’s still a bit more work to be done. There’s the Lodash ES modules which are basically the constructed ES6 variants of the CommonJS stuff that’s there by default. So I thought, “Oh this’ll be different, “it can walk these paths much better.” No I’m sorry.
I think it’s actually bigger, yeah.
So start to think if I’m depending on Lodash I have to be careful.
Surely it’s okay if I’m using my own modules. And for the most part that’s actually really right. So the Case One, get customer data, update customer data from my fake customer API, which there’s a delete customer data method in as well, function rather.
Then both Webpack and Rollup shake that out, no problem at all.
In the output you can’t see any of it.
The size is affected, it’s fantastic.
Case Two where you pass that through in that passthrough construct that I mentioned before where is main says I’m gonna import the customers from the API and the API says for all of the functions in customers I want to export them as customers.
Only Rollup actually successfully removes the delete customer implementation which is a little sad. So once again, just take all of this tree shaking stuff with a grain of salt.
One of the things that’s a common question in the land of node is thinking about when can we have ES6 modules? And the answer is a lot longer, you have to wait longer than you like.
Now the reason for that is because there’s a pretty significant difference between the way node module resolution works and ES6 module resolution works.
Apologise, I’ve got my back to you guys all the time. So they’re very contrived examples.
And honestly it’s more like side effect city than to demonstrate how this behaviour works, you shouldn’t do it anyway.
But if you’ve got these two modules, main, which is saying, “Okay here’s at the start, “do a log.
“Import the logger and then after the “log’s imported say hi.” But the logger will also have a little side effect thing where it does logger initialised. So the output that you would see in Common JS is this starting main logger initialised hi and the reason that is is because in CommonJS it actually goes through and imports everything as it sees it.
So as things come through and notice the module it basically evaluates that inline and then does the thing.
In ES6 it does a static analysis of the module. So it walks through and says, “Okay, look at this. “I can see all the modules but now “that I’ve resolved the modules, “I’ll walk through the execution.
And then for that one, because the logger is the last chain in the execution, that is what’s initialised first.
Like I said, this is very very contrived and there’s no good case for you doing this. So if you’re following the patterns where you’re basically always exporting something and doing the correct thing, you should not be bitten by this.
So as much as there’s people talking about it and it won’t land in node anytime soon, you should be pretty safe if you’re doing smart things. So what does the future hold? Actually just added the Stage Three to this slide ’cause everyone’s talking about stages and I think it’s important.
This is at Stage Three? Yeah cool.
So this is the dynamic import.
So one of things that you can do with CommonJS in node is you can basically say, “Hey I’ve got this thing “and I’ve got a property that I would like “you to take into consideration when importing a module. “So I want you to vary the behaviour of my application “depending on certain constants or variables.” And you can’t do that with the syntax that we have now because everything is static.
And that’s excellent form the perspective of understanding the programme and being able to work out where things are.
However there will be instances where you want to change the behaviour in a dynamic fashion.
So this is the proposal and as I checked just before it’s at Stage Three.
And it resolves in a promise.
So it reads pretty well.
You can use the 08’s as well there and that would be fine.
So if you are looking for that functionality you can have it pretty soon.
So the big surprise and it took me by surprise is that you can start using ES6 modules now. They’re not as well supported as Asynch functions. (laughter) Okay so maybe.
Look it’s! Okay who can spot the thing that’s amusing about this? Exactly! Safari’s first to the party! So it’s flagged in Chrome at the moment and it’s flagged in Firefox and it’s flagged in Edge. Now, I found out about that from blog posts. I tend not to believe things I read in blog posts as you saw with the tree shaking.
This is how you wanna enable it if you wanna play with it.
So I wrote a tool to basically go through and automate some browsers and test to see if it was actually working.
And it’s absolutely true.
So Safari, Chrome with experiments enabled, Firefox with modules enabled, it works and it’s fantastic. So yes back here if you wanna enable the features, these are the flags.
In Firefox it’s dom.moduleScripts.enabled and in Chrome 60, this is like all the goodness, if you find Experimental Web Platform Features, turn that on.
Just says goodness, on.
(laughter) So I guess where is this leading? I’ll often lament with friends who’ve been doing web development for a while that this is what we’re going through.
And a development process is a rule by trans-poles and Webpack dev server chewing a gig of modern memory. That’s obviously not other people’s experience. So I think back here there was a road that was fairly straight.
We’re on this road now which is all windy and I think what we’ve got is a road that’s coming that is actually quite straight again.
We have the ability, at least I can see, of being able to develop again with essentially a static web server, an HTML page. John’s gonna be very happy about this because you basically have a static webpage, a script include and then your application, at least in a development sense, you’ll be able to work through.
You’ve got CSS imports, all of that stuff works. Bumbling for production is a different story and I think you should still go with something like Rollup, Closure, whatever you wanna do, able to get that compatibility across browsers. But at least from a development perspective, I think we can start to feel a little freer from what our normal processes are today.
(applause) (nerdy electronic music)