Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exclude react from bundle, and include it directly from the layout #1275

Closed
YoussefTaghlabi opened this issue Jul 22, 2015 · 29 comments
Closed
Labels

Comments

@YoussefTaghlabi
Copy link

I'm including React directly from my layout, and I would like webpack to exclude it from the bundle, and make react refers to the window global object React

I'm using the following:

{
        ...
        externals: {
            // Use external version of React
            "react": "React"
        },
        plugins: [
            new webpack.IgnorePlugin(/react/)
        ]
        ...
}

react gets excluded from the bundle, but client side I'm getting Uncaught Error: Cannot find module "react".

Any hints?

@YoussefTaghlabi YoussefTaghlabi changed the title Exclude react from bundle, and including it directly from the layout Exclude react from bundle, and include it directly from the layout Jul 22, 2015
@xusiyuan841028
Copy link

@YoussefTaghlabi Do you add <script> tag in your page?

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/0.13.3/react.js"></script>
<script type="text/javascript" src="/scripts/app.bundle.js"></script>

@YoussefTaghlabi
Copy link
Author

@xusiyuan841028 Yep, I do. But webpack is looking for react while on my page I have React.
Even having window.react = React right after loading my React script from CDN doesnt solve it.
Internally webpack is returning Uncaught Error: Cannot find module "react"

@IngwiePhoenix
Copy link

Solution:

  • Create a JS file called dummyReact.js

  • Fill that with these contents:

    module.exports = window.React;

  • Now add an alias in your config.webpack.js

    "react": "dummyReact.js"

  • Make sure your react is loaded before the WebPack bundle, and this should work.

In addition, you can also use the ProvidePlugin in conjunction with the dummyReact.js.

@YoussefTaghlabi
Copy link
Author

Thanks @IngwiePhoenix , the following did work indeed:

{
    ...
    resolve: {
        extensions: ['', '.js'],
        alias: {
            "react": "dummyReact.js"
        }
    },
    ...
    externals: {
        // Use external version of React
        "react": "React"
    },
    ...
    module: {
        noParse: [ "react" ]
    }
}

I had to remove new webpack.IgnorePlugin(/react/) for it to work as it seems webpack completely ignores anything that has to do with react.

My next question, how can I make use of ProvidePlugin in this use case?

@IngwiePhoenix
Copy link

Straight from the docs:

plugin ProvidePlugin
This plugin makes a module available as variable in every module. The module is required only if you use the variable.

Example: Make $ and jQuery available in every module without writing require("jquery").

new webpack.ProvidePlugin({
    $: "jquery",
    jQuery: "jquery",
    "window.jQuery": "jquery"
})

http://webpack.github.io/docs/shimming-modules.html#order-of-loaders

I use it exactly like that for jQuery and other modules. Use something like this for React, maybe.

new webpack.ProvidePlugin({
    React: "React", react: "React", "window.react": "React", "window.React": "React"
})

ProvidePlugin correctly resolves aliases too. So if you just add the plugin like os, itll "just work" :)

@YoussefTaghlabi
Copy link
Author

Perfect!
Thanks @IngwiePhoenix !

@IngwiePhoenix
Copy link

If you wonder how exactly this affects your code:

var $ = require("jquery");
reuqire("jquery-plugin");
require("other-plugin");

code();

// VS.

code();

Combining Alias and Provide, you are able to pretty much reduce the amount of "startup" code you write - effectively saving yourself lines of code (which in turn saves bytes with Uglify).

@IngwiePhoenix
Copy link

@YoussefTaghlabi Very welcome. =)

@YoussefTaghlabi
Copy link
Author

That works really well!!
One more thing, is there anyway we can get rid off dummyReact.js and somehow directly export window.React from webpack?
Now that it's working, I'm trying to make it graceful :)

@IngwiePhoenix
Copy link

I have no idea how to achieve this, sorry. :) But this is actually a graceful soltution imo. But if you ever find something, feel free to post it.

@YoussefTaghlabi
Copy link
Author

I sure will. Thanks for your help!

@sokra
Copy link
Member

sokra commented Jul 22, 2015

{
        ...
        externals: {
            // Use external version of React
            "react": "React"
        },
       ...
}

should be enough

@YoussefTaghlabi
Copy link
Author

either external or ProvidePlugin works in conjunction with alias.

@larrybotha
Copy link

#1275 (comment) should be in the docs, surely?

#1275 (comment) alone doesn't exclude React from my bundle.

@larrybotha
Copy link

Alright, so after playing around, my issue was that because ReactDOM was not being excluded, React was being bundled along with ReactDOM.

I needed to add ReactDOM as an external, in addition to React:

{
        ...
        externals: {
            // Use external version of React
            "react": "React",
            "react-dom": "ReactDOM"
        },
       ...
}

This is applicable since ReactDOM was made a separate dependency when mounting a component (React 0.14.x?).

I don't require any dummy-react.js to properly exclude React from my bundle - the externals property is sufficient.

@jide
Copy link

jide commented Jun 13, 2016

If you want to import your component as a library in another project already importing react instead of having a script tag, you must do this :

  externals: {
    react: 'umd react',
    'react-dom' : 'umd react-dom'
  }

