-
-
Notifications
You must be signed in to change notification settings - Fork 3.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Updated output-management.md #1344
Changes from 10 commits
df84d39
c6e6ab3
783976e
7cb3408
b1f7c91
57b9b5b
daa7fa5
ad93685
04260fb
ae3cbca
953567b
55ee88e
0a6bac0
0393601
229ee8f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,85 +3,201 @@ title: Output Management | |
sort: 4 | ||
contributors: | ||
- skipjack | ||
- TheDutchCoder | ||
--- | ||
|
||
Managing webpack's [output](/configuration/output) and including it in your HTML files may not seem tough at first, but once you start [using hashes in filenames](/guides/caching) and outputting [multiple bundles](/guides/code-splitting), things can start to get a bit hairy. However, there's no need to fear as a few plugins exist that will make this process much easier to manage. | ||
>T This guide extends on code examples found in the [`Asset Management`](/guides/asset-management) guide. | ||
|
||
First let's take a look at where you might stand without these plugins: | ||
So far we've manually included all our assets in our `index.html` file, but as your application grows and once you start [using hashes in filenames](/guides/caching) and outputting [multiple bundles](/guides/code-splitting), it will be difficult to keep managing your `index.html` file manually. However, there's no need to fear as a few plugins exist that will make this process much easier to manage. | ||
|
||
__index.html__ | ||
## Preparation | ||
|
||
``` html | ||
<!doctype html> | ||
First, let's adjust our project a little bit: | ||
|
||
<html> | ||
<head> | ||
<title>Output Management</title> | ||
<link rel="shortcut icon" href="/favicon.ico" /> | ||
<link rel="stylesheet" href="/styles.min.css" /> | ||
<script src="/vendor.bundle.js"></script> | ||
</head> | ||
<body> | ||
<script src="/app.bundle.js"></script> | ||
</body> | ||
</html> | ||
__dist/index.html__ | ||
|
||
``` diff | ||
<html> | ||
<head> | ||
- <title>Asset Management</title> | ||
+ <title>Output Management</title> | ||
+ <script src="./vendor.bundle.js"></script> | ||
</head> | ||
<body> | ||
- <script src="./bundle.js"></script> | ||
+ <script src="./app.bundle.js"></script> | ||
</body> | ||
</html> | ||
``` | ||
|
||
Now adjust the config. We'll be adding a vendor entry point as an example and we'll change the output as well, so that it will dynamically generate bundle names, based on the entry point names: | ||
|
||
__webpack.config.js__ | ||
|
||
``` diff | ||
const path = require('path'); | ||
|
||
module.exports = { | ||
entry: { | ||
- index: './src/index.js', | ||
+ app: './src/index.js', | ||
+ vendor: ['lodash'] | ||
}, | ||
output: { | ||
- filename: 'bundle.js', | ||
+ filename: '[name].bundle.js', | ||
path: path.resolve(__dirname, 'dist') | ||
} | ||
}; | ||
``` | ||
|
||
Now remove the lodash import from your main script: | ||
|
||
__src/index.js__ | ||
|
||
``` diff | ||
- import _ from 'lodash'; | ||
- | ||
function component() { | ||
var element = document.createElement('div'); | ||
|
||
element.innerHTML = _.join(['Hello', 'webpack'], ' '); | ||
|
||
return element; | ||
} | ||
|
||
document.body.appendChild(component()); | ||
``` | ||
|
||
Let's run `npm run build` and see what this generates: | ||
|
||
``` bash | ||
Hash: 5f3b0265b87c603b4a0f | ||
Version: webpack 2.6.1 | ||
Time: 539ms | ||
Asset Size Chunks Chunk Names | ||
vendor.bundle.js 544 kB 0 [emitted] [big] vendor | ||
app.bundle.js 2.81 kB 1 [emitted] app | ||
[0] ./~/lodash/lodash.js 540 kB {0} [built] | ||
[1] (webpack)/buildin/global.js 509 bytes {0} [built] | ||
[2] (webpack)/buildin/module.js 517 bytes {0} [built] | ||
[3] ./src/index.js 172 bytes {1} [built] | ||
[4] multi lodash 28 bytes {0} [built] | ||
``` | ||
|
||
Here we've loaded a favicon, our stylesheet (extracted with the [`ExtractTextWebpackPlugin`](/plugins/extract-text-webpack-plugin)), any libraries (split into [a separate bundle](/guides/code-splitting)), and finally our main bundle (`app.bundle.js`). This is ok, but what happens if we change our entry point names? What if we decide to take advantage of [better caching practices](/guides/caching)? | ||
We can see that webpack generates our `vendor.bundle.js` and `app.bundle.js` files, which we also specified in our `index.html` file. But what would happen if we changed the name of one of our entry points? The generated bundles would be renamed on a build, but our `index.html` file would still reference the old names. Let's fix that with the [`HtmlWebpackPlugin`](/plugins/html-webpack-plugin). | ||
|
||
|
||
## Auto-Generated HTML | ||
## Setting up HtmlWebpackPlugin | ||
|
||
In comes the [`HtmlWebpackPlugin`](/plugins/html-webpack-plugin) to save the day. Using this plugin, you can stop hard-coding the output filenames into a manually-managed file and, instead, sit back as webpack auto-generates an `index.html` file for you. Let's take a look at what you'll need to add to your configuration: | ||
First install the plugin and adjust the `webpack.config.js` file: | ||
|
||
``` bash | ||
npm install --save-dev html-webpack-plugin | ||
``` | ||
|
||
__webpack.config.js__ | ||
|
||
``` js | ||
var path = require('path'); | ||
var HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
|
||
module.exports = { | ||
entry: { | ||
app: './src/index.js', | ||
vendor: [ 'react', 'react-dom' ] | ||
}, | ||
|
||
output: { | ||
path: path.resolve(__dirname, 'dist'), | ||
filename: '[name].bundle.js' | ||
}, | ||
|
||
plugins: [ | ||
new HtmlWebpackPlugin({ | ||
title: 'Output Management', | ||
favicon: './favicon.ico' | ||
}) | ||
] | ||
}; | ||
``` diff | ||
const path = require('path'); | ||
+ const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
|
||
module.exports = { | ||
entry: { | ||
app: './src/index.js', | ||
vendor: ['lodash'] | ||
}, | ||
+ plugins: [ | ||
+ new HtmlWebpackPlugin({ | ||
+ title: 'Output Management' | ||
+ }) | ||
+ ], | ||
output: { | ||
filename: '[name].bundle.js', | ||
path: path.resolve(__dirname, 'dist') | ||
} | ||
}; | ||
``` | ||
|
||
Before we do a build, you should know that the `HtmlWebpackPlugin` by default will generate its own `index.html` file, even though we already have one in the `dist/` folder. This means that it will replace our `index.html` file with a newly generated one. Let's see what happens when we do an `npm run build`: | ||
|
||
``` bash | ||
Hash: 81f82697c19b5f49aebd | ||
Version: webpack 2.6.1 | ||
Time: 854ms | ||
Asset Size Chunks Chunk Names | ||
vendor.bundle.js 544 kB 0 [emitted] [big] vendor | ||
app.bundle.js 2.81 kB 1 [emitted] app | ||
index.html 249 bytes [emitted] | ||
[0] ./~/lodash/lodash.js 540 kB {0} [built] | ||
[1] (webpack)/buildin/global.js 509 bytes {0} [built] | ||
[2] (webpack)/buildin/module.js 517 bytes {0} [built] | ||
[3] ./src/index.js 172 bytes {1} [built] | ||
[4] multi lodash 28 bytes {0} [built] | ||
Child html-webpack-plugin for "index.html": | ||
[0] ./~/lodash/lodash.js 540 kB {0} [built] | ||
[1] ./~/html-webpack-plugin/lib/loader.js!./~/html-webpack-plugin/default_index.ejs 538 bytes {0} [built] | ||
[2] (webpack)/buildin/global.js 509 bytes {0} [built] | ||
[3] (webpack)/buildin/module.js 517 bytes {0} [built] | ||
``` | ||
|
||
This setup will generate the same HTML shown in the example above, excluding the extracted CSS. The plugin allows for many settings including extending your own template, minifying the output, and changing the script injection site. See [its documentation](/plugins/html-webpack-plugin) for more details. | ||
If you open `index.html` in your code editor, you'll see that the `HtmlWebpackPlugin` has created an entirely new file for you and that all the bundles are automatically added. | ||
|
||
T> Check out the [`HTMLWebpackTemplate`](https://github.com/jaketrent/html-webpack-template) extension for even more options including easy insertion of an `appMountId`, `meta` tags, and a `baseHref`. | ||
If you want to learn more about all the features and options that the `HtmlWebpackPlugin` provides, then you should read up on it on the [`HtmlWebpackPlugin`](https://github.com/jantimon/html-webpack-plugin) repo. | ||
|
||
You can also take a look at [`html-webpack-template`](https://github.com/jaketrent/html-webpack-template) which provides a couple of extra features in addition to the default template. | ||
|
||
## Multiple HTML Files | ||
|
||
To generate multiple HTML files, say for a multi-page web application, you can utilize the [`MultipageWebpackPlugin`](https://github.com/mutualofomaha/multipage-webpack-plugin). This plugin is similar to the `html-webpack-plugin`, in fact it uses that plugin under the hood, however it can be used to generate an HTML file per entry point. | ||
## Cleaning up the `/dist` folder | ||
|
||
As you might have noticed over the past guides and code example, our `/dist` folder has become quite cluttered. Webpack will generate the files and put them in the `/dist` folder for you, but it doesn't keep track of which files are actually in use by your project. | ||
|
||
## The Manifest | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why scrap this section? I know it's not easy to do a fully fleshed out example but we could leave it in for now and just say something like:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I honestly thought it was way too advanced to be useful, but I'll add it back in with some adjustments and link off to the right resources in case people are interested in it. |
||
In general it's good practice to clean the `/dist` folder before each build, so that only used files will be generated. Let's take care of that. | ||
|
||
Let's step back for a second now and ask a more high-level question -- how do these plugins know what files are being spit out? The answer is in the manifest webpack keeps to track how all your modules map to the output bundles. If you're interested in managing webpack's [output](/configuration/output) in other ways, the manifest would be a good place to start. | ||
A popular plugin to manage this is the [`clean-webpack-plugin`](https://www.npmjs.com/package/clean-webpack-plugin) so let's install and configure it. | ||
|
||
This data can be extracted into a json file for easy consumption using the [`WebpackManifestPlugin`](https://github.com/danethurber/webpack-manifest-plugin) or [`ChunkManifestPlugin`](https://github.com/soundcloud/chunk-manifest-webpack-plugin). Using the `ChunkManifestPlugin`, you would specify what to name it, what variable to expose it under, and whether or not to inline it via the `html-webpack-plugin`: | ||
``` bash | ||
npm install clean-webpack-plugin --save-dev | ||
``` | ||
|
||
``` js | ||
new ChunkManifestPlugin({ | ||
filename: 'manifest.json', | ||
manifestVariable: 'webpackManifest', | ||
inlineManifest: false | ||
}) | ||
__webpack.config.js__ | ||
``` diff | ||
const path = require('path'); | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
const CleanWebpackPlugin = require('clean-webpack-plugin'); | ||
|
||
module.exports = { | ||
entry: { | ||
app: './src/index.js', | ||
vendor: ['lodash'] | ||
}, | ||
plugins: [ | ||
+ new CleanWebpackPlugin(['dist']), | ||
new HtmlWebpackPlugin({ | ||
title: 'Output Management', | ||
filename: 'index.html', | ||
template: 'src/index.html' | ||
}) | ||
], | ||
output: { | ||
filename: '[name].bundle.js', | ||
path: path.resolve(__dirname, 'dist') | ||
} | ||
}; | ||
``` | ||
|
||
See [the concepts page](/concepts/manifest) for more background information and the [caching guide](/guides/caching) guide to find out how this ties into long term caching. | ||
Now run an `npm run build` and inspect the `/dist` folder. If everything went well you should now only see the files generated from the build and no more old files! | ||
|
||
|
||
## The Manifest | ||
|
||
You might be wondering how webpack and its plugins seem to "know" what files are being generated. The answer is in the manifest that webpack keeps to track how all the modules map to the output bundles. I fyou're interested in manageing webpack's [`ouput`](/configuration/output) in other ways, the manifest would be a good place to start. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /s/I fyou're/If you're There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. /s/manageing/managing |
||
|
||
The manifest data can be extracted into a json file for easy consumption using the [`WebpackManifestPlugin`](https://github.com/danethurber/webpack-manifest-plugin). | ||
|
||
Were not going through a full example of how to use this plugin within your projects, but you can read up on the [`manifest concepts`](/concepts/manifest) and the [`Caching`](/guides/caching) guide to find out how this ties into long term caching. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At some point a small manifest example here would be good (the current 200 lines actually isn't bad so adding to this probably wouldn't be overwhelming). Maybe using the manifest to accomplish the same thing as above in a custom way could be cool if it can stay fairly concise. The problem is I don't think either of us has experience with this so maybe someone will volunteer or one of us could dig into it at some point. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I agree, an example would be cool, but maybe someone with more experience can chip in on that, would be greatly appreciated! |
||
|
||
|
||
## Conclusion | ||
|
||
Now that you've learned about dynamically adding bundles to your HTML, let's dive into the next guide: [`Development`](/guides/development). If you want to dive into more advanced topics, we would recommend heading over to the [`Code Splitting`](/guides/code-splitting) guide. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you'll need to include the
CommonsChunkPlugin
for this to work -- meaning I think yourapp.bundle.js
will actually havelodash
included in it as well.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's discuss this one, maybe there's a simpler example that we can use as we should probably hold off on introducing the
CommonsChunkPlugin
until Code Splitting.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You seem to be right, maybe we can just remove the lodash import from the main script for this example?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That could work, however then the
vendor
bundle wouldn't really be used anywhere. How about we leavelodash
but don't extract it and instead ofvendor
we add separate entry script that does something else to the page? This way that second bundle will be automatically included by theHTMLWebpackPlugin
(i.e. it should serve as a good way to illustrate the ideas of this article) but doesn't require any extra complexity in terms of plugins or configuration.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I didn't think about that.
Adding something else in might be a good idea, what would you propose?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm I'll give this some thought. Nothing jumping to mind at the moment. Maybe inserting another button that does something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Scratch this.
How about something like a
print.js
script that outputs something on the console.We import it in
index.js
and attach it to a button.With the
HtmlWebpackPlugin
it then gets automatically added.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, I've set up a repo to host the code examples for the guides.
It's under my account for now, but we could move it over to the webpack organization, that would be fine by me.
I'll go through the
output-management
code examples, based off the end state of theasset-management
one and I'll see what I can add as a vendor entry that makes sense.