使用 webpack 动态需要别名模块
Dynamically require an aliased module using webpack
我正在通过 resolve.alias
通过 webpack 配置一堆模块别名。然后,在我的应用程序代码中,我想使用包含别名的变量来要求这些模块之一:
var module = require(moduleAlias);
不幸的是,这会创建一个 "context module",其中包含脚本目录及其后代中的所有内容,这不是我在这种特殊情况下所追求的。此外,由于我的代码中没有任何地方明确要求所有别名模块,因此它们不会内置到我的应用程序中。
两个问题:
- 如何确保所有别名模块都与我的代码捆绑在一起?
- 如何使用包含别名的变量访问它们?
谢谢!
这仅回答了您问题的第二部分:如果您将模块与别名捆绑在一起,并且您希望这些别名在上下文中是必需的:
据我所知,没有正式的方法可以使用 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;
}
我找到的最干净的解决方案是覆盖默认模块 ID 系统。 Webpack 似乎默认使用数组索引。我检查文件路径是否在我的别名模块中,然后将其 id 设置为该文件路径。
在我的代码中,我需要使用别名执行同步动态要求,我可以这样做 __webpack_require__(alias)
这是一个完全使用私有方法的 hack (__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));
}
我正在通过 resolve.alias
通过 webpack 配置一堆模块别名。然后,在我的应用程序代码中,我想使用包含别名的变量来要求这些模块之一:
var module = require(moduleAlias);
不幸的是,这会创建一个 "context module",其中包含脚本目录及其后代中的所有内容,这不是我在这种特殊情况下所追求的。此外,由于我的代码中没有任何地方明确要求所有别名模块,因此它们不会内置到我的应用程序中。
两个问题:
- 如何确保所有别名模块都与我的代码捆绑在一起?
- 如何使用包含别名的变量访问它们?
谢谢!
这仅回答了您问题的第二部分:如果您将模块与别名捆绑在一起,并且您希望这些别名在上下文中是必需的:
据我所知,没有正式的方法可以使用 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;
}
我找到的最干净的解决方案是覆盖默认模块 ID 系统。 Webpack 似乎默认使用数组索引。我检查文件路径是否在我的别名模块中,然后将其 id 设置为该文件路径。
在我的代码中,我需要使用别名执行同步动态要求,我可以这样做 __webpack_require__(alias)
这是一个完全使用私有方法的 hack (__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));
}