在很多 JavaScript 项目中,不管是前端 React 项目,还是后端 node.js 项目你可以通过一个 index.js 文件来聚合所有的导入。在一个文件中导出所有相关的 exports 会方便许多,接着其它文件可以从这个文件中导入内容。它看上去简洁,但可能会很快失控。
TLDR:
为何你应该避免聚合所有的导入到一个文件 index.js 中?
- 循环引用;
- 使用动态导入时的意外导入;
- nodejs 的作者也说要避免使用;
什么时候可以 index.js ?
当你创建 npm 包时,建议使用 index.js 。
典型的 index.js 长什么样子?
典型的 index.js 文件像下面这样,可以聚合所有的导出在一个文件中。
1. 循环引用
虽然时间的推移 index.js 文件变得越来越大,此时一位开发者利用自动引入功能,在一个相邻文件中引入了 index.js 中的内容。
现在,该开发者又通过 index.js 文件导出了一些内容,就像其他人做的一样。
现在问题是什么?他制造了一个循环引用。
什么?"composition.js" 从 "index.js" 中导入 Items,"index.js" 导入了 "composition.js" 中的 "CompostionsOfItems" 并导出。
composition.js → index.js → composition.js
现在,一个(例如,items.js)从 index.js 文件中导入一些变量的文件将会抛出错误:cannot read property "Item1" of undefined
。
一些编译工具可以应付这种循环导入(例如:带有 babel 的 webpack),但是其他例子(jest + ts-jest、jest + bable)的编译无法处理这种问题,将会抛出不清晰的错误信息。这很难调试但是当你知道避免使用 index.js 时,此种问题将会很少。
问题:那我们该如何做 ?
回答: 避免使用 index.js 同时只从文件中引入你需要的内容。在我们这个例子中,从 items.js 中引入 Item1 和 Item2 就不会产生问题。
避免使用 index.js 并不能保证不会产生循环引用,你必须把导入语句组织成一个树状结构。
避免循环引用的另一种方法是使用 eslint-plugin-import 中的 Eslint no-cycle
规则:
2. 使用动态导入时的意外导入
当在写前端项目时,通常是要考虑使用动态导入来分割代码。例如:在 React 中你可以使用 React.lazy()
使代码分割为多块。使用哪个框架并不重要,但我们以写 react app 为例,你把所有的 routes 整洁的聚合与 routes.js 文件中:
// routes.js
export Route1 from 'route1'
export Route2 from 'route2'
export Route3 from 'route3'
export Route4 from 'route4'
若你打算利用懒加载来使你的应用更快,你可能会写出下面的代码:
const LazyRoute1 = React.lazy(() => import('./routes').then(module => ({ default: module.Route1 }));
罪魁祸首是什么?你实际上是一次性请求了所有的路由而不是只按需加载了 Route1 ,导致使用了比预期更多的带宽同时也使懒加载和代码分割功能失效。
3. nodejs 的作者也说避免使用 index.js
在 JSConf EU 的会议上,nodejs 的作者 Ryan Dahl 演示了关于 nodejs 的 10 件遗憾的事,其中一个就是 index.js 文件。那是一个很好的演讲我建议你看下:
总结:10 Things I Regret About Node.js — Ryan Dahl
视频:10 Things I Regret About Node.js – Ryan Dahl – JSConf EU
什么时候你应该使用 index.js ?
如果你创建了一个开源的包同时希望导出公共模块,通常是使用一个文件来暴露出所有需要导出的东西。
另外我个人加一个非作者所写,就是当搜文件或文件编辑器里打开了很多 index.js 时,会让我抓狂,根本不知道打开了什么文件:
我的个人建议是:业务代码中少用 index.js 来组织代码。
原文链接:https://medium.com/@alonmiz1234/why-you-should-avoid-index-js-3321a9902120