通过Webpack捆绑的JavaScript全局变量的公开暴露

29

问题

我觉得这应该比它现在的情况更加直截了当。我需要从前端访问所有我的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. ProvidePlugin:只能让库看到其他库。在我的当前设置下,我也不能显式地编写我需要的所有全局变量(会不断添加更多变量)。

我需要什么

为了更加清晰明了,我需要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: 当通过 <script src="js/bundle.js"></script> 在前端 HTML 页面上导入时,如何使我的捆绑 JS 文件中的全局变量暴露到全局作用域?

Btw: 如果有人是 webpack 专家并且知道为什么这种方法不好,请在下面发帖,并简要说明,以便我可以修复自己的生活。


3
你可以在被打包的个人脚本中在window对象上声明它们。你也可以使用一个文件作为入口点,然后说window["silly"] = require("silly.js")等等。不管怎样,这就是我所做的。 - mhodges
6个回答

16

以下是我在自己网站上的一个示例。我不确定它是否是唯一的方法,甚至是否是最好的方法,但它干净、简单且适用于我的情况。

重要提示 - 当在window上声明变量时,请使用window["propName"],因为当你运行webpack -p时,它会压缩任何非字符串类型的内容。如果你将其定义为window.propName,它可以被更改为类似s.c的东西,而你的其他代码则无法识别它。使用方括号表示法并将其声明为字符串将强制webpack保留名称不变,这样你就可以在任何地方以相同的名称访问它。

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({})
    ],
}];

@Lofus 如果你不断地向全局作用域添加东西,那么你可能做错了什么。只将真正的全局内容添加到全局包中,然后为应用程序中的不同模块使用不同的入口点。你的全局范围不应该知道应用程序中每个页面需要的各个单独的东西。每个文件都应该知道它需要什么,只加载它自己的依赖项。Webpack 将去重任何重复的 require,所以尽管 require。 - mhodges
我知道这不是理想的情况,但团队中有很多人,js/文件夹每小时都在不断扩展,我真的不想告诉每个人如何更新webpack以接受这些添加。 - Loufs
1
@Lofus 好的,你的问题非常明确地阐述了“如何在使用webpack打包时使JavaScript全局可用”,而我的回答正好解决了这个问题。也许这不是你问题的意图,但现在它所表达的方式是一个有效的问题,将对未来的用户有所帮助(甚至比你具体的使用情况更有用)。 - mhodges
1
没办法反驳这个观点 :) - Loufs
1
你真是个救星 @mhodges!我正在尝试改进工作中的旧代码库,你的解决方案完美地解决了我的问题。 - Grant
显示剩余5条评论

4
注意:这不是理想的情况,但由于我不断添加新的全局变量,所以需要制作一个插件来为我打包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-loaderimportwindow['module'] = require('module.js')会更安全,因为webpack是围绕它们构建的。但是,如果您遇到一些麻烦,只想要一个简单的打包工具,可以随意使用此插件。


能否添加代码压缩和哈希功能? - Arihant
我通过另一个包在外部完成了这个操作,以保持所有内容的模块化。 - Loufs

2
听起来 OP 寻找的是 exports-loader 而不是 expose-loader
要暴露模块,请使用 expose-loader
要暴露全局变量,请使用 exports-loader
这是其中一种情况,答案明确记录在文档中,但你必须首先了解你要寻找的东西并知道它的名称。这两个相似的加载器也被赋予了相似的名称,这并没有帮助解决问题。

1
我遇到过同样的问题,我找到的最佳解决方案是使用webpack-concat-plugin

它的功能包括:

  • 将所有内容连接到一个文件中
  • 允许我指定结果文件名称,包括[cache]模板进行缓存破坏
  • 通过HtmlWebpackPlugin将自己添加到生成的html中

唯一没有做到的是不向全局作用域泄露所有全局变量。


0
如果你使用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";


是的,我尝试过ProvidePlugin。问题在于在调用webpack之前,我不知道我需要的所有全局变量。我稍微编辑了一下我的帖子,以使这更明显。 - Loufs

0

[2022] 不要使用webpack(或类似工具)来打包< script >标签链接,这在旧网站中很常见。手动解决50多个外部库中的全局变量不是明智之举,从个人经验来看,在某些情况下甚至是不可能的,而且当脚本包含'require'时会出现破坏性问题,所有这些问题都是由webpack处理js代码而不仅仅是合并内容和压缩引起的。

使用uglifyJS(和/或uglifyCSS)。

npm install uglify-js -g

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

然后按照您想要的方式操作即可。如果不需要额外的代码压缩,可以删除--compress或--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可能非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接