This course has been updated! We now recommend you take the Webpack 4 Fundamentals course.
Transcript from the "Exercise: Adding Commons Chunking Part 3" Lesson
[00:00:00]
>> [MUSIC]
[00:00:04]
>> Kent C Dodds: The next piece of this is actually we haven't quite accomplished all of our purposes as far as caching is concerned. So we can configure our server and to tell the browser, hey, this has been updated you. Or the browser can say, okay, I need to update this resource every so now and then.
[00:00:28] It happens at the server configuration level. It just tells the browser how long to cache it. But what's even become a lot more prevalent as the better approach is that every single one of your assets has a unique ID as part of its filename. And so, then you can configure your server to say, hey, browser cache this for years, forever, and you never need to re-download this.
[00:00:57] And then every single time you re-bundle you, [COUGH] sorry, I'm starting to really lose my voice now. So then every single time that you deploy a new version, it's a totally separate ID, totally separate resource that the browser gets. And so when the browser says, hey, give me your index.html, let me find the JavaScript files in there, it's gonna say, this is a totally new URL.
[00:01:25] I've never seen it before and it can't check the cache cuz it doesn't exist. And so what's nice about this is pretty much just the fact that you can say, never try to fetch this resource ever again. And so, yeah, so then you don't have to even really worry about the cache and the browser being outdated or invalid.
[00:01:48] It just is always cached. So to accomplish that, we're going to utilize something called the chunk hash in our bundle filename. So let's see, what did I do? We have bundle.name, we also have chunkhash. So there are a couple things that you can have. I should've explained this earlier with name.
[00:02:17] But you can have webpack interpolate a couple of things in filename and path and publicPath, I think, and a couple other places, I think. But yeah, filename it will do for us and we'll just say chunkhash. And now if you run the build, you're gonna get. Right now I'm running the prod build, but [SOUND],
[00:02:45]
>> Kent C Dodds: I should say prod actually does take a while and it's because it's uglifying all of your code. We just uglified all of react and d3. So, it normally won't take you quite that long when you're just doing your development stuff. But yeah, if you look at the output now, you're gonna see a bunch of files with the chunkhash in there.
[00:03:07] And so, now every time you make a change to app, for example, it's gonna update the app IDs. And so now that begs the question, okay, so what are we gonna do in our index HTML? So should I just, okay, copy, paste that? Every single time that I go to update stuff, probably not a good idea.
[00:03:29] So there's a plugin that will generate our index HTML for us and take the results of the Webpack build and all the assets and stick those into our HTML. So that's what we're going to do next. Any questions before I continue on that? Yeah.
>> Speaker 2: Is it possible to cache a rendered version of react app in webpack?
[00:03:52]
>> Kent C Dodds: That's an interesting question. So basically, no. Well, I imagine that you could do something interesting, but that you would not want to do it that way. Normally, if you wanted to send a rendered version of a react app to the client, you would do this on the server, so server side rendering.
[00:04:17] Yeah, to a cache, a rendered version of a react app, unless your app was totally static and didn't have any dynamic properties at all, then you could store it as a string and then send that to the client. So that could potentially be possible. But I would recommend against that approach.
[00:04:37] Probably overcomplicated and not really doing what you're looking for. Yeah, so Scott is asking if there's a way we could kind of work around the limitation of our script tags and the names of things and stuff. Unfortunately, Scott, we need to send the index HTML to the client that has all the scripts in it that we want to have them loading.
[00:05:06] And so, yeah, we are gonna use a plugin to do that and it'll make our lives a lot easier. Let's see and Billy is asking how can I just tell webpack to build the app entry but not the vendor? Billy, I imagine there's a way to do that but I'm not certain off the top of my head.
[00:05:26] And we can chat later. I imagine there's a way. Okay, cool, so let's go ahead and get into adding this plug in so that we can start using our app again. So this plugin is called the html webpack plugin. And you install it the same way that you install normal modules, except it's already installed for you.
[00:05:50] So we'll go to, I'll just add this any arbitrary place. html-webpack-plugin. And the version that you all have on your machines right now is 2.22.0, pretty sure that's the latest. And then with that installed, we'll say const Html WebpackPlugIn=require (html-webpack-plugin). And then we add that as just a regular plugin like we have with these other items.
[00:06:39] With this one, it doesn't make a difference whether it's prod not and so we don't need to wrap it in if prod. And we can say, create a new HTMLWebpackPlugin. And actually, sorry, I should be more specific. We want this in development. If we don't have it in development, then we're gonna have a hard time.
[00:07:00] AKA, it will not work. So yeah, I don't think it makes a difference that it's in test though. So you could actually say ifNotTest if you want to but we're not going to. So then we'll say index.html is where the template is going to reside. And for us, by default, it'll inject all of our script tags and everything for us at the bottom of, or in the body.
[00:07:27] That's where you should normally put stuff but because we're doing some fancy things, or pretty much we're just being lazy, we're gonna say inject into the head. And with that, we actually need to make a couple other changes. So because right now we have our index HTML at the root of our project.
[00:07:52] But the html webpack plugin resolves the template based off of the context. And quite honestly, now it has turned from the index HTML that we're shipping to browsers into actually source code. And I mean that in every sense of the word, it is now like something that is used to generate our app.
[00:08:15] It's no longer something that we ship directly. So we're gonna move our index HTML to our source directory. Where the HTML Webpack plugin will grab it and then stick it into our disk directory. So we'll change a couple of things about the way how our start script works and stuff like that, and there are a couple ways to do this.
[00:08:40] But that's what we're going to do. And because now the index HTML is in the same place as it's gonna be in the disk directory just like our bundle, we can now get rid of this public path as well. Because it's no longer the disk directory, it's just a sibling.
[00:08:56] This will make a little bit more sense when I actually run the build. So if we npm run build:dev and we will build, build, build, build. And we have our index HTML right there. It gets the script tags that we need it to. Looks like it has a bundle app and a bundle up with a name in it, which is kind of not what I expected.
[00:09:21] Something's funny here, I know what it is. Because our template actually has this script tag in it. So we need to get rid of that. And then if we ran, cuz our template no longer needs the script tag, HTML webpack plugin is going to inject it for us.
[00:09:40] And so that script tag, so here, again, here's our template. This is just pretty much exactly what we had before without the script tags in it. This is the result, the Webpack plugin added this script tag for us. It adds the bundle app and the bundle vendor for us.
[00:10:00] And with the correct hashes and everything, now all we need to do is deploy this disk directory and we're off to the races. One other thing that you'd want to think about is having a script that'll copy this favicon to the disk directory. Webpack doesn't really do anything with your favicons.
[00:10:19] That's one thing that Tabia said that he wished that was more of a first class citizen. So pull requests are welcome I'm sure. But yeah, actually if we go really quickly and update our package.json start script to serve out the disk directory now, we should get a working functioning app somewhere.
[00:10:43] There we go. Yeah?
>> Speaker 2: They're asking why are you're adding it in the head section?
>> Kent C Dodds: Yeah, so if you recall here, well, I'll demonstrate it again. If we go back to our webpack config and I remove that in the default being at the end of the body.
[00:11:05] Actually, we'll just run dev, same problem will occur here. Yeah, actually we're gonna address that problem here in a little bit. We're gonna build dev [NOISE] and then npm start. So you remember the flash? That's why we put it in the head. So this is the critical CSS thing and the, yeah, making sure that the user has a good experience when they load your app in the first place.
[00:11:41] So that's why we put it in the head. Generally, you wouldn't wanna do that. But that is application-specific stuff anyway. Okay, great, so with every new cool thing that we add, there's yet and a new problem to surmount. And one of them is our developer experience. Now if we say npm run dev, then we're going to get this error.
[00:12:09]
>> Kent C Dodds: And that is cannot use chunkhash for chunk in here, use hash instead. I actually don't know why that's a problem, but I know how to get around it. So, this is another scenario where we don't really care to have the chunkhash in developments. I don't care what these files are called while I'm developing.
[00:12:32] I don't need my browser to cache these things while I'm developing it, right? So I can just use these utils again to say ifProd, then I want you to do this thing. Otherwise, I want it to just be a bundle.[name].js.
>> Kent C Dodds: And that will solve the problem.
>> Kent C Dodds: And we can go to localhost and we have HMR enabled and all the fun stuff, loading the bundle properly, or that code split bundle.
[00:13:09] And everything is good to go.