Exercise: Adding Commons Chunking Part 3

In notebook:
FrontEndMasters Webpack Deep Dive
Created at:
2017-01-04
Updated:
2017-01-04
Tags:
JavaScript Webpack libraries React
We can now configure the server to tell the browser how long to cache the resource. 

Chunk hash

What's an even strategy is that every file has a unique id in the filename. Now we can set the server to tell the browser to cache the resource forever. 
And if you re-bundle you change the id of the file. This way you never have to worry about the cache of the browser. 

We will now use the chunkhash to identify the file versions. 
  // ****   webpack.config.babel.js   ****

...
module.exports = env => {
  const {ifProd, ifNotProd} = getIfUtils(env)
  const config = webpackValidator({
    context: resolve('src'),
    entry: {
      app: './bootstrap.js',
      vendor: ['todomvc-app-css/index.css'],
    },
    output: {
      // 1. ++++ add the chunkhash
      filename: 'bundle.[name].[chunkhash].js', 'bundle.[name].js',
      path: resolve('dist'),
....
Now, each file gets a unique id in the name (the chunkhash).

How to include these files in index.html?

At every rebuild you would need to update index.html since the bundle filename just changed. 

We will use a plugin that will generate the index.html file for us that will include the correct bundles. 

Building the index.html

Q&A: is it possible to generate an html file with React in Webpack?
He says no... If you want to send a rendered version of react to the client you would do it with NodeJS on the server. 
But you might be able to do it, by rendering the page as string, saving it, and sending that to the client. 

need to install "html-webpack-plugin": "2.22.0",​ as devdependency. 

Then include it in the webpack config file:
  // ****   webpack.conf.babel.js   ****

const ProgressBarPlugin = require('progress-bar-webpack-plugin')
// 1. ++++ load the plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
const webpackValidator = require('webpack-validator')
const {getIfUtils, removeEmpty} = require('webpack-config-utils')

module.exports = env => {
  const {ifProd, ifNotProd} = getIfUtils(env)
  const config = webpackValidator({
    context: resolve('src'),
    entry: {..},
    output: {
      filename: ifProd('bundle.[name].[chunkhash].js', 'bundle.[name].js'),
      path: resolve('dist'),
      // 3. ---- can remove ↴
      // publicPath: '/dist/',
      pathinfo: ifNotProd(),
    },
    devtool: ifProd('source-map', 'eval'),
    module: {
      loaders: [
        ..],
    },
    plugins: removeEmpty([
      new ProgressBarPlugin(),
      ifProd(new webpack.optimize.CommonsChunkPlugin({
        name: 'vendor',
      })),
      // 2. ++++ use the plugin
      // we need it for env (dev, prod, and ok for test)
      new HtmlWebpackPlugin({
        template: './index.html',
        inject: 'head',
      }),
    ]),
  })
  if (env.debug) {
    console.log(config)
    debugger // eslint-disable-line
  }
  return config
}
we need to move index.html to the src directory for webpack to be able to find it. And also because it just became a source file that is generated. 
For this we also have to update how our npm start script works. 

We can remove the ​<script src="dist/bundle.js"></script>​ from index.html. 

Now the scrip tag is injected in index.html. 

You would also need to programatically copy your favicon to the dist directory now. 
Update the start script in package.json: 
  // ****   package.json    ****

// 1. ++++ add "dist"
"start": "http-server dist",

Q&A: why need to add the script tag to the head?

Otherwise you get the flash of unstyled content. 

​$ npm run dev​ → you get an error that webpack cannot use chunkhash need to use hash instead. 

This is why we have this above in the config:
  filename: ifProd('bundle.[name].[chunkhash].js', 'bundle.[name].js'),