【问题标题】:ES6 circular dependency in react projectreact项目中的ES6循环依赖
【发布时间】:2019-06-27 14:51:27
【问题描述】:

我刚刚使用 react native 建立了一个小测试项目。这对我来说是全新的(包括 ECMAScript 6)。 Eslint 告诉我一些关于“循环依赖”的事情,我不知道如何解决这个问题。代码仍然有效。

我的 package.json:

...
"dependencies": {
    "axios": "^0.19.0",
    "node-sass": "^4.12.0",
    "react": "16.8.3",
    "react-native": "0.59.9",
    "react-navigation": "^3.11.0"
},
"devDependencies": {
    "@babel/core": "7.4.5",
    "@babel/runtime": "7.4.5",
    "babel-eslint": "^10.0.1",
    "babel-jest": "24.8.0",
    "babel-plugin-module-resolver": "^3.2.0",
    "eslint": "^5.16.0",
    "eslint-config-airbnb": "^17.1.0",
    "eslint-import-resolver-babel-module": "^5.1.0",
    "eslint-plugin-import": "^2.17.3",
    "eslint-plugin-jsx-a11y": "^6.2.1",
    "eslint-plugin-react": "^7.13.0",
    "jest": "24.8.0",
    "metro-react-native-babel-preset": "0.54.1",
    "react-dom": "^16.8.6",
    "react-test-renderer": "16.8.3"
},
...

src/index.jsx 是主要的 JSX 文件:

import { Comp1 } from 'components';
...

我创建了一个src/components/index.jsx 来启用类似的导入

import { Comp1, Comp2, Comp3 } from 'components'

而不是

import { Comp1 } from 'components/comp1';
import { Comp2 } from 'components/comp2';
import { Comp3 } from 'components/comp3';

文件src/components/index.jsx 看起来像:

export * from './button';
export * from './comp1';
...

src/components/button/index.jsx:

import React from 'react';
import {
  Text,
  TouchableOpacity
} from 'react-native';
import style from './style';

const Button = ({ onPress, children }) => {
  const {
    buttonStyle,
    textStyle
  } = style;

  return (
    <TouchableOpacity onPress={onPress} style={buttonStyle}>
      <Text style={textStyle}>
        {children}
      </Text>
    </TouchableOpacity>
  );
};

export default Button;
export { Button };

src/components/comp1/index.jsx:

import React from 'react';
import {
  Text,
  View
} from 'react-native';
import { Button } from 'components';
import style from './style';

const Comp1 = (props) => {
  const {
    textStyle,
    viewStyle
  } = style;

  return (
    <View style={viewStyle}>
      <Text style={textStyle}>some text</Text>
      <Button>Test</Button>
    </View>
  );
};

export default Comp1;
export { Comp1 };

运行此设置会产生 eslint 错误 import/no-cycle。代码本身有效。

如果我将src/components/comp1/index.jsx 中的import { Button } from 'components' 更改为import { Button } from 'components/button',则会弹出no eslint 错误。

我想使用上面描述的这种简短的导入语法,而不会失去在彼此内部使用模块的可能性。有什么办法吗?

【问题讨论】:

  • 这段代码的运行时结构是什么?您是在转译为 CommonJS(例如,require 调用),还是将 ESM(importexport 声明)留在浏览器看到的转译结果中?
  • 尝试使用npmjs.com/package/madge检查此类错误,非常有用。
  • @T.J.Crowder 老实说,我不知道 React Native 项目中是否生成了 CJS 或 ESM。我只知道这里使用 babel 来转译代码。 “Babel [...] 主要用于将 ECMAScript 2015+ 代码转换为向后兼容的 JavaScript 版本”(参见 babeljs.io/docs/en)。
  • @T.J.Crowder Facebook 告诉我JavaScriptCore 用于当前实现ECMAScript 的react 本机应用程序,如ECMA-262 specification。这是否自动意味着我们在这里谈论 ESM?有人知道吗?
  • 我知道 JavaScriptCore 支持 ESM,但这并不一定意味着 React Native 正在使用它,尤其是。因为 React Native 早于 JavaScriptCore 对 ESM 的支持。 (抱歉,还没有玩过 React Native。)我希望可以查看实际转译的 .js 文件以了解它们使用的模块语法...

标签: javascript reactjs ecmascript-6


【解决方案1】:

您的结构components/index.jsxcomp1/index.jsx (以及其他你有同样东西的地方)之间建立了一个循环依赖关系。 comp1/index.jsxcomponents/index.jsx 导入,从 comp1/index.jsx 导入。

实际本机 ESM¹ 模块和 CJS² 或类似模块之间的周期运行时处理不同。两者都需要仔细处理周期,但它们的行为方式略有不同。如果您在捆绑器或类似工具中将 ESM 转换为 CJS,这可能会特别令人困惑。

当两个模块之间存在循环依赖关系时(为了简单起见),这意味着在某些时候两个模块中的一个将在另一个之前运行,这意味着它从另一个模块获取的任何导入都将被未初始化 (ESM)或尚未定义(CJS)。因此,两个模块之一中的 顶级代码 不能依赖现有的导入。在 ESM 中,尝试使用未初始化的导出会引发错误;在 CJS 中,导出的值只是 undefined

在您的示例中,我不认为这是一个问题,因为循环中模块中的顶级代码不使用循环中其他模块的导入,它仅用于调用的函数中稍后(comp1/index.jsx 使用Button,但仅当Comp1调用,并且它不在顶层代码中调用。(过程可能比这更复杂,但是.. .)

如果代码经过测试并且工作正常,您可以使用configuration comments 为组件文件禁用该规则(如果它允许您这样做)。这样一来,您就可以在全局范围内保留规则,以防实际出现问题的周期,而不必为这些您已经测试并知道是好的周期而烦恼。


¹ ESM = ECMAScript Modules,真正的原生 JavaScript 模块,如果您仅使用 import,则可以进行静态分析和export 声明(不是import() 动态导入)。

² CJS = CommonJS,使用requireexports 对象的动态模块结构。

【讨论】:

  • 我现在知道了。大概。为了能够导出像export * from './button'; 这样的模块,需要导入它,这在此处自动完成。没有这么想过。就这样转来转去。但无论如何,这里唯一的解决方案是丢弃包含所有导出的index.jsx,并使用像import { Comp1 } from 'components/comp1';这样的导入语句?
  • @iwanuschka - 不一定。正如您评论的那样,我正在编辑答案。 :-)
  • 如何为整个项目禁用此规则?
  • @theprogrammer - 我假设你可以像任何其他规则一样在your configuration 中禁用它。
  • @theprogrammer - 我也是。我想可能是import/no-cycle
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-18
  • 2020-03-14
  • 2018-03-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多