@matt-mcdaniel
Copy link

matt-mcdaniel commented Jul 22, 2016

@larrybotha Does this mean that you are loading in ReactDOM externally, then? When I bundle per your comment I get "ReactDOM is not defined".

I'm having the same issue of ReactDOM requiring React, but would rather not have to fetch ReactDOM via CDN as it requires an extra roundtrip.

@ghost
Copy link

ghost commented Aug 10, 2016

I'm not sure what is going on, but I'm getting react included in my bundle despite explicit exclusion. I've tried every-which-way to stop this, but nothing seems to be working.

externals: {
    ...
    , 'react': 'React'
    , 'react-dom': 'ReactDOM'
    , 'react-dom/server': 'ReactDOMServer'
}

Yet when loading the app I still get two logs of Download the React DevTools for a better development experience:. The first happens from my externally included library, but the second points to webpack:///./~/react/lib/ReactDOM.js. I've lost a ton of time on this and would appreciate if anyone could point me in the right direction.

@bebraw
Copy link
Contributor

bebraw commented Aug 11, 2016

@aft-luke Could you open a question about that at Stack Overflow with more info? You could also check the resulting bundle through the analyze tool to see what's bringing React in. You could have something like require('React') somewhere.

@ghost
Copy link

ghost commented Aug 11, 2016

@bebraw thanks for the hint about the analyze tool. I was able to deduce that some of the react addons directly require react. CSSTransitionGroup and TransitionGroup, for example, were the culprits in this case. Changing my externals to this seemed to solve the problem:

...
        'react': 'React'
        , 'react-dom': 'ReactDOM'
        , 'react-dom/server': 'ReactDOMServer'
        , 'react/lib/ReactTransitionGroup': 'React.addons.TransitionGroup'
        , 'react/lib/ReactCSSTransitionGroup': 'React.addons.CSSTransitionGroup'

@bebraw
Copy link
Contributor

bebraw commented Aug 11, 2016

@aft-luke Great to hear!

@buildbreakdo
Copy link

So which is it..

  externals: {
    react: 'react',
    'react-dom' : 'react-dom',
  }

or

 externals: {
    react: 'React',
    'react-dom' : 'ReactDOM',
  }

Seeing the following in the console.

There is another module with an equal name when case is ignored.
This can lead to unexpected behavior when compiling on a filesystem with other case-semantic.
Rename module if multiple modules are expected or use equal casing if one module is expected.

Curious because different solutions seem to be working for different folks (presumed via the thumbs up) and I am not understanding why..

@buildbreakdo
Copy link

Okay so here is the solution that worked for me.. all credits to @Download

In webpack.config.js you can apparently pass an object..

  externals: {
        react: {
            root: 'React',
            commonjs2: 'react',
            commonjs: 'react',
            amd: 'react'
        },
        'react-dom': {
            root: 'ReactDOM',
            commonjs2: 'react-dom',
            commonjs: 'react-dom',
            amd: 'react-dom'
        }
    }

Download/react-mdl@755c568

@Download
Copy link

Download commented Sep 9, 2016

Thanks for the credits @buildbreakdo and glad it's working for you now.

Basically what is happening here is that there are different conventions on different systems:

On the web, libraries normally export to the window object. E.g. react to window.React, react-dom to ReactDOM etc. On Node on the other hand, we require the dependencies by package name.

Many people that are writing React applications are only using it on the client side. They set output.target to 'web' and only need to list the web method (grab from root) in their externals config. However if you target more than one environment, you need separate externals configs for each environment. React MDL targets the 4 environments listed above.

Edit:
For completeness, have a look at the docs on externals. Specifically, the table below it shows for each possible externals type what the resulting import statement looks like. So if you know where it should be coming from, you can work backwards to determine which externals type you need.

@jaredreich
Copy link

Just to add my two cents, this did the trick for me:

output: {
  ...
  libraryTarget: 'umd'
},

@neopostmodern
Copy link

Another two cents: If you feel like you've done all the right things and it still doesn't work, maybe it's actually not your fault but one of your dependencies includes a react addon (e.g. material-ui).
As they [Facebook] state in a bug report, they'll ignore the exclusion. But by adding even more webpack configuration you can work around that too:

externals: {
  ...
  'react-addons-transition-group': {
    commonjs: 'react-addons-transition-group',
    commonjs2: 'react-addons-transition-group',
    amd: 'react-addons-transition-group',
    root: ['React','addons','TransitionGroup']
  }
}

@AZaviruha
Copy link

If you are using lerna and "monorep", and building packages with webpack - this may help:
lerna/lerna#1049

alias: {
            react: path.resolve(__dirname, "node_modules/react")
        }

@ycscholes
Copy link

@jaredreich , Work for me

@abhchand
Copy link

I'm getting the below error when using noParse: ['react'] as noted above:

Invalid configuration object. Webpack has been initialised using a configuration object that does not match the API schema.
 - configuration[0].module.noParse[0]: The provided value "react" is not an absolute path!
   -> An absolute path, when the module starts with this path it is not parsed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests