公开通过 webpack 捆绑的 javascript 全局变量

Expose javascript globals bundled via webpack

提问人:Loufs 提问时间:6/23/2017 最后编辑:CommunityLoufs 更新时间:11/4/2022 访问量:11173

问:

问题

我觉得这应该比实际更直接。我需要从前端访问我所有的 javascript 库,因为我要将其集成到旧系统中,所以我无法从前端调用。捆绑文件全局范围内的所有内容都必须可从前端页面的全局范围访问,并通过标记导入它们。require("bundle.js");<script>

所以我需要改变旧的:

<script src="js/jquery.js"></script>
<script src="js/silly.js"></script>
<script>
    $(silly()); // Some function in silly.js's global scope
</script>

到新的:

<script src="js/bundle.js"></script>
<script>
    $(silly()); // Some function in silly.js's global scope
</script>

我尝试过的事情

  1. expose-loader:如果我没有 100 个我不想明确告诉它查看的全局变量,这将完全有效 为。

  2. 提供插件:只有真正让库才能看到其他库。我也无法用我当前的设置显式写入我需要的所有全局变量(不断添加更多全局变量)。

我需要什么

因此,为了更清楚起见,我需要我的看起来像以下选项之一:webpack.config.js

// Everything is wrapped in module.exports and other irrelevant things
plugins: [
         new StaticLibraryMergerSuperNeatPlugin("js/*.js")
]
// ...

艺术

rules: [
        {
            test: /\.js$/,
            use: [
                "neat-merging-cool-loader",
                "babel-loader"]
            
        }
]
// ...

我做错了吗?

我缺少一个明显的解决方案吗?

Tl;Dr:我如何从捆绑的js文件制作全局变量,当通过<script src=“js/bundle.js”></script>在前端html页面上导入时,可以暴露在全局范围内?

顺便说一句:如果有人是 webpack 的传奇人物并且知道为什么这是一种糟糕的方法,请在下面发布简短的解释,以便我修复我的生活。

JavaScript Webpack 前端

评论

3赞 mhodges 6/23/2017
您可以在捆绑的单个脚本中的对象上声明它们。您也可以使用单个文件作为入口点,并说 ,等等。无论如何,这就是我所做的。windowwindow["silly"] = require("silly.js")

答:

16赞 mhodges 6/23/2017 #1

这是我如何在自己的网站中执行此操作的示例。我不确定这是否是唯一的方法,甚至是最好的方法,但它干净、简单,而且对我有用。

重要的旁注 - 在窗口上声明内容时使用,因为当您运行时,它会丑化任何非字符串,因此如果您将其定义为 ,它可以更改为类似的东西,而您的代码的其余部分不知道它是什么。用括号表示法将其声明为字符串将强制 webpack 保持名称不变,以便您可以从任何具有相同名称的地方访问它。window["propName"]webpack -pwindow.propNames.c

site.ts(可以.js,没关系)

/*************************/
/*** JQUERY + JQUERYUI ***/
/*************************/
/* var declaration for typescript - not needed if not using .ts */
declare var $:JQueryStatic; declare var jQuery:JQueryStatic;
window["$"] = window["jQuery"] = require("jquery");
require("jquery-ui/effects/effect-slide");
require("jquery-ui/widgets/autocomplete");
require("jquery-ui/widgets/button");
require("jquery-ui/widgets/datepicker");
require("jquery-ui/widgets/tooltip");
/*************************/
/* END JQUERY + JQUERYUI */
/*************************/

/***************/
/*** ANGULAR ***/
/***************/
/* var declaration for typescript - not needed if not using .ts */
declare var angular:ng.IAngularStatic;
window["angular"] = require("angular");
require("angular-sanitize");
/***************/
/* END ANGULAR */
/***************/

/************************/
/*** MISC THIRD-PARTY ***/
/************************/
window["moment"] = require("moment");
window["saveAs"] = require("FileSaver").saveAs;
window["JSZip"] = require("jszip");
/************************/
/* END MISC THIRD-PARTY */
/************************/

/* var declaration for typescript - not needed if not using .ts */
declare var globals:Globals;
window["globals"] = require("./globals");

Layout.html(加载到每一页)

.....
<script src="/dist/scripts/site.bundle.js"></script>
.....

webpack.config.js

var path = require('path');
var resolve = path.resolve;
var AssetsPlugin = require('assets-webpack-plugin');
var WebpackCleanupPlugin = require("webpack-cleanup-plugin");
'use strict';

var babelOptions = {
    "presets": [
      [
        "es2015",
        {
            "modules": false
        }
      ],
      "es2016"
    ]
};

