如何在控制台中使用webpack的require()方法?

51

我如何在控制台中使用require() / import导入模块?例如,假设我已经安装了ImmutableJS npm,我想在控制台中使用该模块的函数。


1
已被接受的答案不再适用。请查看最新回答:https://dev59.com/w14b5IYBdhLWcg3wYQmZ#70600070 - daymannovaes
10个回答

15

这里有另一种更通用的方法。

按 ID 要求一个模块

WebPack 的当前版本暴露了 webpackJsonp(...),可用于按 ID 要求一个模块:

function _requireById(id) {
  return webpackJsonp([], null, [id]);
}

或者使用TypeScript

window['_requireById'] =
  (id: number): any => window['webpackJsonp'];([], null, [id]);

在捆绑文件的模块顶部或通过源映射提供的原始源文件的页脚中可见ID。

按名称引用模块

按名称引用模块要复杂得多,因为Webpack在处理所有源代码后似乎没有保留任何对模块路径的引用。但以下代码在许多情况下似乎能够解决问题:

/**
 * Returns a promise that resolves to the result of a case-sensitive search
 * for a module or one of its exports. `makeGlobal` can be set to true
 * or to the name of the window property it should be saved as.
 * Example usage:
 *   _requireByName('jQuery', '$');
 *   _requireByName('Observable', true)´;
 */
window['_requireByName'] =
  (name: string, makeGlobal?: (string|boolean)): Promise<any> =>
    getAllModules()
    .then((modules) => {
      let returnMember;
      let module = _.find<any, any>(modules, (module) => {
        if (_.isObject(module.exports) && name in module.exports) {
          returnMember = true;
          return true;
        } else if (_.isFunction(module.exports) &&
                   module.exports.name === name) {
          return true;
        }
      });
      if (module) {
        module = returnMember ? module.exports[name] : module.exports;
        if (makeGlobal) {
          const moduleName = makeGlobal === true ? name : makeGlobal as string;
          window[moduleName] = module;
          console.log(`Module or module export saved as 'window.${moduleName}':`,
            module);
        } else {
          console.log(`Module or module export 'name' found:`, module);
        }
        return module;
      }
      console.warn(`Module or module export '${name}'' could not be found`);
      return null;
    });

// Returns promise that resolves to all installed modules
function getAllModules() {
  return new Promise((resolve) => {
    const id = _.uniqueId('fakeModule_');
    window['webpackJsonp'](
      [],
      {[id]: function(module, exports, __webpack_require__) {
        resolve(__webpack_require__.c);
      }},
      [id]
    );
  });
}

这只是快速尝试,所以所有内容都有待改进!


