鉴于ES2015,dependency injection和库抽象,2016年我的理想模块应该是什么样子?

如果不是这样的话,首先我要全部写上我所有的模块

import A from './a.js'; var B = function(){ //use A }; export default B; 

然后使用编译器将其构build为某种浏览器或服务器格式。

但是我的一个问题是在import明确./a.js

我明白为什么规范是这样做的1 ,赞成静态分析 。 但是有两个非常实际的原因,为什么在模块的文件名path中烘焙都很麻烦。

  1. 正如已经在这里提出的那样 ,当从项目到项目频繁地回收模块时,您很可能无法在项目树中保持一致的pathimport myModule from './../../vendor/lib/dist/mod.js'到一个模块的代码,这样的导入调用不会让我感觉到未来。
  2. 除了path本身之外,指定文件名还会将您closures。 像这样的东西似乎是无辜的:

    import $ from 'vendor/jquery.js'

    但是我想用Zepto代替jQuery呢? 我发现抽象,特别是在供应商库上的抽象,在处理大型代码库,自以为是的开发者和不断变化的JavaScript生态系统时非常有用。 我今天可能要导入React作为我的组件库,但明天呢? 此外,如果我要在客户端和服务器上使用相同的模块,但是我需要不同版本的从属库?

我坚持强有力(但明确而一致)的抽象。 通常,抽象已经采取了某种命名空间的forms。 我幻想一下这个:

 //BAD: Bakes React into my component modules import ComponentLib from './React.js'; //GOOD: Leaves me free to use any React-like library import ComponentLib from 'vendor.lib.component'; 

vendor.lib.component Java的方式, vendor.lib.component已经在某处注册过了。

请注意, 与这个问题不同 ,我的目标不是对我的导入进行dynamic控制。 我不想要运行时的灵活性,我想要灵活的构build时间 。 我应该能够为另一个依赖的框架,或为一个模拟,或在某些特定的环境中工作,而不必担心什么依赖我的模块调用,或试图复制一些疯狂的目录我为之后的每一个构build产品树。

类似的问题导致了一个利用系统规范的库的build议,如SystemJS 。 然后你可以使用类似jspm的东西来引入一个模块映射来获取抽象。 但是当我这样做的时候,我正在写所有的模块:

 System.import('a', function(A){ //use 'A' }); 

这是突然的未来吗? 如果是这样,为什么我不继续使用AMD? 为什么即使打算重新使用一个asynchronous的加载器API,甚至会打扰ES2015模块和运行转换器?

更让人eye目结舌的是 ,在ES2017规范中 ,我没有看到太多或者提及解决模块加载器API标准的问题 。

编辑:修改问题以符合非意见答案的标准

考虑到以上所有内容,我正在问社区 – 我如何编写一个JavaScript模块,该模块(i)遵守ES2015标准,(ii)不通过其文件名或path引用依赖模块,以及(iii)并不依赖于广泛的中间工具/configuration,这使得与多个团队共享模块成为可能。

注1 @zeroflagL在注释中注明,规范没有明确指出模块应该被指定为path,只是一个string(参见ModuleSpecifier – http://www.ecma-international.org/ecma-262/ 6.0 /#table-41 )。 但是,也有明确的说明来解释循环引用,这意味着某种静态分析( http://www.ecma-international.org/ecma-262/6.0/#sec-imports ),文件path似乎是这一点的select的参考上下文。 所以我们不能责怪规范在这里僵硬,而是相反。 然后,我们可能会负责开发更强大的import / ModuleSpecifier实现,以实现二级标准。

对我来说,这似乎是JS界最大的未解决的问题之一。 围绕依赖pipe理和dependency injection(至less据我所知)没有最佳实践。

关于这个线程有一个很长的讨论: 在NodeJS中是否需要dependency injection,或者如何处理…? ,但大多数解决scheme似乎只适用于特定情况,并且需要改变您以某种方式编写模块的方式。 最令人震惊的是很多答案都认为你甚至不需要DI。

我自己的问题的解决scheme是这个最小的DI框架 ,它可以让你定义模块一次,它会为你连接适当的依赖关系。

我知道,你正在寻求一个解决scheme,你可以专门用于JS编译器的情况下,但是因为你要求一个最佳实践,我觉得我们必须考虑到JavaScript依赖pipe理发生的完整的游戏场包括浏览器和Node.js等不同的主机环境,Webpack,Browserify和jspm等前端打包系统,以及HTML和HTTP / HTTP2等相邻技术。

ES模块的历史

在ECMA规范中找不到加载程序API是有原因的:当ECMA2015规范的截止date到达时,依赖性解决没有完成,并且决定ECMA规范只描述模块语法,而且主机环境(例如,浏览器,node.js,JS编译器/转译器)将负责通过它们的说明符parsing模块,通过一个名为HostResolveImportedModule的钩子,您可以在ECMA2015和ECMA2017规范中find它 。

ES2015模块模式背后的语义规范现在掌握在WHATWG的手中。 两个值得注意的事态发展是他们努力的成果:

  1. 他们已经确定 HTML中的模块可以用<script type="module">来表示。
  2. 有一个加载程序规范的草案 ,将考虑到不同的主机环境和前端封装系统。 这听起来像最终将有助于回答你的问题的规范,但不幸的是,它远远没有完成,并且还没有更新一段时间。

当前的实现

浏览器

通过添加<script type="module">标签,在浏览器中实现ES6模块的一切都变得简单了。 现在它的工作方式是不考虑性能(见这个和这个评论)。 此外,它不支持任何前端包装系统。 很显然,模块的概念已经被扩展到可以在生产网站中使用,因此浏览器供应商在其浏览器中没有急于实现它。

Node.js的

Node.js是一个完全不同的故事,因为它从一开始就实现了CommonJS风格的模块。 目前,不支持ES2015模块。 已经提出了两个独立的build议,将CommonJS模块中的ES6模块并入Node.js中。 这篇博客文章详细讨论了这些build议。

编译器和包装系统

  • Webpack 2 支持ES2015模块 。 此外,可以调整使用自定义依赖关系解决scheme 。
  • Babel支持ES2015模块,并可以将它们转换为AMD,CommonJS,SystemJS和UMD模块。 Webpack以外的软件包pipe理器似乎通过Babel来支持ES2015模块。

结论

所以当你要求一个最好的写模块的方法时,你可以看到这是非常困难的。 与所有可能的主机环境相容的解决scheme是可取的,因为:

  • 您可能想要在不同的环境中分享您的代码。
  • 有些开发人员(例如前端开发人员)可能会接受ES2015模块,而其他开发人员(例如Node.js开发人员)会坚持另一种解决scheme(例如CommonJS模块)。 这会进一步贬低跨环境代码的可能性。

但是从上面你会看到,不同的环境有不同的要求。 在这个意义上,对你的问题的最好的答案可能是:目前没有最好的写模块的方式来涵盖你的抽象问题。

但是,如果我现在正在编写编译为JS的ES2015模块,那么我将远离相对path,并始终使用项目根目录中的绝对path作为模块的标识符,我并不认为这是有问题的。 Java实际上以相同的方式镜像命名空间及其目录结构。 我将使用Webpack或Babel将我的源代码编译成可在当今JS环境中运行的代码。

至于你的其他问题,如果我想要能够替代供应商库,我可能会创build一个模块,将供应商库别名为我将在内部使用的名称。 有点像:

 // /lib/libs.js import jQuery from 'vendor/jquery.js' export const $ = jQuery; 

所有其他模块将从lib / libs.js中导入$,您可以通过在一个地方更改引用来切换供应商库。

如果您想遵循最佳实践,请遵循AirBnb JavaScript styleguide。 在我看来,最好和最完整的JavaScript风格指南在那里

https://github.com/airbnb/javascript#classes–constructors

import

这对于重用模块看起来很糟糕: import myModule from './../../vendor/lib/dist/mod.js'

在NPM(也可以是私有的或自托pipe的NPM)上发布你的模块,并像import myModule from 'my-module';import myModule from 'my-module';一样import myModule from 'my-module';

最后将NODE_PATH设置为根文件夹,并从根目录引用模块。

在package.json中

 'start': 'NODE_PATH=. node index.js' // in Windows 'start': 'NODE_PATH=. && node index.js' 

现在像这样导入:

 import myModule from 'vendor/lib/dist/mod.js' 

variables

var不是ES6的一部分。 使用:

  • constant – 当variables的值不会改变时,也是对象和导入。 即使对象的参数改变,它仍然是一个常量。

  • let – 当variables的值改变时,即for(let = i; i < 100; i++)

  • 从我自己的经验总是设置为默认的const ,只有改变, let如果ESLint抱怨(顺便说一句,使用ESLint http://eslint.org/

现在有一个正确的方法来定义JavaScript中的类

 class B { constructor() { } doSomething() { } } 

你的例子更新了:

 import A from './a'; Class B { constructor() { } doSomething() { } }; export default B; 

如果你想扩展A:

 import A from './a'; Class B extends A{ constructor(argumentA, argumentB) { super(argumentA, argumentB); this.paramA = argumentA; } }; export default B; 

提示

  • 使用NPM作为构build工具的Webpack。 不要使用Gulp或Grunt
  • 使用Babel来传输代码(JSX loader可能不够用)
  • 学会根本不使用jQuery,而是select正确的polyfills和工具来完成NPM需要做的工作
  • github上有很多精心制作的样板库,所以从最好的地方偷取。 这里有一些我正在使用的React。

我的答案实质上是:

你要求的模式/库是AirBnb JavaScript styleguide,忘记了jQuery