module.exports = [{
    cache: true,
    context: resolve('Scripts'),
    devtool: "source-map",
    entry: {
        site: './site.ts',
    },
    output: {
        path: path.resolve(__dirname, './dist/scripts'),
        filename: '[name].bundle.js',
    },
    module: {
        rules: [{
            test: /\.ts$/,
            exclude: /node_modules/,
            use: [
              {
                  loader: 'babel-loader',
                  options: babelOptions
              },
              {
                  loader: 'ts-loader'
              }
            ]
        }, {
            test: /\.js$/,
            exclude: /node_modules/,
            use: [
              {
                  loader: 'babel-loader',
                  options: babelOptions
              }
            ]
        }]
    },
    plugins: [
        new AssetsPlugin({ path: path.resolve(__dirname, './dist/assets') }),
        new WebpackCleanupPlugin({})
    ],
}];

评论

0赞 Loufs 6/24/2017
感谢您的回答和 +1,因为这完全有效。但是,我不知道我需要的所有模块,因此我需要在运行时动态地进行。不幸的是,有太多的不断添加让我无法使用这个技巧。webpackwindow['module'] = require('module.js')
0赞 mhodges 6/24/2017
@Lofus 如果你不断地向你的全局范围添加东西,你可能做错了什么。仅将真正的全局内容添加到全局捆绑包中,然后对应用程序中的不同模块使用不同的入口点。全局范围不应知道应用程序中每个页面所需的单个内容。每个文件都应该知道它需要什么,并且只知道它需要什么,并且应该自己加载其依赖项。Webpack 将消除任何重复的重复需求,因此需要消除。
1赞 mhodges 6/24/2017
@Lofus:好吧,你的问题非常明确地指出了“当与 webpack 捆绑在一起时如何使 JavaScript 全球化”,我的回答解决了这个问题。这可能不是你对这个问题的意图,但它现在的措辞方式是一个有效的问题,对未来的用户有用(甚至比你的特定用例更有用)。
1赞 Loufs 6/24/2017
无法反驳这种:)
1赞 Grant 2/2/2022
你实际上是一个救命恩人@mhodges!尝试在工作中改进旧的代码库,您的解决方案可以完美地工作。
0赞 Christian4423 6/24/2017 #2

如果您使用的是 webpack 2.x,则有一个内置插件

定义全局变量,然后就可以访问它。

    plugins: [
        new webpack.ProvidePlugin({
            $: "jquery",
            jQuery: "jquery",
            "window.jQuery": "jquery",
            "window.Tether": 'tether',
            "Tether": 'tether'
        }),
        ...
    ]

这是我的完整配置

    
    var webpack = require("webpack");
    var ExtractTextPlugin = require("extract-text-webpack-plugin");
    var path = require("path")


    module.exports = {
        entry: "./src/entry-js.js",
        devtool: 'source-map',
        output: {
            path: path.join(__dirname, "/public/dist/js"),
            publicPath: "/public/",
            filename: 'bundle.js',
            chunkFilename: 'chunk.[name].[id].js',
        },
        module: {
            rules: [
                {
                    test: /\.js$/,
                    loader: "babel-loader",
                    options: {
                        presets: ["es2015", "stage-0"]
                    },
                    exclude: [
                        path.resolve(__dirname, "node_modules")
                    ],
                },
                {
                    test: /\.css$/,
                    use: ExtractTextPlugin.extract({
                        fallback: "style-loader",
                        use: "css-loader"
                    })
                },
                {
                    test: /\.(scss|sass)$/,
                    use: ExtractTextPlugin.extract({
                        fallback: "style-loader",
                        use: [
                            "css-loader",
                            "sass-loader"
                        ]
                    })
                },
                {
                    test: /\.less$/,
                    use: ExtractTextPlugin.extract({
                        fallback: "style-loader",
                        use: [
                            "css-loader",
                            "less-loader"
                        ]
                    })
                },
                {
                    test: /\.(png|svg|jpg|gif)$/,
                    use: [{
                        loader:"file-loader",
                        options: {
                            limit: 500,
                            name: "../img/[name].[ext]"
                        }
                    }]
                },
                {
                    test: /\.(woff|woff2|eot|ttf|otf)$/,
                    use: [{
                        loader:"file-loader",
                        options: {
                            limit: 500,
                            name: "../fonts/[name].[ext]"
                        }
                    }]
                }
            ]
        },
        plugins: [
            new ExtractTextPlugin({
                filename: "../css/bundle.css",
                disable: false,
                allChunks: true
            }),
            new webpack.ProvidePlugin({
                $: "jquery",
                jQuery: "jquery",
                "window.jQuery": "jquery",
                "window.Tether": 'tether',
                "Tether": 'tether'
            })
        ]
    };