很好!对于我来说,它能够检索捆绑包中的所有模块。但是,只有当我将ID设置为0时才有效:const id = 0;。当我使用上面的代码(和其他一些字符串/数字)时,Promise从未解决。 - Venryx
3
对我来说不起作用:window.webpackJsonp 不是一个函数 :( 我需要引入什么吗? - Septagram
@Septagram 当你加载某个块时,webpackJsonp 才会被包含在页面中。我发现的一个技巧是使用 CommonChunksPlugin,并仅传递主要内容(假设这是您所拥有的所有内容),然后您将得到 webpackRuntime 和您的捆绑包分开,您可以将它们都包含在页面上(或者 htmlplugin 为您完成)。 - light24bulbs
1
webpackJsonp() 函数定义在哪里?它的参数是什么?在 webpack 的源代码中找不到。 - cprcrack
您的解决方案不再起作用了。请检查我的解决方案:https://dev59.com/w14b5IYBdhLWcg3wYQmZ#70600070 - daymannovaes

11

在模块中包含此内容将允许从浏览器中使用 require([modules], function)

window['require'] = function(modules, callback) {
  var modulesToRequire = modules.forEach(function(module) {
    switch(module) {
      case 'immutable': return require('immutable');
      case 'jquery': return require('jquery');
    }
  })
  callback.apply(this, modulesToRequire);
}

使用示例:

require(['jquery', 'immutable'], function($, immutable) {
  // immutable and $ are defined here
});

注意: 每个switch语句选项应该是这个模块已经要求的内容或由ProvidePlugin提供。


来源:

基于这个答案,可以用来添加整个文件夹。

另一种方法来自Webpack文档 - 允许像require.yourModule.function()这样的操作。


使用这种方法,是否可以将某些内容require到顶层上下文中? - amoe

4
我找到了一种适用于 WebPack 1 和 2 的方法(只要源代码未经压缩)。以下是该方法的步骤:
1. 安装依赖:https://github.com/Venryx/webpack-runtime-require 2. 在代码中使用该依赖
请注意,以上步骤需要按照格式严格执行。
npm install --save webpack-runtime-require

使用方法

首先,至少要引入一次该模块。

import "webpack-runtime-require";

接下来它会向window对象添加一个Require()函数,供控制台或代码中的任何位置使用。

然后只需像这样使用:

let React = Require("react");
console.log("Retrieved React.Component: " + React.Component);

这并不十分美观(它使用正则表达式来搜索模块包装函数),也不够快(第一次调用需要 ~50ms,之后为 ~0ms),但如果只是用于在控制台中进行hack测试,这两点都没问题。

技术

以下是源代码的精简版本,以展示它的工作原理。 (有关完整/最新版本,请参见存储库)

var WebpackData;
webpackJsonp([],
    {123456: function(module, exports, __webpack_require__) {
        WebpackData = __webpack_require__;
    }},
    [123456]
);

var allModulesText;
var moduleIDs = {};
function GetIDForModule(name) {
    if (allModulesText == null) {
        let moduleWrapperFuncs = Object.keys(WebpackData.m).map(moduleID=>WebpackData.m[moduleID]);
        allModulesText = moduleWrapperFuncs.map(a=>a.toString()).join("\n\n\n");

        // these are examples of before and after webpack's transformation: (which the regex below finds the var-name of)
        //      require("react-redux-firebase") => var _reactReduxFirebase = __webpack_require__(100);
        //      require("./Source/MyComponent") => var _MyComponent = __webpack_require__(200);
        let regex = /var ([a-zA-Z_]+) = __webpack_require__\(([0-9]+)\)/g;
        let matches = [];
        let match;
        while (match = regex.exec(allModulesText))
            matches.push(match);

        for (let [_, varName, id] of matches) {
            // these are examples of before and after the below regex's transformation:
            //      _reactReduxFirebase => react-redux-firebase
            //      _MyComponent => my-component
            //      _MyComponent_New => my-component-new
            //      _JSONHelper => json-helper
            let moduleName = varName
                .replace(/^_/g, "") // remove starting "_"
                .replace(new RegExp( // convert chars where:
                          "([^_])"      // is preceded by a non-underscore char
                        + "[A-Z]"       // is a capital-letter
                        + "([^A-Z_])",  // is followed by a non-capital-letter, non-underscore char
                    "g"),
                    str=>str[0] + "-" + str[1] + str[2] // to: "-" + char
                )
                .replace(/_/g, "-") // convert all "_" to "-"
                .toLowerCase(); // convert all letters to lowercase
            moduleIDs[moduleName] = parseInt(id);
        }
    }
    return moduleIDs[name];
}

function Require(name) {
    let id = GetIDForModule(name);
    return WebpackData.c[id].exports;
}

2

能够在控制台中使用require模块对于调试和代码分析非常方便。@psimyn的答案非常具体,因此您可能无法使用所有所需的模块来维护该函数。

当我需要自己的一个模块用于此目的时,我将其分配给窗口属性,以便我可以访问它,例如:window.mymodule = whatever_im_exporting;。如果我想要使用系统模块进行操作,我也会使用相同的技巧来公开它,例如:

myservice.js:

let $ = require('jquery');
let myService = {};

// local functions service props etc...

module.exports = myService;

// todo: remove these window prop assignments when done playing in console
window.$ = $;
window.myService = myService;

虽然有些麻烦,但是深入研究这些代码包,我发现没有方便的方法来映射模块。


1
这是一种不好的做法。每次我像这样做事情,都会后悔。 - Aluan Haddad
1
@AluanHaddad: 你能解释一下吗?这是纯粹的交互式实验和测试,随着你的进行而进行。我发现可以与我的服务进行交互以“戳”它们并验证它们是否按预期运行非常有用。 - Paul Whipp
1
@AluanHaddad,我感觉很相似。另一方面,如果您已经建立了良好的TODO管理,那就没问题了。我们有一个简单的规则:“发布时不允许有TODO。” ;) - Milan Jaros
@MilanJaros也许是真的,但我甚至无法让我的一些同事运行linter,所以在// TODO: remove this方面,因人而异。我更喜欢加载器而不是打包程序的原因之一是,您可以进行此类实验而无需更改代码,只需使用浏览器控制台即可。SystemJS.import('lodash').then(({default}) => window._ = default);。我知道这个问题与Webpack有关,我只是在发牢骚,您必须承认这很不错。 - Aluan Haddad
@PaulWhipp 这个代码块通过将 libs 赋值给 window 来改变全局状态,这可能会导致隐式依赖。最明显的例子是一些糟糕编写的 jQuery 插件(没有检查 definerequire/exports)。 - Aluan Haddad
显示剩余5条评论

