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

Posted

技术标签:

【中文标题】公开通过 webpack 捆绑的 javascript 全局变量【英文标题】:Expose javascript globals bundled via webpack 【发布时间】:2017-11-27 05:14:49 【问题描述】:

问题

我觉得这应该比现在更直接。我需要从前端访问我的所有 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>

我尝试过的事情

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

    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;博士: 如何从捆绑的 js 文件中创建全局变量,并在通过 &lt;script src="js/bundle.js"&gt;&lt;/script&gt; 在前端 html 页面上导入时暴露于全局范围?

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

【问题讨论】:

您可以在捆绑的各个脚本中的 window 对象上声明它们。您也可以使用单个文件作为入口点并说window["silly"] = require("silly.js") 等。反正我就是这样做的。 【参考方案1】:

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

重要的附注 - 在窗口上声明内容时使用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()
    ],
];

【讨论】:

感谢您的回答和+1,因为这完全有效。但是,我不知道我需要的所有模块,所以我需要在运行webpack 时动态地执行它。不幸的是,有太多不断添加的内容让我无法使用window['module'] = require('module.js') 技巧。 @Lofus 如果您不断向全局范围添加内容,那么您可能做错了什么。仅将真正全局的内容添加到全局包中,然后为不同的模块使用不同的入口点在您的应用程序中。您的全局范围不应该知道应用程序中每个页面需要的个别事物。每个文件都应该知道它只需要什么,并且应该自己加载它的依赖项。 Webpack 将删除任何重复的要求,因此要求离开。 我意识到这并不理想,但团队中有这么多人,js/ 文件夹每小时都在扩展,我真的不想告诉每个人如何更新webpack 接受这些添加 @Lofus 好吧,您的问题非常明确地说明了“如何在与 webpack 捆绑时使 JavaScript 全球化”,而我的回答解决了这个确切的问题。这可能不是您提出问题的意图,但现在措辞的方式是对未来用户有用的有效问题(甚至比您的特定用例更有用)。 不能反驳 :)【参考方案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";

【讨论】:

是的,我已经尝试过 ProvidePlugin。问题是在调用 webpack 之前我不知道我需要的所有全局变量。我稍微编辑了我的帖子以使其更加明显。【参考方案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-loaderimportwindow['module'] = require('module.js') 更安全,因为这就是 webpack 的构建。但是,如果您有些头疼并且只想要一个简单的捆绑器,请随意使用此插件。

【讨论】:

可以添加缩小和散列吗? 我通过另一个包在外部执行此操作,以保持一切模块化【参考方案4】:

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

它的作用:

将所有内容合并到一个文件中 允许我指定生成的文件名,包括执行缓存清除的 [cache] 模板 通过 htmlWebpackPlugin 将自身添加到生成的 html 中

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

【讨论】:

【参考方案5】:

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

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

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

这是其中明确记录了答案的交易之一,但您必须首先了解您要寻找的东西并知道它的名称。这两个相似的加载器也被赋予了相似的名称,这无济于事。

【讨论】:

以上是关于公开通过 webpack 捆绑的 javascript 全局变量的主要内容,如果未能解决你的问题,请参考以下文章

使用 Webpack 和 Typescript 将模块公开给全局 Window 对象

webpack 不再缩小捆绑包

NPM 包:最佳实践和公开多个导入路径

Webpack 捆绑许可证合规性?

与 Webpack 捆绑的 Node 或 Electron 主进程的 VSCode 调试

动态加载外部 webpack 捆绑的 ngModule 作为路由处理程序