Introduction to WebPack

Johannes Stein speaking at JS Monthly London in January, 2017
95Views
 
Great talks, fired to your inbox 👌
No junk, no spam, just great talks. Unsubscribe any time.

About this talk

Webpack has become one the of most popular bundlers for the web. In this talk we will take a look into why we need bundlers, get into what makes Webpack so special and look into some of the improvements that are coming up with the upcoming major release.


Transcript


Hello, everyone. So, I'm Joe or Johannes as my usual name because I'm from Germany and I have one of those normal names. Anyway, so what I'm going to do today is pretty much like an introduction to Webpack or I want to say kind of like the basics, and some small code examples, but also a lot of like how it came to the progression of Webpack. What kind of led to it. So, before I start, I just want to like... quick question, who of you is using Webpack in production right now? Oh, actually not that many. That's really surprising. So, a bit about myself. So, I've been using React for the past, I want to say one and a half years or something. And like all of the kids that use React, they also use Webpack. So, that's how I got introduced into Webpack. And I found it really cool and since then, if I do any web projects, Webpack is like a must for me. So, everyone loves a good slide with a definition of what this thing is, actually, and what does it do? So, what is Webpack? Well, I mean, the official website says it's a module bundler. So, we probably want to explore what modules are before we get into what Webpack does for us. But essentially, what it does for you is it takes all of your assets and all of your front-end assets and puts it into one single file. So, let's go back like a few years. Let's go back like five years maybe or like six years before any of the modules that we now know and love came about. So, web development, let's say around 2009, 2010, it looked like this, more or less. So, we used global scope because global scope, at the time, was like the best thing we could ever think about. So, like, "Oh, amazing." And then, we had different properties attached to global scope. We would say, "Okay, my app was on the global scope." And then, obviously, we have like objects beneath my app, and then I guess everything works perfectly like in this example I wrote here. There are no dependencies on each other, but we're not living in a perfect world here. So, obviously, we will have different dependencies on different objects here. And this is kind of where the problem lies. In the world, 2009, 2010, all of those different files would live in their own JavaScript file. Let's say, the common object would be like CommonJS, and the info page, infopage.js, and so on. And they all have dependencies, and they kind of like depend on each other more or less in ways we can't imagine. It could get quite tricky to kind of fiddle things out, and so at the time, we had ScriptText, which we loved. And then we needed to think about in which order would we actually load all of those scripts? And it was actually like...in one of my first jobs, I was the responsible person for figuring this thing out. So, people came up to me, we had like a largish code base. And then other developers came up to me and said, "Hey, so I built this new thing right here, you figure out where it goes in terms of like the uploading process." And then I would need to go through all of the code base that they've written and then see, "All right, where's that dependency to this and to that?" And then just load it in the right order so it doesn't break the app completely. And yeah, it was a pain. So, there are different module systems that actually do this job a lot better. One of them, most of us know. So, when Node.js came along, they had like a module system called CommonJS. So, it's your typical require. So, it's really great because it says give me this file, and everything that's exported from the file, we store into a variable. And then we can say, we'd rather have exports and module.exports whereas exports pretty much allows us to define an object with export variables. Whereas module.export says, "Oh yeah, this can be pretty much anything like a function, like a number, whatever." And this is pretty great because with that, we have the ability to explicitly think about our dependencies instead of like just one global object where everything kind of like depends on each other, but we don't really know when and how well it plays together. And this was pretty much like where one of the module systems started. But this was, at the time, when Node.js came just on the server. So, there was, actually, like another movement, I would say, to get a module system into the browser which was called AMD, Asynchronous Module Definition. So, how it works is we define a module name, which in this case is MyModule. Then we go into, what are the dependencies for this module? And here we say, "Oh, we want this other module." And then inside of the function which is called the function factory, we can then access the exports from the other module. And then whatever we say and return will be exported to other modules if you would so desire. And yeah, this is another approach to the modules. What's really interesting about this one is that it would load my modules asynchronously if I so desired. So, for example, let's say I have like 1000 modules all across my app, and at the start, it would not load all at once, but just the modules that were required at the start. I mean, if I configure it that way, to be on. But, that's another story. So, out of those kind of like slightly different module systems, something came along, which is the ES6, ES2015 module center. So, we have the import, and then we import something from a module, and then we have the exports, which could be like a default export, which is very similar to what we know in Node.js from the module exports or it could be like an export just without a default. And this would be just the equivalent for the exports in Node.js. And yeah, so for example, if we want to use a default export, we would do like this example, just say import this thing from this file. Or if we have like just the exports instead of the default one, we would put the curly braces around the statement. So, as to this director, what comes out of it? All right, so now that we got modules out of the way, let's see what Webpack does. So, Webpack is pretty much like your typical npm install Webpack or like Yarn as Webpack, if you're already opting into Yarn. And so, it has like a configuration and I'm going to go into that in just a bit. And so, we create this configuration plan and we have the command line, Webpack app. And so we would just say Webpack and then take my configuration and then do anything without it. So, this would be like one of the most simplest configuration. So, this would be called Webpack.config.js. And then if we just say Webpack on the command line, it will automatically pick up this file and do something with it. So, two things we need to take a look at. One of them is the entry point. So, we need to say, "This is the thing that is our entry point to the app," and from that, it will see which modules does it explicitly define and then resolve those modules. And then it will create a bundle out of those and this is where the output comes in. Do we just specify a file name here? And then if we were to call this now on the command line, and actually have like an app.js, it would generate the bundle-js out of it. So, just want to take a look at what Webpack exports out of this. So, each of the defined modules that we have has like a little function around it, like similar...if you've seen what Node.js does with the modules we use, it's pretty much like the same thing. So, we have like module and exports and then like a specific Webpack require. So, Webpack, for example, resolves those very interestingly in that it saves the modules by an identifier inside of the bundle, and then instead of specifying...going to the file name that it was declared as, would just say, "Webpack require two," for example. And this is how it would solve the module inside of the bundle. All right. So, the next thing to talk about is loaders. That's actually like one of the things that changed in Webpack Version 2. And I actually totally forgot to say tis, but when I actually suggested this talk, Webpack 2 wasn't out yet, and so just a beta or like a release candidate. And then, during the between me suggesting the talk for this meet-up and the meet-up actually happening, Webpack 2 was released, and it's like now out in the wild, and if you install Webpack now, you would get the Version 2 instead of Version 1. So, that prompted me to kind of like use Version 2 examples for my coding samples, but since I've been using Webpack 1 in production so far, some of the coding samples might still be like a bit Version 1, so sorry for that. Anyways, back to the loaders. So, this is another Webpack configuration example which just concentrates on the loaders. So, we would say like there are two things we can do here that are pretty interesting. So, we say we have like a module property and then inside of that, we define rules, how to use the different things from different modules that Webpack would find. So, let's say Webpack or the entry point has like different modules that resolve to JavaScript, then it would now go inside of this rule since the...what does this regex matching to? And then it would load the appropriate file with the loader that we defined. So, let's say we want to use Babel, because Babel's cool and everyone should use Babel. Then we just I'll say everything that's a .js or .jsx file if you use with React, just use a Babel loader. And then it will compile all of those files that have like a .js or .jsx with Babel, and then the inside of this Webpack shell module that I showed earlier it will be ES2015 code transpiled to ES5. And now the interesting part is, Webpack doesn't just apply to JavaScript, which is kind of like where it differentiates from other bundlers. So, an interesting thing here is that we can also say, okay, let's take those styles or like the CSS and then just use a css-loader. So, which leads us to...if you use it like that, then we can say inside of our JavaScript, we can actually require CSS files which is pretty neat. I mean, when I saw that the first time, I was blown away and the thing is like it really...so by default, what it does with the css-loader in place, it will actually generate off the CSS and then put it inside of your JavaScript bundle and then insert into the dump the first time you load it. So, that's a default behavior, but you can change it around if you so desire. And if you write your own loader, for example, you can also define how this should happen. So, I just put in like a document, createElement, or just to say there's some dumb stuff going on here, because I use React. So, I don't know what other kids use who don't use React. So, plugins. We got loaders out of the way and the next thing is what Webpack also does is, plugins. So, now we have like this big bundle, like all of the modules put into a bundle, but obviously, there are some things that should be done depending on an environment. Let's say like uglifying, minifying my bundle because, obviously, in development, it's totally fine if you have like everything as this huge JavaScript file. But obviously, in a production environment, we want the fast loading time. So, what we do here is... I mean, same properties as I explained before, so we have...inside our config, we have the app entry point and then the output will then be like the bundle, and then we say okay, this should go into this dist folder. And then, obviously, again, we use Babel because Babel is still cool. And then in the plugins, we can then say, use Uglify. So, the resulting bundle will now be completely minified. The idea is, if you use a Webpack this way, you will have different configurations depending on your environment. You will have like a different one for your development environment, and like a production environment where, for example, Uglify is one of the key parts of that. All right. So, there's one thing that's really interesting which is tree shaking and like when Webpack first announced that it would be going with tree shaking for Version 2, people got really excited about that. And the reason is, so earlier, we've seen six modules in terms of like the export and the export default. So, now imagine like a file where you have like different exports. And then other files, obviously, require those. But you only require part of it and not the complete module. Just inside of a module that has five exports, you only use two of them. So, wouldn't it be cool if in the resulting bundle, it wouldn't put out like the complete module, but just the two exports that I'm actually using, that I'm actually importing here. And this is pretty much what tree shaking does for us. Because it statically analyzes what exports are actually being used and then only puts those in their resulting bundle. And another interesting thing that Webpack really does well is code splitting. So, imagine you have a single page app with different subsides. Like you have an about page, you have like a product page, you have like whatever, or a perfect example for that, Instagram. So, your resulting bundle, your resulting JavaScript bundle, is obviously huge. Let's say 4 MBs if things are good, maybe more like 10 MBs or 20 MBs. But, obviously, that's not what you want to send out to the user because it's just way too much and it takes way too long. So, code splitting is a way for Webpack to split out your one bundle into different chunks. And that means then, instead of like one giant bundle, it will generate different smaller bundles. And when your app loads, it will just load the specific things you will need for start. So, a way that really works well, for example, is saying I want to split up all of the things that are on top pages. So, at the start, it will only require what's really needed. And then when I click like on an about page, it will dynamically then get what's needed for this about page. And so how this works is, if you've used Webpack 1 before, you have like require.ensure and the syntax is really similar to like the AMD syntax. Or if you are on V.2, it's called system.imports and it's exactly what you have with the system.imports ES2015 specifications [inaudible 00:20:26] tons of promise with the modules. All right. So, those are like the big features, I would say, that Webpack has. So, let's see what else there is. So, obviously, one of the things that's like a big question if you have just one giant bundle there, how would I do debugging? And by default, Webpack emits source maps to step through your code and see where the different things went wrong. So, this one is a bit interesting. So, if you are a library author, for example, and want to bundle with Webpack, Webpack, I want to say like the target audience or the target developer are more like developers who are doing single page application, but that in itself doesn't say that UMD builds don't work. So, what it does is, if you're a library author, you don't know that the transfer player, there is that in JavaScript, will it be like an AMD module? Will it be a CommonJs one or will it actually be using one of those browser globals. So, UMD is a way to cater for all those three. And then Webpack allows us to build your bundles out in that way. And one of the things also, this is pretty much one of the cool features I would say. Especially if you work in a React environment, then you typically see like all of those things configured with live reloading and stuff like that. And it's amazing, definitely. And it's really easy to get started actually because it's just a loader that you put in. And you can chain all of those loaders together that you needed. So, obviously, so far, it was more front-end oriented and like single page apps and those kinds of things. But Webpack has different targets. I mean, I kind of like touched on the UMD builds for library author kind of thing, but it can also target Electron, for example, of like Node which is also pretty interesting and I actually have an example for that. So, let's say you want to build a server with Webpack. It sounds crazy, don't worry, it's actually not that bad. So, how it works is you build your stuff up, etc. And then you can build a bundle. And you just say target: 'node' pretty much. That's the only property that's coming in in the configuration. And then it will build out a bundle that's deployable. I mean, it's still just a bundle, but it will be compatible to Node. This is great for two things like obviously, if you're deploying your server, you want similar techniques that you have on the front-end in terms of stuff like smaller size, etc. And all-in-one...if you have like all-in-one bundles, it can definitely improve the deployment if you so desire to go into that direction. And also, if you have a universal JavaScript app in terms of like they run the same way on the server and on the front-end, then, at some point, especially if you're using Webpack on the front-end, you will go into, "Oh, I kind of want the similar things on the server as well," especially because Webpack has like a few specialties in terms of like if you want to require stylesheets and stuff like that. And if you try the same on the server without a bundled Webpack, it will result in errors, to be fair. So, it's not a good thing. All right. So, that's pretty much it, it was kind of like touching on all different subjects. So, if you have any questions, now is the time, otherwise, I'll be here for a bit and then you can ask me later, as well. Yes. - [Man 1] I just have a question about Webpack and TypeScript. Do you like know any best practices, for example, how to eventually use a TypeScript, but not just to confer, but as it goes to debugging because it's like a double source maps because it eventually compile a bundle GS and then it needs to map to GS6 and then map it back to the TS6. - Yeah, that's a tricky one. Actually, I don't know the specifics of that and I kind of had hoped that source maps in itself would actually completely work on that. But I've never used TypeScript and the JSX in a project together yet. - [Man 2] Keep talking. - [Joe] Thank you. - [Man 3] Any other questions? - [Man 4] The documentation for Webpack is notoriously not very good to digest. Have you found it easy? Has it got better? - Oh, definitely. Like the documentation for Webpack 2 is very good. Obviously, there are some things that are not quite documented yet, or like not quite documented for V.2 yet. For example, this code splitting I brought up earlier, they only have like how it was with the requirements here, but not like the new way. But otherwise, I feel like the documentation in itself for Webpack 2 has gotten like way better, and it has super simple examples to start with. Yes. - [Man 5] Hi. The things you didn't mention about like the sort of trunk caches, breaking cache, so we do that. It's not really related to Webpack, it's like a common issue, but are there any tips for like actually doing deployments for hooking up your Webpack building so you're deploying to a server? - I'm sorry, like doing deployments on what? - So, if you're doing a production build with the trunk caches and your output fails, then you wanted to deploy that onto a server while people are using... generally, I'd just like to know what people are using for that. - So, on the front-end side, I've done like a typical kind of trunk caching and like the single page app, and what I told before about having like different things for different parts on the app. But for the server specifically, I haven't done anything. And to be honest, most things just kind of worked for me without actually needing to put in more effort. But I'd be happy to discuss that more in detail after. All right. - [Man 3] If anyone else have questions, we will [inaudible 00:28:36] in about an hour. Thank you very much.