【问题标题】:Define global variable with webpack使用 webpack 定义全局变量
【发布时间】:2016-10-06 00:17:07
【问题描述】:

是否可以使用 webpack 定义一个全局变量来产生如下结果:

var myvar = {};

我看到的所有例子都是使用外部文件require("imports?$=jquery!./file.js")

【问题讨论】:

    标签: javascript webpack webpack-2 webpack-3


    【解决方案1】:

    DefinePlugin 实际上并没有定义任何东西。它的作用是替换捆绑代码中存在的变量。如果您的代码中不存在该变量,它将什么也不做。所以它不会创建全局变量。

    为了创建一个全局变量,把它写在你的代码中:

    window.MyGlobal = MY_GLOBAL; 
    

    并使用DefinePlugin 将MY_GLOBAL 替换为一些代码:

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

    那么你的输出JS会是这样的:

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

    但是MY_GLOBAL 在运行时永远不会真正存在,因为它从未被定义。这就是 DefinePlugin 有一个误导性名称的原因。

    【讨论】:

      【解决方案2】:

      有几种方法可以处理全局变量:

      1. 将变量放入模块中。

      Webpack 只对模块进行一次评估,因此您的实例仍然是全局的,并在模块之间进行更改。因此,如果您创建类似 globals.js 的内容并导出所有全局变量的对象,那么您可以import './globals' 并读/写这些全局变量。您可以导入一个模块,从一个函数对对象进行更改,然后导入另一个模块并在函数中读取这些更改。还要记住事情发生的顺序。 Webpack 将首先获取所有导入并加载它们,从您的entry.js 开始。然后它将执行entry.js。所以你在哪里读/写全局变量很重要。它是来自模块的根范围还是在稍后调用的函数中?

      config.js

      export default {
          FOO: 'bar'
      }
      

      somefile.js

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

      注意:如果您希望实例每次都是new,请使用ES6 class。传统上,在 JS 中,您会将类(而不是对象的小写)大写,例如
      import FooBar from './foo-bar' // <-- Usage: myFooBar = new FooBar()

      1. Webpack 的ProvidePlugin

      以下是使用 Webpack 的 ProvidePlugin 的方法(它使模块在每个模块中作为变量可用,并且仅在您实际使用它的那些模块中可用)。当您不想一遍又一遍地输入import Bar from 'foo' 时,这很有用。或者你可以在这里引入一个像 jQuery 或 lodash 这样的包作为全局包(尽管你可以看看 Webpack 的 Externals)。

      步骤 1) 创建任何模块。例如,一组全局实用程序会很方便:

      utils.js

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

      步骤 2) 为模块命名并添加到 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'
          })
        ]  
      
      }
      

      现在只需在任何 js 文件中调用 utils.sayHello() 即可。如果您在 Webpack 中使用,请确保重新启动您的开发服务器。

      注意:不要忘记告诉你的 linter 全局,这样它就不会抱怨了。例如,查看我的answer for ESLint here

      1. 使用 Webpack 的 DefinePlugin

      如果你只是想为你的全局变量使用带有字符串值的 const,那么你可以将此插件添加到你的 Webpack 插件列表中:

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

      像这样使用它:

      console.log("Running App version " + VERSION);
      if(!BROWSER_SUPPORTS_HTML5) require("html5shiv");
      
      1. 使用全局窗口对象(或Node的全局)

      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/
      

      你会看到这通常用于 polyfill,例如:window.Promise = Bluebird

      1. 使用像dotenv这样的包

      (对于服务器端项目)dotenv 包将采用本地配置文件(如果有任何密钥/凭据,您可以将其添加到您的 .gitignore 中)并将您的配置变量添加到 Node 的 process.env 对象。

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

      在项目的根目录中创建一个.env 文件。以NAME=VALUE 的形式在新行中添加特定于环境的变量。例如:

      DB_HOST=localhost
      DB_USER=root
      DB_PASS=s1mpl3
      

      就是这样。

      process.env 现在拥有您在.env 文件中定义的键和值。

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

      注意事项:

      关于 Webpack 的 Externals,如果您想排除某些模块不包含在构建的包中,请使用它。 Webpack 将使模块全局可用,但不会将其放入您的包中。这对于像 jQuery 这样的大型库很方便(因为摇树外部包 doesn't work in Webpack),您已经在单独的脚本标签(可能来自 CDN)中将这些加载到页面上。

      【讨论】:

      • +1。我正在重新构建一个应用程序以利用 webpack 作为构建工具,这最初对我不起作用,因为我没有在目标文件中包含对我自己的 utils 命名空间的任何引用 - 最初我只是放了一个断点在浏览器的源代码窗口中,我一直对为什么没有定义 utils 感到困惑。最后我发现 webpack(相当巧妙地)包含一个模块,如果它的命名空间至少被引用一次。因此,一旦我用utils 作为目标文件的一个实用函数的前言,模块被包含在中。
      • 是的,只有在您使用它的地方,它才会使其可用。我把它放在答案的第一行,但我做了轻微的调整,所以它可能读起来更好。感谢您的 +1!
      • 请注意,ProvidePlugin 实际上会加载模块,如果您只需要一个变量,它就不会那样工作。如果您需要创建全局变量,只需使用externals。示例:externals: { 'webpackVariables': `{ serverUrl: '${ env.server }', cordovaBuild: '${ env.cordova }', }`, }, 然后将其用作const webpackVariables = require('webpackVariables');
      • 您知道如何将这种方法与 TypeScript 一起使用吗?如果你使用一个未声明的变量,它会抛出一个错误......
      • @prograhammer 实际上,我已经找到了解决方案。在应用程序的根目录中,通常是 tsconfig.json 所在的位置,您需要添加一个名为 global.d.ts 的定义文件。您可以在其中声明全局变量,如下所示:declare const isProduction: bool; 作为参考,请查看typescriptlang.org/docs/handbook/declaration-files/templates/…
      【解决方案3】:

      我通过将全局变量设置为与它们最相关的类的静态属性解决了这个问题。在 ES5 中是这样的:

      var Foo = function(){...};
      Foo.globalVar = {};
      

      【讨论】:

        【解决方案4】:

        我正要问同样的问题。在进一步搜索并解密 webpack 文档的一部分后,我认为您想要的是 webpack.config.js 文件中的 output.libraryoutput.libraryTarget

        例如:

        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'
           ...
        }
        

        现在,如果您将生成的 www/js/index.js 文件链接到 html 脚本标记中,您可以从其他脚本中的任何位置访问 myLibrary.foo

        【讨论】:

        • 我认为 export { foo } 缺少 index.js
        • myLibrary 在我的情况下在另一个文件中给出 undefined 。你能帮帮我吗
        【解决方案5】:

        您可以使用定义window.myvar = {}。 想用的时候可以用likewindow.myvar = 1

        【讨论】:

        • 这不适用于 EMCAScript 6。使用var window.CKEDITOR_BASEPATH = {}; 产生错误window. 之后的错误是“意外令牌”
        • 对不起。我刚刚更新了我的答案。你应该var关键字。 window.CKEDITOR_BASEPATH = {};
        • 这行得通,不幸的是我遇到的问题是我需要在 CKEditor 之前将它加载到包中,但是无论我将它放在我的导入/js 中的哪个位置,Webpack 都坚持将它放在之后. ://
        【解决方案6】:

        使用DefinePlugin

        DefinePlugin 允许您创建全局常量 在编译时配置。

        new webpack.DefinePlugin(definitions)
        

        示例:

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

        用法:

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

        【讨论】:

        • DefinePlugin 具有欺骗性。它不会创建全局变量。相反,它用其他代码字符串替换代码字符串,并且它只在您的捆绑包上运行。因此,对于需要真正的 window.* 变量的人来说,例如对于外部脚本,DefinePlugin 将不起作用。
        猜你喜欢
        • 1970-01-01
        • 2017-10-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-07-02
        相关资源
        最近更新 更多