我如何使用 webpack 从控制台 require()?

How do I require() from the console using webpack?

如何从控制台 require() / 导入模块?例如,假设我已经安装了 ImmutableJS npm,我希望能够在控制台中工作时使用模块中的函数。

将此包含在模块中将允许 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

提供

来源:

基于this answer,可用于添加整个文件夹。

来自 Webpack Docs 的替代方法 - 允许 require.yourModule.function()

能够在控制台中使用 require 模块,便于调试和代码分析。 @psimyn 的回答非常具体,因此您不太可能使用您可能需要的所有模块来维护该功能。

当我为此需要我自己的模块之一时,我会为其分配一个 window 属性,以便我可以获取它,例如 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;

仍然有点痛苦,但是深入研究包,我看不出有什么方法可以方便地映射模块。

这是另一种更通用的方法。

通过 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]
    );
  });
}

这是快速的第一次尝试,所以一切都需要改进!

您可以按照 psimyn 的建议做类似的事情 将以下代码添加到包中的某个模块:

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

从控制台使用要求:

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

See more

在我看来,

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.

我找到了一种适用于 WebPack 1 和 2 的方法。(只要源未缩小)

回购:https://github.com/Venryx/webpack-runtime-require

安装

npm install --save webpack-runtime-require

用法

首先,至少需要一次模块。

import "webpack-runtime-require";

然后它将向 window 对象添加一个 Require() 函数,以便在控制台或您的代码中的任何地方使用。

那就直接使用吧,像这样:

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

它不是很漂亮(它使用正则表达式来搜索模块包装器函数)或速度很快(第一次调用大约需要 50 毫秒,之后需要大约 0 毫秒),但如果只是用于黑客测试,这两个都非常好在控制台中。

技术

以下是源代码的修剪版本,以显示其工作原理。 (请参阅 full/latest 的存储库)

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;
}

为此制作了一个 npm 模块后(参见我的 ),我在 npms.io 上进行了搜索,似乎找到了一个现有的 webpack-plugin 可用于此目的。

回购: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);

有关详细信息,请参阅 package/repo 自述文件页面。

编辑

我在自己的项目中试用了该插件,但无法正常工作;我不断收到错误消息:Cannot read property 'resource' of undefined。不过,我会把它留在这里,以防它对其他人有用。 (我目前正在使用上面提到的解决方案)

在为此制作了我自己的 npm 包之后 (), as well as finding an existing one (),我还找到了一种方法,只需使用内置的 webpack 函数即可在一行中完成。

它使用 WebPack "contexts": https://webpack.github.io/docs/context.html

只需将以下行直接添加到 "Source" 文件夹中的文件中:

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

现在您可以像这样使用它(例如在控制台中):

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

但是,与上述两种解决方案相比,这种方法的一个重要缺点是它似乎不适用于 node_modules 文件夹中的文件。当路径调整为“../”时,webpack 无法编译——至少在我的项目中是这样。 (可能是因为 node_modules 文件夹太大了)

将以下代码添加到您的模块之一将允许您通过 id 加载模块。

window.require = __webpack_require__;

在控制台中使用以下命令:

require(34)

@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');