How to use a library from a CDN in a Webpack project in production
Asked Answered
M

3

57

I'd like to use react.min.js from a CDN in production (e.g. https://unpkg.com/[email protected]/dist/react.min.js)

What is the best way to get Webpack to transform my import React from 'react' statements into const React = window.React instead of building node_modules/react into the bundle?

I've been doing it with resolve.alias like this:

In index.html:

<head>
  <script type="text/javascript" src="https://unpkg.com/[email protected]/dist/react.min.js"></script>
  <script type="text/javascript" src="/assets/bundle.js"></script>
</head>

In webpack.prod.config.js:

alias: {
  react$: './getWindowReact',
},

getWindowReact.js:

module.exports = window.React;

Note: In the old question I didn't realize that building React into a Webpack bundle with NODE_ENV=production would strip out the propTypes checks. One of the answers focuses on that.

Melonie answered 22/7, 2015 at 23:37 Comment(0)
E
43

In your webpack config you can use the externals option which will import the module from the environment instead of trying to resolve it normally:

// webpack.config.js
module.exports = {
  externals: {
    'react': 'React'
  }
  ...
};

Read more here: https://webpack.js.org/configuration/externals/

Esmerolda answered 22/7, 2015 at 23:41 Comment(5)
So it turns out I was wrong about React being built with Webpack, so this wouldn't work. But it appears to be the correct answer for using a module that is built with Webpack, so I'll accept this answer.Melonie
@Melonie I may not fully understand your comment, but what has been suggested here doesn't require that the library being loaded be built with Webpack. All this externals example does is tell webpack "if a module requests react, return window.React".Othilie
@Othilie yeah, sorry, I misunderstood webpack externals at the time...the docs for them are kind of awkward.Melonie
When you defined 'react': 'React' how does webpack knows from where to take it? I mean, there can be many versions or namesSizable
@Raz 'react': 'React' means "when I import from 'react', instead go looking in window.React". The key is the package name, the value is the global property name.Brett
W
18

I created https://github.com/mastilver/dynamic-cdn-webpack-plugin which is doing exactly that out of the box

const path = require('path')
const HTMLWebpackPlugin = require('html-webpack-plugin')
const DynamicCDNWebpackPlugin = require('dynamic-cdn-webpack-plugin')

module.exports = {
  entry: './main.js',
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader'
        }
      }
    ]
  },
  plugins: [
    new HTMLWebpackPlugin(),
    new DynamicCDNWebpackPlugin()
  ]
}

Will dynamically add unpkg.org urls and add the appropriate code in your bundle to load librairies from global

Wordage answered 4/7, 2017 at 15:6 Comment(10)
badass, that's a great idea! I use the https://github.com/kossnocorp/assets-webpack-plugin instead of the manifest plugin, do you know if your plugin will work with assets-webpack-plugin as well?Melonie
@Melonie I haven't tried, but I believe it does. If not feel free to raise an issue :)Wordage
@Wordage path is where the CDN link goes?Cowpox
@Cowpox no your links goes in the index.html created by html-webpack-pluginWordage
This works perfectly. There aren't many modules predefined in modules-cdn and it's not entirely obvious how to write a custom resolver to fill in the void - an example in the readme would be great! I went with if (modulePath == 'lodash') return { name: 'lodash', url: 'https://unpkg.com/lodash@' + version + '/lodash.min.js' }; and it seems to work so far.Clastic
@RomanStarkov Almost! :) It should be: if (modulePath == 'lodash') return { name: 'lodash', url: 'https://unpkg.com/lodash@' + version + '/lodash.min.js', version: version, var: '_' }; But that way it will replace module-to-cdn... Can I ask, why you didn't send a PR to module-to-cdn? You are not the only one, and I'm trying to understand how I can make it easier?Wordage
Yes I realised the var part soon after but couldn't edit comment anymore. I needed it right there and then, so PR would be too slow. After the fact... just a mix of lazy, busy, and feeling like it needs more work. Do packages have to be enumerated? I believe unpkg boasts predictable URLs, so you could just construct one and hope the package is there.Clastic
No, if you look at github.com/mastilver/module-to-cdn/blob/master/modules.json you can see that there is a lot of varieties (even for the same package). Even M. Jackson the author of unpkg had a look at it and he didn't mention anything. I need to find a way to make editing easier :)Wordage
@Wordage How does this plugin figure out which version of the module, say React, should be loaded? Do I still need to have the right version mentioned in package.json dependencies? Or can I omit React from package.json altogether?Metabolism
@Metabolism your app should work the same way with or without this plugin, so please add react in your package.json . What's happening is that it's requiring the package.json of your dependency and use the version define there (github.com/mastilver/dynamic-cdn-webpack-plugin/blob/…)Wordage
D
4

All the development-only portions of the React codebase, such as PropType checks, are guarded with:

if ("production" !== process.env.NODE_ENV) {
  ..
}

To strip these out from React in your own build, creating the equivalent of the minified React build in your own bundle, use DefinePlugin to replace references to process.env.NODE_ENV with "production".

plugins: [
  // ...
  new webpack.DefinePlugin({
    'process.env.NODE_ENV': JSON.stringify('production')
  }),
  new webpack.optimize.UglifyJsPlugin({
    compressor: {
      warnings: false
    }
  })
  // ...
],

Uglify's dead code elimination will then strip it all out, as it will detect that code wrapped with a "production" !== "production" check is unreachable.

Doited answered 23/7, 2015 at 0:25 Comment(1)
This is great to know too, though since my boss wants to use React from a CDN we'll stick with doing it that way.Melonie

© 2022 - 2024 — McMap. All rights reserved.