如何有条件地导入 ES6 模块
How can I conditionally import an ES6 module?
我需要做这样的事情:
if (condition) {
import something from 'something';
}
// ...
if (something) {
something.doStuff();
}
上面的代码不编译;它抛出SyntaxError: ... 'import' and 'export' may only appear at the top level
。
我尝试使用此处所示的System.import
,但我不知道System
来自哪里。这是一个最终没有被接受的ES6提案吗?该文章中指向"编程 API"的链接将我转储到一个已弃用的文档页面。
我们现在确实有ECMA的动态导入提案。这是在第 3 阶段。这也可以作为 babel 预设使用。
以下是根据您的情况进行条件渲染的方法。
if (condition) {
import('something')
.then((something) => {
console.log(something.something);
});
}
这基本上返回了一个承诺。承诺的解决方案预计将具有该模块。该提案还具有其他功能,例如多个动态导入,默认导入,js文件导入等。您可以在此处找到有关动态导入的更多信息。
如果你愿意,你可以使用require。这是一种拥有条件要求语句的方法。
let something = null;
let other = null;
if (condition) {
something = require('something');
other = require('something').other;
}
if (something && other) {
something.doStuff();
other.doOtherStuff();
}
你不能有条件地导入,但你可以做相反的事情:有条件地导出一些东西。这取决于您的用例,因此此解决方法可能不适合您。
你可以做:
接口.js
import mockAPI from './mockAPI'
import realAPI from './realAPI'
const exportedAPI = shouldUseMock ? mockAPI : realAPI
export default exportedAPI
apiConsumer.js
import API from './api'
...
我用它来模拟分析库,如mixpanel等......因为我目前不能有多个构建或我们的前端。不是最优雅的,但有效。根据环境的不同,我在这里和那里只有几个"如果",因为在 mixpanel 的情况下,它需要初始化。
2020 年更新
您现在可以将 import
关键字作为函数调用(即 import()
( 在运行时加载模块。它返回一个 Promise,该承诺解析为具有模块导出的对象。
例:
const mymodule = await import('modulename');
const foo = mymodule.default; // Default export
const bar = mymodule.bar; // Named export
或:
import('modulename')
.then(mymodule => {
const foo = mymodule.default; // Default export
const bar = mymodule.bar; // Named export
});
请参阅 MDN 上的动态导入
看起来答案是,到目前为止,你不能。
http://exploringjs.com/es6/ch_modules.html#sec_module-loader-api
我认为目的是尽可能多地启用静态分析,而有条件导入的模块会打破这一点。另外值得一提的是 - 我正在使用 Babel,我猜 Babel 不支持System
,因为模块加载器 API 没有成为 ES6 标准。
如果您使用动态导入 Webpack 模式eager
,则重要区别:
if (normalCondition) {
// this will be included to bundle, whether you use it or not
import(...);
}
if (process.env.SOMETHING === 'true') {
// this will not be included to bundle, if SOMETHING is not 'true'
import(...);
}
在 JS 中有条件地导入和导出
const value = (
await import(`${condtion ? `./file1.js` : `./file2.js`}`)
).default
export default value
require()
是一种在运行时导入某些模块的方法,如果与字符串文字路径一起使用,它同样有资格进行静态分析,例如import
。这是捆绑程序为捆绑包选择依赖项所必需的。
const defaultOne = require('path/to/component').default;
const NamedOne = require('path/to/component').theName;
对于具有完整静态分析支持的动态模块解析,首先在索引器(index.js(中索引模块,然后在主机模块中导入索引器。
// index.js
export { default as ModuleOne } from 'path/to/module/one';
export { default as ModuleTwo } from 'path/to/module/two';
export { SomeNamedModule } from 'path/to/named/module';
// host.js
import * as indexer from 'index';
const moduleName = 'ModuleOne';
const Module = require(indexer[moduleName]);
条件导入也可以通过三元和require()
s来实现:
const logger = DEBUG ? require('dev-logger') : require('logger');
此示例取自 ES Lint 全局需求文档:https://eslint.org/docs/rules/global-require
评估中掩盖它对我有用,将其隐藏在静态分析器中......
if (typeof __CLI__ !== 'undefined') {
eval("require('fs');")
}
我能够使用立即调用的函数和 require 语句来实现这一点。
const something = (() => (
condition ? require('something') : null
))();
if(something) {
something.doStuff();
}
查看此示例以清楚地了解动态导入的工作原理。
动态模块导入示例
对导入和导出模块有基本的了解。
JavaScript modules Github
Javascript Modules MDN
不,你不能!
但是,遇到这个问题应该会让你重新考虑如何组织代码。
在 ES6 模块之前,我们有使用 require(( 语法的 CommonJS 模块。这些模块是"动态的",这意味着我们可以根据代码中的条件导入新模块。- 来源: https://bitsofco.de/what-is-tree-shaking/
我想他们放弃对 ES6 的支持的原因之一是编译它将非常困难或不可能。
可以通过以下链接了解有关动态导入的更多信息
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports
问题所要求的,但这是我在使用 vite 时使用模拟的方法。我相信我们可以对 webpack 和其他做同样的事情。
假设我们有两个具有相同接口的库:link.js
和 link-mock.js
,那么:
在我的vite.config.js
export default defineConfig(({ command, mode }) => {
const cfg = {/* ... */}
if (process.env.VITE_MOCK == 1) {
cfg.resolve.alias["./link"] = "./link-mock"; // magic is here!
}
return cfg;
}
法典:
import { link } from "./link";
在控制台中,我们调用:
# to use the real link.js
npm run vite
# to use the mock link-mock.js
VITE_MOCK=1 npm run vite
或
包.json 脚本
{
....
"scripts": {
"dev": "vite",
"dev-mock": "VITE_MOCK=1 vite"
}
}
正如 ericsoco 所说,而不是:
import {ExampleClass} from "./ExampleClass.js";
new ExampleClass();
你可以写
if(condition) {
import("./ExampleClass.js").then((module) => {
new module.ExampleClass();
});
}
我也有类似的情况。我的项目结构是这样的:
- 库/
- 模拟Api.js
- realApi.js
- 索引.js
对我来说,在生产模式下模拟没有进入捆绑包是必要的。对我来说,不要在每个使用的地方都写条件,不要使用Promises,这一点也很重要。
对我来说,解决方案是创建一个统一的 api.js 文件,其中包含代码:
// solution 1
export const api = (
await import(`${process.env.NODE_ENV === 'development' ? './mockAPI.js' : './realAPI.js'}`)
).default
export default api;
在生产模式下使用这种方法,只有经过处理的 realAPI .js 才能进入捆绑包,并且解决方案的使用不需要单独的条件或使用 Promises,例如:
import api from './libs/api';
api.getUser();
也可以使用类似的解决方案:
// solution 2
let api = (await import('./realAPI')).default;
if (process.env.NODE_ENV === 'development') {
api = (await import('./mockAPI')).default;
}
export default api;
这两种解决方案都允许在生产模式下的捆绑包中不包含"模拟"。这是通过在构建过程中删除无法访问的代码来完成的,重要的是不要将process.env.NODE_ENV ==="开发">条件移动到变量中。
- 在一个表达式中加载并使用ES6模块
- ES6模块进口是否吊装
- 使用ES6模块创建JS
- 无法使用Webpack加载ES6模块
- 可以'I don’我不明白;使用webpack和es6模块进行EventEmitter
- 观察ES6模块属性
- NPM:取决于本地 ES6 模块(无转译)
- 使用 Babel 转译器导入类并调用带有 ES6 模块的静态方法
- 使用 Babel 将 ES6 模块转换为 ES5 AMD 模块,无法按预期工作
- ES6模块导入和依赖关系管理
- browserify/requirejs模块和ES6模块有什么区别
- 使用es6模块:未捕获ReferenceError:define未定义
- 如何通过名称(从模块本身)访问ES6模块变量
- 如何有条件地导入 ES6 模块
- 使用webpack和babel加载程序导入导出ES6模块
- ES6模块初始化顺序错误
- ES6模块:为什么先前导出的模块不为“;“孩子”;模块
- 流星 ES6 模块相对路径
- ES6 模块不适用于 babel 6 和 gulp
- 使用 webpack 和 ts-loader 重新导出 ES6 模块