Webpack chunking. No content appearing - chunks not loaded
Asked Answered
K

1

8

It's been a day I'm trying to solve this annoying but, I'm sure, simple issue. I am trying to divide my bundle.js into chunks to optimize website loading time.

Here is my webpack.config file :

module.exports = {
devServer: {
historyApiFallback: true
},
entry: {
index: ["./src/index.js"],
vendor: [
  "react",
  "react-dom",
  "react-redux",
  "react-router",
  "react-router-dom",
  "redux"
]
},
output: {
path: __dirname + '/public/views',
filename: "[name].js",
publicPath: "/views/"
},
module: {
loaders: [
  {
    test: /\.js$/,
    loader: "babel-loader",
    exclude: [/node_modules/, /pdfmake.js$/]
  },
  {
    test: /\.json$/,
    loader: "json-loader"
  }
]
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
  name: "vendor",
  filename: "vendor.js",
  minChunks: Infinity
}),
new webpack.optimize.CommonsChunkPlugin({
  name: "meta",
  chunks: ["vendor"],
  filename: "meta.js"
}),
new webpack.NamedModulesPlugin(),
new HtmlWebpackPlugin({
  title: "Deals",
  filename:  __dirname + "/views/index.ejs",
  template: __dirname + "/views/template.ejs",
  inject: false
}),
new PreloadWebpackPlugin({
  rel: "preload",
  as: "script",
  include: "all"
}),
new webpack.optimize.OccurrenceOrderPlugin(),
]
};

Here is my my simplified index.ejs, file created by running webpack code, result from template.ejs :

<!DOCTYPE html>
<html lang="en">

<head>

    <link href="/pace.css" rel="stylesheet" />
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=yes">
    <meta charset="utf-8">
    <link rel="stylesheet" href="/style.css">
    <link rel="canonical" href="http://verygoodprof.fr/" />
    <link rel="preload" as="script" href="/views/index.js">
    <link rel="preload" as="script" href="/views/vendor.js">
    <link rel="preload" as="script" href="/views/meta.js">
</head>

<body>

    <noscript>
        <a href="http://enable-javascript.com/">Javascript must me enabled to use this site.</a>
    </noscript>

    <div class="text-center root" id="root">

    </div>

</body>

</html>

Here, I see I've got my preloaded chunks, dynamically written, as wanted, and those chunks are in the correct folder, created after running the webpack code.

And here is my index.js file (React), stated to be the index entry in webpack.config file

ReactDOM.render(
  <Provider store={createStoreWithMiddleware(reducers)}>
    <AppInit>
      <BrowserRouter>
        <div style={{ height: "100%" }}>
          <ProfRegisteringModal />
          <Switch>
            {/* <Route path="/inscription/:user" component={Registering} />
            <Route path="/inscription" component={Registering} />
            <Route path="/connexion" component={SigningIn} />
            <Route path="/equipe" component={TeamPres} />
            <Route path="/" component={AppContainer} /> */}
            <Route
              path="/inscription/:user"
              getComponent={(location, callback) => {
                require.ensure(
                  [],
                  function(require) {
                    callback(
                      null,
                      require("./components/registering/registering_landing_page.js")
                    );
                  },
                  "registerChunk"
                );
              }}
            />
            <Route
              path="/inscription"
              getComponent={(location, callback) => {
                require.ensure(
                  [],
                  function(require) {
                    callback(
                      null,
                      require("./components/registering/registering_landing_page.js")
                    );
                  },
                  "registerChunk"
                );
              }}
            />
            <Route
              path="/connexion"
              getComponent={(location, callback) => {
                require.ensure(
                  [],
                  function(require) {
                    callback(
                      null,
                      require("./containers/registering/signing_in.js")
                    );
                  },
                  "signinChunk"
                );
              }}
            />
            <Route
              path="/equipe"
              getComponent={(location, callback) => {
                require.ensure(
                  [],
                  function(require) {
                    callback(null, require("./components/team_pres.js"));
                  },
                  "teampresChunk"
                );
              }}
            />
            <Route
              path="/"
              getComponent={(location, callback) => {
                require.ensure(
                  [],
                  function(require) {
                    callback(null, require("./containers/app_container.js"));
                  },
                  "appContainerChunk"
                );
              }}
            />
          </Switch>
        </div>
      </BrowserRouter>
    </AppInit>
  </Provider>,
  document.querySelector(".root")
);

The first thing I notice is that the chunks that are supposed to be build are correctly built for vendor and meta but not for my inner react components. But this is not the major issue, the fact is, when running the server locally, I can't see my react app at all. The index.ejs file is correctly loaded though when I check in the console.

Everything was working perfectly when using a simple bundle.js file with everything inside (no chunks). With an index.ejs pointing to it as

<script src="/views/bundle.js"></script>

Thank you very much for your help !

EDIT

This webpack.config file made it work (credits to @margaretkru):

module.exports = {
  devServer: {
    historyApiFallback: true
  },
  entry: {
    app:"./src/index.js",
    vendor: [
      "axios",
      "jquery",
      "react",
      "react-dom",
      "react-redux",
      "react-router",
      "react-router-dom",
      "redux"
    ]
  },
  output: {
    path: __dirname + '/public/views',
    filename: "[name].js",
    publicPath: "/views/"
  },
  module: {
    loaders: [
      {
        test: /\.js$/,
        loader: "babel-loader",
        exclude: [/node_modules/, /pdfmake.js$/]
      },
      {
        test: /\.json$/,
        loader: "json-loader"
      }
    ]
  },
  plugins: [
    new webpack.NamedModulesPlugin(),
    new HtmlWebpackPlugin({
      filename:  __dirname + "/views/index.ejs",
      template: __dirname + "/views/template.ejs",
      inject: 'body',
      chunks: ['vendor', 'app'],
      chunksSortMode: 'manual'
    }),
    new PreloadWebpackPlugin({
      rel: "preload",
      include: ["vendor", "app"]
    }),
    new webpack.optimize.OccurrenceOrderPlugin(),
  ]
};new webpack.optimize.CommonsChunkPlugin({
      name: "vendor",
      minChunks: Infinity
    }),
    new webpack.NamedModulesPlugin(),
    new HtmlWebpackPlugin({
      filename:  __dirname + "/views/index.ejs",
      template: __dirname + "/views/template.ejs",
      inject: 'body',
      chunks: ['vendor', 'app'],
      chunksSortMode: 'manual'
    }),
    new PreloadWebpackPlugin({
      rel: "preload",
      include: ["vendor", "app"]
    }),
    new webpack.optimize.OccurrenceOrderPlugin(),
  ]
};

The real issue was not the loaded scripts ordering but more that I was not actually loading the scripts after "pre-loading" them : The "inject" line of the HTMLWebpackPlugin helped since it injected those two lines :

<script src="/views/vendor.js"/> 
<script src="/views/app.js"/> 

in my index.ejs body

Kirwan answered 16/1, 2018 at 12:10 Comment(1)
so I see that the order of scripts that are loaded in index.ejs is incorrect, first should be loaded vendor.js and meta.js and only THEN index.js. But I don't understand why the order is wrong.Rafa
R
6

The order of the scripts that are loaded in index.ejs is incorrect. Now it is:

<link rel="preload" as="script" href="/views/index.js">
<link rel="preload" as="script" href="/views/vendor.js">

This means your index.js will be loaded before vendor.js causing your app to not appear at all. First all your app's dependencies should be loaded (vendor.js) and only then index.js since you need the dependencies to be present while executing your custom code. So it should be:

<link rel="preload" as="script" href="/views/vendor.js">
<link rel="preload" as="script" href="/views/index.js">

Edit:

As we figured out eventually, the issue was not in the order of the scripts, but due to the fact that the scripts weren't actually loaded in the html. For that they need to be added as chunks in HtmlWebpackPlugin and injected into index.html:

 new HtmlWebpackPlugin({
      title: "Deals",
      filename:  __dirname + "/views/index.ejs",
      template: __dirname + "/views/template.ejs",
      inject: 'body',
      chunks: ['vendor', 'index']
    })

Moreover, the configuration for the chunk meta was removed completely. All the rest of the configuration (including PreloadWebpackPlugin) was fine.

Rafa answered 16/1, 2018 at 13:1 Comment(7)
Thank you for your answer. The suggestion you made do not work though. The order is still not correct and, even if I change the order manually in index.ejs, the website is not loadedKirwan
do you see any errors in the console? could you post them if you do?Rafa
You're right, I could delete it ? I thought It was something to "bundle up" the other chunks?Kirwan
I am not sure what you mean :) I would go with this configuration, the simplest one, and test whether it works. If it doesnt try removing PreloadWebpackPlugin completely and see if something changes.Rafa
Let us continue this discussion in chat.Kirwan
Btw, the real issue was not the loaded scripts ordering but more that I was not actually loading the scripts after "pre-loading" them : The "inject" line of the HTMLWebpackPlugin helped since it injected those two lines : <script src="/views/vendor.js"/> <script src="/views/app.js"/> in my index.ejs bodyKirwan
Perfect! I updated the answer so that it can help others in case they encounter the same problem :)Rafa

© 2022 - 2024 — McMap. All rights reserved.