2

@Rene Hamburger的答案很好,但不幸的是它已经不能用了(至少在我的webpack版本中)。所以我进行了更新:

function getWebpackInternals() {
    return new Promise((resolve) => {
        const id = 'fakeId' + Math.random();
        window['webpackJsonp'].push(["web", {
                [id]: function(module, __webpack_exports__, __webpack_require__) {
                        resolve([module, __webpack_exports__, __webpack_require__])
                }
        },[[id]]]);
    });
}

function getModuleByExportName(moduleName) {
    return getWebpackInternals().then(([_, __webpack_exports__, __webpack_require__]) => {
        const modules = __webpack_require__.c;

        const moduleFound = Object.values(modules).find(module => {
            if (module && module.exports && module.exports[moduleName]) return true;
        });

        if (!moduleFound) {
            console.log('couldnt find module ' + moduleName);
            return;
        }

        return moduleFound.exports[moduleName];
    })
}

getModuleByExportName('ExportedClassOfModule');

1
这个也不起作用,或者使用方法不完整。 - Mohammad Hossein Amri

1
将以下代码添加到您的模块之一中,即可通过id加载模块。
window.require = __webpack_require__;

在控制台中使用以下代码:
require(34)

1
我的模块有什么编号,有没有快速的方法可以了解? - Eugene Tiutiunnyk
@eugenet8k,Webpack 的输出应该在模块上有标签。对于上面的示例,我应该在输出文件中看到 /* 34 */。如果这有帮助,请告诉我。 - lcharbon

1

expose-loader 在我看来是一种更加优雅的解决方案:

require("expose-loader?libraryName!./file.js");
// Exposes the exports for file.js to the global context on property "libraryName".
// In web browsers, window.libraryName is then available.

虽然每个模块都作为全局名称暴露出来是一个劣势。这增加了开发构建与生产构建行为不同的可能性。(假设暴露是严格用于开发方便)(当然可以使用 _dev 前缀) - olejorgenb

0

您可以像psimyn建议的那样,将以下代码添加到包中的某个模块中:

require.ensure([], function () {
    window.require = function (module) {
        return require(module);
    };
});

在控制台中使用 require:

require("./app").doSomething();

查看更多


你真的试过这个吗?似乎不起作用,只返回未定义。可能是因为 webpack 在编译时必须知道模块名称(我认为);它在上面编译时显示此错误:“WARNING in [...] Critical dependencies: [...] the request of a dependency is an expression”。 - Venryx
当然,我试过了。我们使用它一段时间来尝试自定义服务。如果你觉得有帮助的话,我可以尝试从项目中剪切出一个示例。请注意,最终我们采用了类似于https://dev59.com/w14b5IYBdhLWcg3wYQmZ#38214280的方法。相似?我的意思是,我们对调试和发布采用不同的配置方式。 - Milan Jaros
没关系,我找到/制作了另一种方法。还是谢谢你。 - Venryx

0

在为此创建了一个npm模块之后(请参见我的其他答案),我在npms.io上进行了搜索,似乎已经找到了一个现有的webpack插件可用于此目的。

存储库:https://www.npmjs.com/package/webpack-expose-require-plugin

安装

npm install --save webpack-expose-require-plugin

用法

将插件添加到您的Webpack配置中,然后在运行时像这样使用:

let MyComponent = require.main("./path/to/MyComponent");
console.log("Retrieved MyComponent: " + MyComponent);

请查看软件包/存储库自述页面以获取更多信息。
编辑
我在自己的项目中尝试了这个插件,但无法使其工作;我一直收到错误消息:Cannot read property 'resource' of undefined。不过,如果它对其他人有效,我会把它留在这里。(我目前正在使用上面提到的解决方案)

0

在创建了自己的npm包(请看这里)以及找到一个现成的npm包(请看这里)之后,我还发现了一种只使用内置webpack函数就可以实现一行代码完成的方法。

它利用了Webpack的“上下文”功能:https://webpack.github.io/docs/context.html

只需要在你的“源”文件夹中的任意文件中添加以下代码即可:

window.Require = require.context("./", true, /\.js$/);

现在你可以像这样在控制台中使用它:

let MyComponent = Require("./Path/To/MyComponent");
console.log("Retrieved MyComponent: " + MyComponent);

然而,与上述两种解决方案相比,这种方法的一个重要缺点是似乎无法处理 node_modules 文件夹中的文件。当路径调整为“../”时,webpack 无法编译——至少在我的项目中是如此。(也许是因为 node_modules 文件夹太庞大了)

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