Chris Chris - 9 months ago 81
React JSX Question

Using React Router and Webpack 2 how to require external libraries only on certain routes?

Currently working



Using Webpack 2 and React Router v4 I have been able to set up working code splitting. There is an intermediate
<AsyncComponent>
that resolves the promise and returns the component (a pattern found on github issues).

An example set of routes below:

<Switch>
<Route
path="me"
render={(props) =>
<AsyncComponent promise={ require.ensure([], (require) => require('./modules/Profile'), 'profile') } props={props} />
}
/>
<Route
path="credit-card"
render={(props) =>
<AsyncComponent promise={ require.ensure([], (require) => require('./modules/CreditCard'), 'credit-card') } props={props} />
}
/>
</Switch>


Objective



I would like to extend that further, and for certain routes only, load in additional libraries. In the example above, I would like to fetch the StripeJS (https://js.stripe.com/v2/) library only when on the credit card route.

I want to emphasise that I can load Stripe directly into the footer and all works normally. There are multiple libraries, I'm using Stripe as an easy to digest example.

Attempted



The following has been attempted to little success:


  • Marking the lib external within the webpack config. This (correctly) flags the library as being external and won't attempt to bundle it during the resolve sequence of a require. However the expectation is that the library is manually brought in.

  • I have played with the idea of using a pseudo script loader (when hitting that route, manually create a
    <script>
    with a
    src
    attribute, wait for it to load, then let the component do its thing. This works ok, but is really horrible from a maintainability (if two or more libraries are needed, I then need to replicate the clumsy manual
    script
    load) point of view and seems to work against the webpack "way".



Relevant parts of the config



const core = [
'lodash',
'react',
'react-dom',
'axios',
'react-router-dom',
];

const config = {
context: path.resolve(__dirname, './ts_build'),
node: {
fs: "empty"
},
entry: {
app: './app.js',
core: core,
},
output: {
filename: '[name].js',
chunkFilename: '[name].[id].chunk.js',
path: path.resolve(__dirname, './../../public'),
publicPath: 'http://example.org/',
},
resolve: {
modules: [
path.resolve('./src'),
],
},
plugins: [
new webpack.optimize.CommonsChunkPlugin({
names: ['core'],
minChunks: Infinity,
}),
new webpack.NamedModulesPlugin(),
],
};

Answer Source

https://github.com/hinok/webpack2-react-router-code-splitting

Repository contains webpack2 + react-router v4 + implemented code splitting using dynamic import() and loading external libraries on demand only once by using loadjs. As example it loads Stripe.js for specific route.

In repository you can find two ways to do code splitting

It's based on official Webpack documentation and gist by Andrew Clark

While preparing that repository I found that @Chris would like to load Stripe.js only for certain routes which is hosted on external CDN. Since then I've been thinking about the best way to use also AMD modules and avoid leaking global variables but this is a bit tricky because each AMD module could have different wrapper, by saying wrapper I mean something like:

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define(['b'], function (b) {
            return (root.amdWebGlobal = factory(b));
        });
    } else {
        root.amdWebGlobal = factory(root.b);
    }
}(this, function (b) {
    return {};
}));

I could say that UMD wrapper is kind of standard but sometimes people prefer less opinionated wrappers or they just don't care about other environments. I'm mentioning it because in the git history, you can find commit called Amdify which was more proof of concept how AMD env could be simulated. Finally I decided to remove it because it's far from ideal and it doesn't cover all usages and edge cases.

I couldn't find any information about integrating external AMD modules or using AMD modules in some way with webpack. On the contrary I found that it will just not work

@mc-zone you should be able to load AMD modules with script.js. But this has nothing to do with webpack, it just works because AMD was designed for this. Thus you won't be able to use webpack features inside these AMD modules. webpack requires static analysis on build time.

by @jhns see on github

webpack doesn't handle script loading. Use an separate library for this. i. e. https://github.com/ded/script.js/

by @sokra see on github

If it's not possible you should not require() it but load it via a script-loader like script.js.

by @jhns see on github

Related issues on Github: