23

1. ProvidePlugin()

Looks like a commonly used approach. There is a gist about it, showcasing how to include whatwg-fetch polyfill into a Webpack build. Lots of answers on StackOverflow use it here and here.

new webpack.ProvidePlugin({
  '$': 'jquery',
  'jQuery': 'jquery',
  'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
})

Pros

  • It works. (please update this list if I am missing something)

Cons

  • Need to keep track of global libraries in the Webpack config.

2. entry: [...]

I was a little surprised by this approach when I discovered it in this gist but it works just as well.

entry: [
    'babel-polyfill',
    'whatwg-fetch',
    'jquery',
    'webpack-hot-middleware/client',
    path.join(process.cwd(), 'app/app.js')
],

Pros

  • It works.
  • I can drop ProvidePlugin() entirely.

Cons

  • Need to keep track of global libraries in the Webpack config.

3. Top-level import

This one is very straightforward, see this app.js example. This file is an entry-point to a React application.

/**
 * app.js
 */

import 'whatwg-fetch';
import 'babel-polyfill';
import 'jquery';

Pros

  • It works just as well.
  • Easy add/remove. No need to touch the Webpack config.

Cons

  • Doesn't look like this approach alone will work with jQuery plugins, e.g. bootstrap.js.

Observation: Between all three approaches, I haven't noticed any changes in the bundle size.

Is there one recommended way of handling global libraries with Webpack (and React)? Would any of these solutions cause a problem down the road with server-side rendering?

Thanks!

Community
  • 1
  • 1
Sahat Yalkabov
  • 29,198
  • 40
  • 103
  • 171

2 Answers2

24

I wouldn't recommend exposing libraries as global unless you really do need it, i.e. the point of a module system is to explicitly declare dependencies, e.g.

// app.js
import $ from 'jquery';
$.ajax(...);

If you absolutely do need jQuery on the global because a third party script requires it on your page or maybe for debugging in the console then here's some information regarding the approaches you've listed:

ProvidePlugin

The ProvidePlugin won't expose jQuery on the global and is really designed to fix third-party modules which incorrectly rely on the presence of a global module so I wouldn't recommend this, e.g.

// app.js
$.ajax(...);

Is effectively transpiled into:

// app.js
require('jquery').ajax(...);

Entry & Top level import

These approaches won't work for a regular UMD module such as jQuery as jQuery is smart enough not to expose itself on the global when being loaded by a commonjs / amd / es6 aware loader.

These two approaches are however ideal for modules with side effects such as the babel-polyfill / whatwg-fetch because they don't export anything, they inherently mutate the global environment.


My recommendation for jQuery is therefore to use the expose-loader which is designed to expose a modules export globally, e.g.

// webpack.config.js
{
    module: {
        loaders: [
            test: require.resolve('jquery'),
            loader: 'expose-loader?jQuery!expose-loader?$'
        ]
    }
}

You then still need to import it in your app code:

// app.js
import $ from 'jquery';
$.ajax(...)

But it's available on the global for other scripts on the page to access if absolutely necessary:

// console
window.$
window.jQuery

NOTE: Technically you could just import 'jquery' once in your entry point when using the expose loader and then rely on the global in other modules.

As I say however, it isn't really advisable to expose a module if you don't need to, even if you happen to currently use it in every other module.

riscarrott
  • 5,024
  • 1
  • 29
  • 36
0

Just find out that provided lib will be overwritten, if you include several packs (entry points) in one page when you use ProvidePlugin. Examples are for RoR and Webpacker, but i assume it doesn't matter.

For example you have in your layout:

  javascript_pack_tag 'application',
                      'metronic'

And in configs you have:

  environment.plugins.append('Provide', new webpack.ProvidePlugin({
      $: 'jquery',
      jQuery: 'jquery',
      "window.jQuery": "jquery",
      "window.$": "jquery",
      _: 'underscore',
      Handlebars: 'handlebars'
  }));

If in application.js you require libraries, which mutate JQuery, you will loose all this mutation in metronic.js and in browser, even if you expose JQuery with expose-loader.

Correct me if i am wrong, probably ProvidePlugin just imports libs in each entry point.

01ghost13
  • 108
  • 1
  • 12