使用webpack动态要求别名模块

9

我正在通过webpack的resolve.alias配置一系列模块别名。然后,在我的应用程序代码中,我想使用包含别名名称的变量来引用其中的一个模块:

var module = require(moduleAlias);

很遗憾,这会创建一个“上下文模块”,其中包含脚本目录及其子目录中的所有内容。但在这种特定情况下我并不需要这样。此外,在我的代码中没有明确要求所有别名模块,它们不会被构建到我的应用程序中。

两个问题:

  1. 如何确保所有别名模块与我的代码捆绑?
  2. 如何使用包含别名的变量访问它们?

谢谢!


为什么导入需要动态性?你能更好地描述一下上下文吗? - Juho Vepsäläinen
需要引入的模块取决于从服务器返回的数据。在我给出的示例中,moduleAlias 的值来自服务器。 - Aaronius
由于依赖关系是动态的,您可能需要通过其他加载程序(如 $script)进行操作。请参见 issue 150 - Juho Vepsäläinen
澄清一下:moduleAlias所指的模块在运行时不需要下载到客户端。在构建时,我知道所有需要编译和可能需要使用moduleAlias的模块。虽然moduleAlias的值是动态的,但它只能是几个值之一。这些可能的值在构建时已知。 - Aaronius
你能试试require.context吗?我有一种感觉它很适合你的情况。 - Juho Vepsäläinen
2个回答

10

这只回答了你问题的第二部分:如果你有带别名的捆绑模块,并且你想从上下文中要求这些别名:

据我所知,使用webpack没有官方方法。我创建了一个插件,在Node 4中工作(如果您想使用纯ES5,则可以进行调整),该插件将别名列表添加到任何上下文中:

'use strict';

class AddToContextPlugin {
  constructor(extras) {
    this.extras = extras || [];
  }

  apply(compiler) {
    compiler.plugin('context-module-factory', (cmf) => {
      cmf.plugin('after-resolve', (result, callback) => {
        this.newContext = true;
        return callback(null, result);
      });

      // this method is called for every path in the ctx
      // we just add our extras the first call
      cmf.plugin('alternatives', (result, callback) => {
        if (this.newContext) {
          this.newContext = false;

          const extras = this.extras.map((ext) => {
            return {
              context: result[0].context,
              request: ext
            };
          });

          result.push.apply(result, extras);
        }
        return callback(null, result);
      });
    });
  }
}

module.exports = AddToContextPlugin;

这是您可以使用它的方法:

webpack({
      /*...*/
      resolve: {
        alias: {
          'alias1': 'absolute-path-to-rsc1',
          'alias2$': 'absolute-path-to-rsc2'
        }
      },
      plugins: [
        new AddToContextPlugin(['alias1', 'alias2'])
      ]
    })

结果是生成了以下代码:

function(module, exports, __webpack_require__) {

    var map = {
        "./path/to/a/rsc": 2,
        "./path/to/a/rsc.js": 2,
        "./path/to/another/rsc.js": 301,
        "./path/to/another/rsc.js": 301,
        "alias1": 80,
        "alias2": 677
    };
    function webpackContext(req) {
        return __webpack_require__(webpackContextResolve(req));
    };
    function webpackContextResolve(req) {
        return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }());
    };
    webpackContext.keys = function webpackContextKeys() {
        return Object.keys(map);
    };
    webpackContext.resolve = webpackContextResolve;
    module.exports = webpackContext;
    webpackContext.id = 1;

}

非常酷的@JBE。由于我的项目需求已经改变,我还没有尝试过这个,但在没有其他答案的情况下,我会将其标记为正确。谢谢。 - Aaronius
这似乎可以解决我的问题,但是在最新的webpack中,我遇到了以下错误:DeprecationWarning: Tapable.plugin已弃用。请改用.hooks上的新API你可能有更新的版本吗? - Lanklaas

6
我发现最干净的解决方案是覆盖默认的模块ID系统。Webpack 默认使用数组索引。我检查文件路径是否在我的别名模块中,然后将其 ID 设置为该路径。
这样,在我需要使用别名进行同步动态加载的代码中,我可以使用 __webpack_require__(alias)
这是一种完全使用私有方法(__webpack_require__)的黑客方式,但我认为这是一个临时的解决方案,直到我能够将我们的代码库迁移到正确的异步动态加载或像许多 requireJS 代码库一样正确地使用路径而不是别名。
var path = require('path');
var _ = require('lodash');

function NamedAliasModules(){};    

NamedAliasModules.prototype.apply = function(compiler){
    compiler.plugin('compilation', function(compilation){
        compilation.plugin("before-module-ids", function(modules) {
            modules.forEach(function(module) {
                if(module.id === null && module.libIdent) {
                    var id = module.libIdent({
                        context: compiler.options.context
                    });
                    var fullpath = path.resolve(__dirname, id);

                    if (_.has(aliasLookup, fullpath) || _.has(aliasLookup, fullpath.replace(/\.js$/,''))){
                        id = aliasLookup[fullpath] || aliasLookup[fullpath.replace(/\.js$/, '')];

                        module.libIdent = function(){
                            return id;
                        }

                    }
                    module.id = id;
                }
            }, this);
        }.bind(this));
    }.bind(this));
}

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