javascript 动态模块的WebPack导入数组(编译时已知)

ebdffaop  于 2023-05-16  发布在  Java
关注(0)|答案(1)|浏览(124)

在我的项目中,我想导入特定的模块,它们的实际路径只有在编译过程中才知道。
假设我有components/A.jscomponents/B.jscomponents/C.js,在我的App.js中,我想包含它们的一个子集,这些子集只有在webpack的编译时才能知道(例如:(JSON)

我所尝试的- 1

//App.js
compileData = await getCompileDataSomehow()

import("./components/" + compileData.component + ".js")
   .then( /* Do stuff with component */);

这个工作很好,但是webpack会为每个components/*.js文件创建一个块。此外,还将有额外的网络往返。我觉得这有点矫枉过正,因为我会知道什么组件,我想当webpack将运行。因此,我尝试 * 提供 * 组件数组,但没有成功,因为require([...])也被认为是动态的。

我的结局

//App.js
import("./components/ALL.js") //this file doesn't actually exists
//webpack.config.js

const fs = require('fs')

const componentsToInclude = ["A", "B"] //this will be read from a json

fs.writeFileSync('./src/components/ALL.js',
  `
export default [
    ${componentsToInclude.map(comp => `require("./${comp}.js")` )}
].map(c => c.default);

`)

module.exports = { // config //}

这是可行的!但正如你所看到的,这不是一个优雅的解决方案,而且很容易发生错误。那么,有没有人知道一个正确的方法来处理这种情况?

nqwrtyyt

nqwrtyyt1#

在这个答案中,我将假设,与问题类似,有一个主App.js文件,以及一个名为components的目录,其中包含文件A.jsB.jsC.jsD.js等。只有A.jsB.js需要捆绑,所有其他文件都应该从捆绑中排除,但这只有在编译时才知道。

require.context

Webpack提供了一种使用定制的、Webpack特定的require.context方法来指定一组依赖项的方法。require.context有四个参数:包含文件的目录的路径、确定是否包含子目录的布尔值、筛选文件的正则表达式以及加载模式的指令。
将所有文件捆绑到我们运行的组件目录中

// App.js
const req = require.context('./components')
  • (这超出了最初问题的范围,但您可以使用req来运行从这些文件导出的任何函数。例如,如果每个.js文件都导出了一个default函数,要运行每个default函数,您需要执行以下操作:*
const req = require.context('./components')
req.keys().forEach( key => {
    req( key )?.default()
} )

因为我们只想加载A.jsB.js,所以必须向require.context发送一个正则表达式来过滤文件。假设我们知道在写App.js时只需要A.jsB.js。我们可以用一个硬编码的正则表达式来过滤文件,如下所示:

// App.js
const req = require.context('./components', true, /(A|B)\.js$/)

/(A|B)\.js$/是一个简单的正则表达式,如果输入是A.jsB.js,则返回true。它可以很容易地扩展到允许更多的文件:/(A|B|C|D)\.js$/,或者只搜索特定的子目录。Webpack将在编译时计算正则表达式,并在包中只包含匹配的文件。
然而,问题仍然存在:当我们在编译时只知道文件名时,我们如何确定过滤器是什么?

webpack.DefinePlugin

使用webpack.DefinePlugin,我们可以在webpack.config.js中定义全局变量,这些变量是在编译时定义的,Webpack使其静态地可用于每个文件。例如,我们可以将前面的正则表达式放入一个全局变量__IMPORT_REGEX__

//webpack.config.js
module.exports = {
    ...
    plugins: [
        new webpack.DefinePlugin( {
            __IMPORT_REGEX__: "/(A|B)\\.js$/"
        } )
    ]
}
// App.js
const req = require.context('./components', true, __IMPORT_REGEX__)

注意,通过DefinePlugin创建的变量的值必须是字符串化的;也就是说,我们不能提供原始RegExp值。这意味着任何反斜杠都必须转义。
如果我们在数组中有文件的名称,也许以前从JSON文件中读取,我们可以在webpack.config.js中动态构建所需的正则表达式。在这个例子中,它是一个简单的join使用|字符作为分隔符的数组:

// webpack.config.js
const fileNames = ["A", "B"];

const importRegex = `/(${ fileNames.join("|") })\\.js$/`; // will return '/(A|B)\\.js$/'

module.exports = { ...

把一切都放在一起

// webpack.config.js
const fileNames = ["A", "B"];

const importRegex = `/(${ fileNames.join("|") })\\.js$/`;

module.exports = {
    ...
    plugins: [
        new webpack.DefinePlugin( {
            __IMPORT_REGEX__: importRegex
        } )
    ]
}
// App.js
const req = require.context('./components', true, __IMPORT_REGEX__)

最后一点:Typescript用户需要为__IMPORT_REGEX__声明一个全局

// interface.d.ts
declare global {
    var __IMPORT_REGEX__: RegExp
}

相关问题