通配符或星号(*)与命名或选择性导入ES6 JavaScript

80

只是想知道使用import的最佳方式是哪一种:

import * as Foo from './foo';

VS:

import { bar, bar2, bar3 } from './foo';

就效率而言,比如说,我正在使用webpack来打包所有的JavaScript文件。即使我在主代码中没有使用它们,第一个文件是否会导入所有内容?
一些我能找到的参考资料是:
Airbnb风格指南中,他们建议不要使用通配符,这样就会始终有默认的导入对象,以及this

4个回答

47

如果您使用webpack与新的uglify提供的死代码消除,或者使用tree-shaking的rollupjs,则未使用的导入将被剥离。

我部分同意airbnb风格指南不使用通配符导入,尽管JavaScript的通配符导入不像例如Python或Java的通配符导入那样患有相同的问题,即它不会在范围内污染由其他模块定义的变量名称(当使用import * as moduleB from ...时,您只能通过moduleB.foo而不是foo来访问它们)。

关于测试文章:我有点理解这些问题,但我认为没有什么是不能解决的。您可以使用一些自定义模块加载器(自定义amd模块加载器只需15行代码)来模拟导入本身,因此您无需干扰已测试模块的本地作用域。


2
如果我使用 import * as Foo from './foo',并且只使用 Foo.bar,那么 Foo.bar2Foo.bar3 会被消除为死代码吗? - brietsparks
3
是的,如果 Foo.bar2Foo.bar3 完全没有被使用。 - Tamas Hegedus
6
我可以帮忙翻译。这个问题的意思是:“我想了解更多信息,比如官方文档或Github上的问题讨论。” - Jason Kuhrt

37

关于问题的这个部分:

即使我在主要代码中没有使用它们,第一个是否会实际导入所有内容?

以下是使用 Babel 6.26 编译的方式:

命名

import { bar, bar2, bar3 } from './foo';

... 变成了 ...

'use strict';

var _foo = require('./foo');

通配符

import * as Foo from './foo';

...变成了...

'use strict';

var _foo = require('./foo');

var Foo = _interopRequireWildcard(_foo);

function _interopRequireWildcard(obj) { 
    if (obj && obj.__esModule) { 
        return obj;
    } else {
        var newObj = {}; 
        if (obj != null) { 
            for (var key in obj) { 
                if (Object.prototype.hasOwnProperty.call(obj, key))
                    newObj[key] = obj[key];
            }
        }
        newObj.default = obj; 
        return newObj;
    }
}

两种情况都是通过 require 导入整个文件。

使用通配符导入时,会定义一个 _interopRequireWildcard 函数,并用于将所有导出分配给命名空间变量。

值得注意的是,编译后的代码只包含单个 _interopRequireWildcard 定义,以及每个导入都需要一次 require_interopRequireWildcard 调用。

最终,使用通配符导入将涉及更多的运行时处理,并导致编译后 js 大小略微增加。


1
很高兴看到在 webpack 4 中,它的摇树优化功能是否能够成立。 - Kostiantyn Ko
1
开销在 Babel 中,如果使用 Rollup.js 则没有开销。 - Tamas Hegedus

11

由于在现代WebPack设置下,两者将生成相同的编译/转译JS代码,因此命名导入的真正价值在于它更加表达。通过为导入命名,您告诉打开文件的任何人您将使用哪个模块中的函数。这种方法有助于编写测试时,如果需要模拟,您就可以获得一个明确的导入列表以进行模拟。


8

我同意 @Tamas 的观点。
如果你需要在目标文件中完全访问所有导出,那么可以使用 import * as Foo from './foo'; 或者 import foo from './foo':

但是,如果你需要使用特定的函数或常量,则最好避免使用 "import *" 并明确说明你需要做什么。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接