如何使用带有可变查询字符串的 webpack 动态导入?

IT技术 javascript reactjs webpack import ecmascript-6
2021-05-20 11:55:56

使用 webpack 3 和 react,我可以导入这样的文件:

import(`src/Main.sass`).then(...do something)

我有一个用于根据查询字符串更改module内容的导入文件的加载程序,我可以成功导入这样的文件:

import(`src/Main.sass?theme=themename`).then(...do something)

但是当查询字符串是一个变量时:

const themeQuery = '?theme=themename';
import(`src/Main.sass${themeQuery}`).then(...do something);

或者

const theme = 'themename';
import(`src/Main.sass?theme=${theme}`).then(...do something);

我收到错误:

Error: Cannot find module '.src/Main.sass?theme=themename'.

我希望这能根据这里的信息工作

完全动态的语句,例如 import(foo),将会失败,因为 webpack 至少需要一些文件位置信息。这是因为 foo 可能是系统或项目中任何文件的任何路径。import() 必须至少包含一些有关module所在位置的信息,因此可以将捆绑限制为特定目录或文件集。

包括可能在 import() 调用中被请求的每个module。例如,import( ./locale/${language}.json) 将导致 ./locale 目录中的每个 .json 文件被捆绑到新的块中。在运行时,当变量语言被计算出来时,任何像 english.json 或 German.json 的文件都可以使用。

这不是一个完全动态的语句,它具有完整的文件路径(位置信息),可以在没有查询字符串的情况下毫无问题地导入。


编辑:我创建了一个可以复制测试仓库使用由 prop 定义的查询字符串src/Main.js导入当使用这种组件,你可以看到但是,使用完整的字符串,包括查询导入时,该文件是没有问题的进口。这可以通过用作组件来查看Main.component.sassthemeName<Main/>src/App.jsCannot find module './Main.module.sass?theme=themename' error.src/Main-no-var.js<Main>

这是用于此示例webpack 配置

2个回答

如果你看这部分文档

例如, import(./locale/${language}.json) 将导致 ./locale 目录中的每个 .json 文件被捆绑到新块中。在运行时,当变量语言被计算出来时,任何像 english.json 或 German.json 的文件都可以使用。

这意味着静态分析将搜索.json文件./locale/夹中的每个文件并将其打包,这意味着它将知道每个可能的目标。有了query string你就会有可能的目标基本上是无限的数字,因为query string可以是任何东西,因此将不能够知道什么捆绑。

我想这与dynamic path分辨率完全不同的功能在当前import()声明中显然不受支持

最好的办法是更改文件夹结构,.css为每个主题设置多个文件,然后dynamic path按照文档(语言示例)中的建议加载它,或者您可以执行以下操作

const themes = {
  "themename": () => import(
    `./Main.module.css?theme=themename`
  ),
  // other themes
}

通过这种方式,您将拥有更清晰的代码,显示可以传递给的所有可能的主题prop(静态分析会确切地知道是什么query string,所以它会起作用),然后您可以像这样使用它

componentDidMount = () => {
    const { themeName } = this.props
    themes[themeName]().then((result) => {
      this.setState({ theme: result })
    })
}

我宁愿按照您的建议使用查询字符串实现,但就目前而言,据我调查,这是不可能的。

我刚刚发现了这篇文章,它指出 Webpack 具有“加载策略”。

起初我认为下面例子中的注释是无害的,但不,正是它设置了 Webpack 的加载策略。

import(/* webpackMode: "eager" */ `assets/images/${imageName}.jpg`)

有四种不同的方法(lazy、lazy-once、eager、weak)。您可以在此处查看更详细的说明

所有学分都归功于文章的作者。

用例

我需要Quasar(一个很棒的 Vue.js 框架)中导入一个本地化版本的FirebaseUI,所以不是使用,它会因动态字符串而失败,迫使我对所需的语言进行硬编码:import

import * as firebaseui from 'firebaseui/dist/npm__it'

我现在使用:

import (/* webpackMode: "eager" */ `firebaseui/dist/npm__${appLanguage}`)

它允许我在导入语句之前评估语言,或者通过在浏览器中检测它,或者通过解析语言查询词的 URL。