Define global variable with webpack

224,610

Solution 1

There are several way to approach globals:


1. Put your variables in a module.

Webpack evaluates modules only once, so your instance remains global and carries changes through from module to module. So if you create something like a globals.js and export an object of all your globals then you can import './globals' and read/write to these globals. You can import into one module, make changes to the object from a function and import into another module and read those changes in a function. Also remember the order things happen. Webpack will first take all the imports and load them up in order starting in your entry.js. Then it will execute entry.js. So where you read/write to globals is important. Is it from the root scope of a module or in a function called later?

config.js

export default {
    FOO: 'bar'
}

somefile.js

import CONFIG from './config.js'
console.log(`FOO: ${CONFIG.FOO}`)

Note: If you want the instance to be new each time, then use an ES6 class. Traditionally in JS you would capitalize classes (as opposed to the lowercase for objects) like
import FooBar from './foo-bar' // <-- Usage: myFooBar = new FooBar()


2. Use Webpack's ProvidePlugin.

Here's how you can do it using Webpack's ProvidePlugin (which makes a module available as a variable in every module and only those modules where you actually use it). This is useful when you don't want to keep typing import Bar from 'foo' again and again. Or you can bring in a package like jQuery or lodash as global here (although you might take a look at Webpack's Externals).

Step 1. Create any module. For example, a global set of utilities would be handy:

utils.js

export function sayHello () {
  console.log('hello')
}

Step 2. Alias the module and add to ProvidePlugin:

webpack.config.js

var webpack = require("webpack");
var path = require("path");

// ...

module.exports = {

  // ...

  resolve: {
    extensions: ['', '.js'],
    alias: {
      'utils': path.resolve(__dirname, './utils')  // <-- When you build or restart dev-server, you'll get an error if the path to your utils.js file is incorrect.
    }
  },

  plugins: [

    // ...

    new webpack.ProvidePlugin({
      'utils': 'utils'
    })
  ]  

}

Now just call utils.sayHello() in any js file and it should work. Make sure you restart your dev-server if you are using that with Webpack.

Note: Don't forget to tell your linter about the global, so it won't complain. For example, see my answer for ESLint here.


3. Use Webpack's DefinePlugin.

If you just want to use const with string values for your globals, then you can add this plugin to your list of Webpack plugins:

new webpack.DefinePlugin({
  PRODUCTION: JSON.stringify(true),
  VERSION: JSON.stringify("5fa3b9"),
  BROWSER_SUPPORTS_HTML5: true,
  TWO: "1+1",
  "typeof window": JSON.stringify("object")
})

Use it like:

console.log("Running App version " + VERSION);
if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");

