【问题标题】:How to use a function in one file that was included globally with webpack如何使用 webpack 全局包含的一个文件中的函数
【发布时间】:2021-05-19 16:56:39
【问题描述】:

我有一个静态 Javascript 项目(没有 react、vue 等),我正在尝试使用 webpack 转换、捆绑和缩小我的 js。我想在我的布局页面上有 bundle.js,其中将包括一堆在所有页面上运行的全局 js,然后是一个 page_x.js 文件,该文件将根据需要在各个页面上。 bundle.js 文件可能包含其他几个文件,应转译为 es5 并缩小。

在我当前的设置下,文件运行了两次。我不确定如何解决这个问题。我希望全局包含该文件,但也希望能够根据需要调用该函数。如果我从 page.js 中删除导入语句,则会收到控制台错误,“doSomething”未定义。如果我只在 page.html 上包含 page.js 而不是在 _layout.html 上包含 page.js,那么 common.js 只会在 page.html 上注销。我希望在每个页面上记录一次“common”,并且我希望 doSomething() 仅在 page.js 上可用。

这是一个运行两次的例子:

common.js

console.log("common");
export function doSomething() {
    console.log("do something");
}

page.js

import {doSomething} from "/common.js";
$(button).click(doSomething);

页面加载(点击任何内容之前)的预期输出是:

"common"

我看到的是

"common"
"common"

我的 webpack.config.js 文件如下:

const path = require("path");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const RemoveEmptyScriptsPlugin = require("webpack-remove-empty-scripts");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const WebpackWatchedGlobEntries = require("webpack-watched-glob-entries-plugin");
const CssnanoPlugin = require("cssnano");
const TerserPlugin = require("terser-webpack-plugin");

const dirName = "wwwroot/dist";

module.exports = (env, argv) => {
    return {
        mode: argv.mode === "production" ? "production" : "development",
        entry: WebpackWatchedGlobEntries.getEntries(
            [
                path.resolve(__dirname, "src/scripts/**/*.js"),
                path.resolve(__dirname, "src/scss/maincss.scss")
            ]),
        output: {
            filename: "[name].js",
            path: path.resolve(__dirname, dirName)
        },
        devtool: "source-map",
        module: {
            rules: [
                {
                    test: /\.s[c|a]ss$/,
                    use:
                        [
                            MiniCssExtractPlugin.loader,
                            "css-loader?sourceMap",
                            {
                                loader: "postcss-loader?sourceMap",
                                options: {
                                    postcssOptions: {
                                        plugins: [
                                            CssnanoPlugin
                                        ],
                                        config: true
                                    },
                                    sourceMap: true
                                }
                            },
                            { loader: "sass-loader", options: { sourceMap: true } },
                        ]
                },
                {
                    test: /\.(svg|gif|png|eot|woff|ttf)$/,
                    use: [
                        "url-loader",
                    ],
                },
                {
                    test: /\.m?js$/,
                    exclude: /(node_modules|bower_components)/,
                    use: {
                        loader: "babel-loader",
                        options: {
                            presets: ["@babel/preset-env"]
                        }
                    }
                }
            ]
        },
        plugins: [
            new WebpackWatchedGlobEntries(),
            new CleanWebpackPlugin(),
            new RemoveEmptyScriptsPlugin(),
            new MiniCssExtractPlugin({
                filename: "[name].css"
            })
        ],
        optimization: {
            minimize: true,
            minimizer: [
                new TerserPlugin({
                    extractComments: false,
                })
            ]
        }
    };
};

任何帮助将不胜感激。

【问题讨论】:

    标签: javascript webpack bundle minify


    【解决方案1】:

    Webpack 是关于构建应用程序文件的依赖关系图并最终生成一个单独的包。

    通过您的配置,您实际上是在尝试将 Webpack 用作 Multi-entry object configuration,如 Webpack 文档中所述。这里的罪魁祸首是WebpackWatchedGlobEntries 插件。对于与 glob 模式匹配的每个文件,它会创建一个你不想要的包。例如,如果您有以下结构:

    - src/scripts
        - common.js
        - some
            - page1.js
        - other
            - page2.js 
    

    这个插件会产生多页应用。所以,你的配置:

    entry: WebpackWatchedGlobEntries.getEntries(
      [
        path.resolve(__dirname, "src/scripts/**/*.js"),
        path.resolve(__dirname, "src/scss/maincss.scss")
      ]),
    

    将在内部返回一个对象:

    entry: {
      "common": "src/scripts/common.js",
      "some/page1": "src/scripts/some/page1.js",
      "other/page2": "src/scripts/other/page2.js"
    }
    

    这意味着如果您将common.js 导入page1.jspage2.js,那么您将生成三个捆绑包,所有这些捆绑包都将拥有common 模块,该模块将被执行三次。

    解决方案实际上取决于您要如何配置捆绑包:

    • 如果您需要捆绑为多页应用程序,则必须使用splitChunk optimization,它允许您创建页面特定捆绑包,同时保持共享代码分开(例如common.js)。请记住,您实际上并不需要为 common.js 手动创建单独的包。使用拆分块,Webpack 应该会自动为您执行此操作。
    • 如果您需要单个包,您可以直接为整个应用程序创建一个包(最典型的 Webpack 工作流程),并在每个页面上使用相同的包。您可以拥有像run 这样的通用函数,它可以计算要使用 URL 或某些唯一页面特定标识符调用的代码。在现代 SPA 中,这是使用路由模块完成的。

    我的建议是保持简单。不要使用WebpackWatchedGlobEntries 插件。如果你不熟悉 Webpack,那会让事情变得复杂。保持entry 像这样简单:

    entry: {
      // Note that you don't need common module here. It would be picked up as part of your page1 and page2 dependency graph
      "page1": "src/scripts/some/page1.js",
      "page2": "src/scripts/other/page2.js"
    }
    

    然后,启用 splitchunk 优化为:

    optimization: {
      splitChunks: {
        chunks: 'all'
      }
    }
    

    同样,有多个选项可供选择。您可以在此处阅读有关preventing code duplication 的更多详细信息。

    【讨论】:

    • 感谢您的回复,但是,我似乎无法让它工作。我无法添加足够长的评论来展示我的工作,但基本上我将 page1 和 page2 添加到上面的条目中,并将上面的 splitChunks 添加到优化标记中。我现在只创建了没有 common.js 的 page1 和 page2
    • 另外,如果我完全走错了方向来解决这个问题,请告诉我。我真的只想获取我所有的 js 文件,转译、缩小,然后将它们打包成类似 site.min.js、page1.minjs 和 page2.min.js 之类的东西。
    • @Lee,你快到了。 Webpack 不生成第三个文件的原因是common.js 不能有太多的内容。默认情况下,如果至少 20KB 大小,Webpack 会将公共代码拆分为单独的文件。它是使用splitChunks.minSize 属性控制的。接下来,块!=文件,因此块名称不是文件名。它会由 Webpack 自动生成,通常是一个数字 - 648.js。您可以使用 splitChunks.name 属性覆盖它,但这不是一个好主意。在这里阅读更多 - webpack.js.org/plugins/split-chunks-plugin
    • 未专门添加到任何页面的功能会发生什么情况。例如,将 global.js 添加到我的 layout.html 模板中。它执行诸如添加事件侦听器和在每个页面的页面加载时运行的函数之类的事情。它永远不会添加到任何单个 Page 的 Js 文件中。它仍然需要被最小化并包含在网站上。它的功能应该可以被其他页面根据需要调用。
    • @Lee,对于此类文件,您应该使用副作用导入,例如 import 'global.js';
    猜你喜欢
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    相关资源
    最近更新 更多