-
-
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 6 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,262 @@ 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-libraries), 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. | ||
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. The |
||
|
||
__index.html__ | ||
## Preparation ## | ||
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. Can you remove the trailing |
||
|
||
``` html | ||
<!doctype html> | ||
First, let's adjust our project a little bit: | ||
|
||
__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') | ||
} | ||
}; | ||
``` | ||
|
||
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 | ||
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 think you'll need to include the 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. Let's discuss this one, maybe there's a simpler example that we can use as we should probably hold off on introducing the 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. 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 commentThe reason will be displayed to describe this comment to others. Learn more. That could work, however then the 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 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Scratch this. How about something like a With the 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. 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 |
||
[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] | ||
``` | ||
|
||
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). | ||
|
||
|
||
## Setting up HtmlWebpackPlugin ## | ||
|
||
First install the plugin and adjust the `webpack.config.js` file: | ||
|
||
``` bash | ||
npm install --save-dev html-webpack-plugin | ||
``` | ||
|
||
__webpack.config.js__ | ||
|
||
``` diff | ||
const path = require('path'); | ||
+ const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
|
||
module.exports = { | ||
entry: { | ||
app: './src/index.js', | ||
vendor: ['lodash'] | ||
}, | ||
+ plugins: [ | ||
+ new HtmlWebpackPlugin() | ||
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. Two things... I think you need to first show that the old HtmlWebpackPlugin({
title: 'Output Management'
}) |
||
+ ], | ||
output: { | ||
filename: '[name].bundle.js', | ||
path: path.resolve(__dirname, 'dist') | ||
} | ||
}; | ||
``` | ||
|
||
Now run `npm run build` and you should see something similar to this: | ||
|
||
``` 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] | ||
``` | ||
|
||
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. This is great, but it also has overwritten everything else we had in our file. | ||
|
||
In most cases you probably want to provide a certain template to the `HtmlWebpackPlugin`, so that you can still have your own `index.html` file, but have webpack automatically add generated files. | ||
|
||
|
||
## Adding a template to HtmlWebpackPlugin ## | ||
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 would drop this as an entire section and, if anything, just mention that you can pass a custom template if necessary, maybe linking to that . It's also probably worth linking to the popular html-webpack-template package as you can get pretty far with that. I have multiple projects that use this plugin and haven't needed to write a custom template -- plus the things you added with the custom template are available from the base plugin (and almost anything else you'd need is available from |
||
|
||
Let's add a template that `HtmlWebpackPlugin` can use, but we keep it in our `src` directory instead. | ||
|
||
>T `HtmlWebpackPlugin` uses the `.ejs` extension by default for templates. | ||
|
||
__project__ | ||
|
||
``` diff | ||
webpack-demo | ||
|- package.json | ||
|- webpack.config.js | ||
|- /dist | ||
|- bundle.js | ||
- |- index.html | ||
|- /src | ||
+ |- index.ejs | ||
|- index.js | ||
|- /node_modules | ||
``` | ||
|
||
__src/index.ejs__ | ||
|
||
``` html | ||
<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> | ||
<title><%= htmlWebpackPlugin.options.title %></title> | ||
</head> | ||
<body> | ||
<script src="/app.bundle.js"></script> | ||
<h1>Output Management</h1> | ||
</body> | ||
</html> | ||
``` | ||
|
||
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)? | ||
Now adjust your `webpack.config.js` and tell `HtmlWebpackPlugin` to use our new template: | ||
|
||
__webpack.config.js__ | ||
|
||
## Auto-Generated HTML | ||
``` diff | ||
const path = require('path'); | ||
const HtmlWebpackPlugin = require('html-webpack-plugin'); | ||
|
||
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: | ||
module.exports = { | ||
entry: { | ||
app: './src/index.js', | ||
vendor: ['lodash'] | ||
}, | ||
plugins: [ | ||
- new HtmlWebpackPlugin() | ||
+ new HtmlWebpackPlugin({ | ||
+ title: 'Output Management', | ||
+ filename: 'index.html', | ||
+ template: 'src/index.html' | ||
+ }) | ||
], | ||
output: { | ||
filename: '[name].bundle.js', | ||
path: path.resolve(__dirname, 'dist') | ||
} | ||
}; | ||
``` | ||
|
||
__webpack.config.js__ | ||
Now run `npm run build` again, which should yield a similar result as in the last step: | ||
|
||
``` 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' | ||
}) | ||
] | ||
}; | ||
``` bash | ||
Hash: 3adf5eed79147a35559e | ||
Version: webpack 2.6.1 | ||
Time: 844ms | ||
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 244 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!./src/index.ejs 540 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. | ||
Open `index.html` in your code editor again. This time you will see that the `HtmlWebpackPlugin` has dynamically added a title (which we specified in our `webpack.config.js` at the `HtmlWebpackPlugin` configuration options), as well as the generated bundles. | ||
|
||
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`. | ||
This is great, because now we don't have to manually adjust our `index.html` file anymore, webpack takes care of that. As an added bonus, we now keep the `index.ejs` template in our `src` directory, so our `dist` directory only contains generated files. | ||
|
||
T> Check out the [`HtmlWebpackTemplate`](https://github.com/ampedandwired/html-webpack-plugin#configuration) page for more advanced options, including outputting multiple HTML files. | ||
|
||
## 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 ## | ||
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 think you need a space between |
||
|
||
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 HtmlWebpackPlugin({ | ||
title: 'Output Management', | ||
filename: 'index.html', | ||
template: 'src/index.html' | ||
- }) | ||
+ }), | ||
new CleanWebpackPlugin(['dist']) | ||
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. Maybe put this before the |
||
], | ||
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! | ||
|
||
|
||
## Conclusion ## | ||
|
||
Now that you've learned about dynamically adding bundles to your HTML, let's dive into the next guide where this will be very useful: [`Code Splitting`](/guides/code-splitting). | ||
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. Even though Code Splitting points to this as a pre-requisite, I think we should just point to the next guide down which I believe is Development so people can go through in order. You could also point to both and mention that Code Splitting is more advanced. |
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.
See my comment below re the manifest -- if we do leave that section in then we should leave this in as well.