4. Use the global window object (or Node's global).

window.foo = 'bar'  // For SPA's, browser environment.
global.foo = 'bar'  // Webpack will automatically convert this to window if your project is targeted for web (default), read more here: https://webpack.js.org/configuration/node/

You'll see this commonly used for polyfills, for example: window.Promise = Bluebird


5. Use a package like dotenv.

(For server side projects) The dotenv package will take a local configuration file (which you could add to your .gitignore if there are any keys/credentials) and adds your configuration variables to Node's process.env object.

// As early as possible in your application, require and configure dotenv.    
require('dotenv').config()

Create a .env file in the root directory of your project. Add environment-specific variables on new lines in the form of NAME=VALUE. For example:

DB_HOST=localhost
DB_USER=root
DB_PASS=s1mpl3

That's it.

process.env now has the keys and values you defined in your .env file.

var db = require('db')
db.connect({
  host: process.env.DB_HOST,
  username: process.env.DB_USER,
  password: process.env.DB_PASS
})

Notes

Regarding Webpack's Externals, use it if you want to exclude some modules from being included in your built bundle. Webpack will make the module globally available but won't put it in your bundle. This is handy for big libraries like jQuery (because tree shaking external packages doesn't work in Webpack) where you have these loaded on your page already in separate script tags (perhaps from a CDN).

Solution 2

I was about to ask the very same question. After searching a bit further and decyphering part of webpack's documentation I think that what you want is the output.library and output.libraryTarget in the webpack.config.js file.

For example:

js/index.js:

var foo = 3;
var bar = true;

webpack.config.js

module.exports = {
   ...
   entry: './js/index.js',
   output: {
      path: './www/js/',
      filename: 'index.js',
      library: 'myLibrary',
      libraryTarget: 'var'
   ...
}

Now if you link the generated www/js/index.js file in a html script tag you can access to myLibrary.foo from anywhere in your other scripts.

Solution 3

Use DefinePlugin.

The DefinePlugin allows you to create global constants which can be configured at compile time.

new webpack.DefinePlugin(definitions)

Example:

plugins: [
  new webpack.DefinePlugin({
    PRODUCTION: JSON.stringify(true)
  })
  //...
]

Usage:

console.log(`Environment is in production: ${PRODUCTION}`);

Solution 4

You can use define window.myvar = {}. When you want to use it, you can use like window.myvar = 1

Solution 5

DefinePlugin doesn't actually define anything. What it does is replace variables that exist in your bundle code. If the variable doesn't exist in your code, it will do nothing. So it doesn't create global variables.

In order to create a global variable, write it in your code:

window.MyGlobal = MY_GLOBAL; 

And use DefinePlugin to replace MY_GLOBAL with some code:

new webpack.DefinePlugin({
    'MY_GLOBAL': `'foo'`,
    // or
    'MY_GLOBAL': `Math.random()`,
}),

Then your output JS will be like this:

window.MyGlobal = 'foo';
// or
window.MyGlobal = Math.random(); 

But MY_GLOBAL will never actually exist at runtime, because it is never defined. So that's why DefinePlugin has a misleading name.

Share:
224,610

Related videos on Youtube

Teneff
Author by

Teneff

Fatal error: imagination limit exceeded

Updated on April 12, 2022

Comments

  • Teneff
    Teneff about 2 years

    Is it possible to define a global variable with webpack to result something like this:

    var myvar = {};
    

    All of the examples I saw were using external file require("imports?$=jquery!./file.js")

  • nb1987
    nb1987 over 7 years
    +1. I am re-structuring an app to leverage webpack as a build tool and this initially didn't work for me because I hadn't included any reference to my own utils namespace in the target file - initially I had just put a breakpoint in the browser's source window and I kept puzzling over why utils wasn't defined. Finally I discovered that webpack (rather smartly) only includes a module if its namespace is referenced at least once. Therefore, once I did preface one of the target file's utility functions with utils, the module was included.
  • prograhammer
    prograhammer over 7 years
    Yup, only where you use it, does it make it available. I put that in the first line of the answer, but I made a slight adjustment so maybe it reads better. Thanks for the +1!
  • Brian Cannard
    Brian Cannard about 7 years
    Note that ProvidePlugin actually loads modules and if you just need a variable, it doesn't work that way. Just use externals instead if you need to create a global variable. Example: externals: { 'webpackVariables': `{ serverUrl: '${ env.server }', cordovaBuild: '${ env.cordova }', }`, }, Then use it as const webpackVariables = require('webpackVariables');
  • prograhammer
    prograhammer about 7 years
    It loads the module and sets it to the variable wherever you used the variable. That was the goal here. Because global variables are available everywhere, and that may not be exactly what you want.
  • Routhinator
    Routhinator almost 7 years
    This doesn't work with EMCAScript 6. Produces error with var window.CKEDITOR_BASEPATH = {}; Error is "Unexpected Token" after window.
  • Quoc-Anh Nguyen
    Quoc-Anh Nguyen almost 7 years
    Sorry. I have just updated my answer. You should var keyword. window.CKEDITOR_BASEPATH = {};
  • Routhinator
    Routhinator almost 7 years
    This works, unfortunately the issue I'm having is that I need it to be loaded into the bundle before CKEditor, however Webpack insists on putting it after no matter where I place it into my imports/js. :/
  • Routhinator
    Routhinator almost 7 years
    Any example of how to set a global window string variable (not module) and ensure the line is packed near the top of the bundle?
  • prograhammer
    prograhammer over 6 years
    Yup @Scofield good docs link, and that's the way to do it for jquery which is resolved from node_modules. For my own module I had to give it an alias first before using with ProvidePlugin.
  • Roy
    Roy over 6 years
    how do I update global variables in one module then auto update in other module like normal global variables in js ? thanks
  • prograhammer
    prograhammer over 6 years
    @Roy Not sure what you mean? Do what I explained in my answer and you can update the global state anywhere. For example, say in utils.js you have foo: null, then in component-a.js you do utils.foo = 'bar'. Then in component-b.js you do console.log(utils.foo) // bar. Just don't do import utils from '../utils in the component files or that will get you a new utils object. Not sure why I got a downvote just now.
  • prograhammer
    prograhammer over 6 years
    @Roy as an alternative, you could import the utils module (as an es6 module) into your main entry point main.js. Then in main.js do utils.foo = 'bar'. Then all the other functions ran after this point in main.js (those modules WILL need to do import utils from '../utils' also) will have 'bar'. Remember, must be ran after main.js. Webpack will grab all the imports first (in order starting from main.js) then actually run main.js. It's a little confusing, so just experiment and you'll get it.
  • Roy
    Roy over 6 years
    @prograhammer thanks, finally I use window.globalVars in real world, but I'll try to understand your update.
  • prograhammer
    prograhammer over 6 years
    @Roy see update once more. I added a few more bullet points. Read 1, 2, 5.
  • knaos
    knaos over 6 years
    And do you know how can I use this approach with TypeScript? There if you use an undeclared variable it throws an error...
  • Liam
    Liam over 6 years
    I've asked a related question if you'd care to have a look and/or provide an answer that'd be great
  • knaos
    knaos over 6 years
    @prograhammer Actually, I already found the solution. In the root of your application, usually where your tsconfig.json is, you need to add a definition file named global.d.ts. In it you can declare global variables, like this: declare const isProduction: bool; For reference, check this out typescriptlang.org/docs/handbook/declaration-files/templates‌​/…
  • LondonAppDev
    LondonAppDev almost 6 years
    I think this is missing export { foo } from the index.js?
  • Joseph238
    Joseph238 about 5 years
    You can also use script-loader. This is handy if the module doesn't export anything so you must execute it in global context.
  • RVCoder
    RVCoder about 5 years
    myLibrary gives undefined in another file in my case. Can you please help me
  • The Dead Man
    The Dead Man over 4 years
    Hi bro can you please help me out on How to update multiple bundled js files , here is my thread with 100 Bounty stackoverflow.com/questions/60030506/…
  • Lemmings19
    Lemmings19 almost 4 years
    @prograhammer thank you for the extensive list of options. I am working on a legacy project which is now utilizing webpack. Is it possible to simply turn off the functionality that changes the scope of all functions and variables? Rather than modifying thousands of lines of code to suit how webpack wants to do things, is it possible to make webpack suit the old code structure? (there are numerous top level functions and variables that need to remain globally accessible)
  • Matthias
    Matthias almost 3 years
    DefinePlugin is deceptive. It doesn't create global variables. Rather, it replaces code strings with other code strings, and it only operates on your bundle. So for people who need a real window.* variable e.g. for external scripts, DefinePlugin won't work.
  • Loenix
    Loenix over 2 years
    Definitively not clear and too much complicated. In a module, I am declaring export const routingService = new RoutingService();. I would like to access routingService from everywhere in the document, How to do that ?
  • prograhammer
    prograhammer over 2 years
    @Loenix not sure what is not clear. Your code is just like the foo example. Just import { routingService } from '...' in any other module and they will all share that same instance of RoutingService. Easy peasy.
  • eternalStudent
    eternalStudent about 2 years
    Is it possible to do the same for replacing a global var with a relative import in the dest?
  • eternalStudent
    eternalStudent about 2 years
    can it work for replacing the window.* var to a relative import of a static file in bumbled dest?