这是我的入口文件

/********************
 *   CSS Libraries  *
 ********************/

// normalize v7
import "../node_modules/normalize.css/normalize.css";
// bootstrap v4.alpha-5
import "../node_modules/bootstrap/scss/bootstrap.scss";


/******************
 *   CSS Custom   *
 ******************/
import "./css/main.css";
import "./sass/main.scss";

/********************
 *   JS Libraries   *
 ********************/

//Jquery v3.2.1
import '../node_modules/jquery/src/jquery.js';
import Tether from 'tether';
//Bootstrap v4-alpha-5
import "../node_modules/bootstrap/dist/js/bootstrap.min.js";

import "./js/main.js";

评论

0赞 Loufs 6/24/2017
是的,我试过 ProvidePlugin。问题是在调用 webpack 之前,我不知道我需要的所有全局变量。我稍微编辑了我的帖子,使这一点更加明显。
4赞 Loufs 6/24/2017 #3

注意:这不是理想的情况,但由于我添加了恒定数量的新全局变量,因此我需要制作一个插件来为我捆绑我的 javascript。

webpack-raw-bundler

这只是将您的代码堆叠在一起以包含在前端。这是我的使用示例:

用法

从旧:

<script src="js/jquery.js"></script>
<script src="js/silly.js"></script>
<script>
    $(silly()); // Some function in silly.js's global scope
</script>

到新的:

<script src="js/bundle.js"></script>
<script>
    $(silly()); // Some function in silly.js's global scope
</script>

安装到配置

  var RawBundlerPlugin = require('webpack-raw-bundler');

  module.exports = {
    plugins: [
       new RawBundlerPlugin({
             excludedFilenames: [/angulartics/],
             readEncoding: "utf-8",
             includeFilePathComments: false,
             bundles: [ "vendor.js", "styles.css" ],
             "vendor.js": [
                'js/*.js'
             ],
             "styles.css": [
                'css/bootstrap.css',
                'css/edits.css'
             ]
       })
    ]
 }

一个公平的警告:

这不应该是您的首选解决方案但我有一个糟糕的案例,使其成为最简单的选择。使用 expose-loaderimport 或 window['module'] = require('module.js') 更安全,因为这是 webpack 的构建方式。但是,如果您有些头疼并且只想要一个简单的捆绑器,请随时使用此插件。

评论

0赞 Arihant 11/13/2018
可以添加缩小和散列吗?
0赞 Loufs 11/14/2018
我通过另一个包在外部执行此操作,以保持一切模块化
1赞 VitalyB 2/24/2018 #4

我遇到了同样的问题,我发现的最佳解决方案是使用 webpack-concat-plugin

它的作用:

  • 将所有内容连接到单个文件中
  • 允许我指定生成的文件名,包括用于执行缓存无效化的 [cache] 模板
  • 通过 HtmlWebpackPlugin 将自身添加到生成的 html 中

它唯一不做的是不会将所有全局变量泄漏到全局范围。

2赞 Paul Williams 12/31/2019 #5

听起来 OP 正在寻找的是 而不是 .exports-loaderexpose-loader

要公开模块,请使用 expose-loader

要公开全局变量,请使用 exports-loader

这是其中一笔交易,答案有明确记录,但你必须首先知道你要找的东西并知道它的名字。这两个相似的装载机也被赋予了相似的名称,这无济于事。

0赞 Jiro Matchonson 11/4/2022 #6

[2022] 不要使用 webpack(或类似工具)捆绑<脚本>标签链接,这在旧 Web 中很常见。在 50+ 外部库中手动解决全局变量是没有办法的,而且从个人经验来看,在某些情况下甚至是不可能的,当脚本包含“require”时也会出现中断问题以及 webpack 处理 js 代码导致的所有其他问题,而不仅仅是将东西合并在一起 + 缩小。

使用 uglifyJS(和/或 uglifyCSS)

npm install uglify-js -g

uglifyjs --compress --mangle --output bundle.js -- js/jquery.js js/silly.js

然后简单地按照自己的意愿去做。如果不希望进行额外的缩小,可以删除 --com 或 --mangle

<script src="js/bundle.js"></script>
<script>
    $(silly()); // Some function in silly.js's global scope
</script>

https://github.com/mishoo/UglifyJS

由于这个特定原因,您可能会在单个文件夹中拥有大量 js 文件,这可能很有用,将它们全部合并到单个命令中 https://github.com/ionutvmi/uglifyjs-folder