新的 JavaScript 模块系统

4 年前

下一代 JavaScript 为我们带来了模块系统,它很大程度上受到了 Node.js 模块的启发。下面解释下它如何工作的。

创建模块

我们要开发一个简单的 asap 模块,它允许你安排一些马上执行的任务,不过是通过异步的方式。在 Node.js 中,你可以使用 process.nextTick,但是在不同的浏览器中就有很多不同的实现方案。我们将创建一个可以工作在任何环境下的模块。

开始,我们为这个模块创建一个文件。命名成 asap.js。这个模块只提供了一个简单的函数,在 JavaScript 中称作默认的 export。可以使用 export default 从模块中 export 一个默认值。

var asap;
var isNode = typeof process !== "undefined" &&
    {}.toString.call(process) === "[object process]";
if (isNode) { 
    asap = process.nextTick;
} else if (typeof setImmediate !== "undefined") { 
    asap = setImmediate;
} else { 
    asap = setTimeout;
}
export default asap;

导入模块

我们可以使用 import 语法导入 asap 到另外一个模块:

import asap from "asap";
asap(function() {
    console.log("hello async world!");
});

这种语法获得了从 asap export 的默认函数,并把它存储在一个名为asap的变量中,然后我们就可以去调用这个函数。

具名 export

一些模块需要输出多个export,这样他们的使用者可以通过名字来单独引用。

比如 jQuery 有一个单独的主要的export(jQuery函数),和一些其他的具名的export(ajax,getJSON,animate等)。在 Node.js 中,mkdirp 模块有一个单独默认export,可以递归地创建目录,还有一个叫做 sync 的具名export,做的事情一样,只不过是异步的方式。

在我们的示例中,除了默认的export之外,asap 模块可能会提供一个 later 函数,在其他网络请求或者UI工作完成后执行某个函数。

我们的模块看起来和之前的类似,除了增加了一个的新的export申明。

var asap;
var isNode = typeof process !== "undefined" &&
    {}.toString.call(process) === "[object process]";
if (isNode) {
    asap = process.nextTick;
} else if (typeof setImmediate !== "undefined") {
    asap = setImmediate;
} else {
    asap = setTimeout;
}
export default asap;
export var later = isNode ? process.setImmediate : asap;

具名import

既然我们export了 later,就可以把它导入到另一个模块中。

import { later } from "asap";
later(function() { 
    console.log("Running after other network events");
});

更有意思的是,你可以使用同一个import,同时导入默认的export和数个具名的export:

import asap, { later } from "asap";

有它就够了!

快捷方式

给具名的导入重命名

有些时候当导入一个具名的export时,想需要给它起一个局部的名字。

import { unlink as rm } from "fs";
rm(filename, function(err) {
    /* check errors */
});

导入成命名空间

可以把一个模块的所有具名export导入到一个单独的局部命名空间,这可能更加的方便。

import * as fs from "fs";
fs.unlink(filename, function(err) {
    /* check errors */
});

简短的具名export

你可以在JavaScript中任意声明(像var或者function)一个具名的export,添加export前缀就行。

// exports this function as "requestAnimationFrame" 
export function requestAnimationFrame() {
    // cross-browser requestAnimationFrame 
}
// exports document.location as "location" 
export var location = document.location; 

这对一些新的声明同样有效,比如 class 和 let。

// exports this class as "File" 
export class File() {
    /* implementation */
}
// exports "0.6.3" as "VERSION" 
export let VERSION = "0.6.3";

这些命名在模块的局部作用域中同样也可以访问,所以你可以在其他的函数里使用它们。

具名export分组

你可以导出任意个局部变量。

export { getJSON, postJSON, animate };
function getJSON() {
    // implementation 
}
function postJSON() {
    // implementation 
}
function animate() {
    // implementation 
}

你可以把已经分组的export声明放在文件中的任意位置,同样如果愿意,你可以在你的模块的顶部把import和export。

特性

JavaScript 模块有很多不错的特性,可以平滑且无缝在重要的场景中使用,包括重构和工具。

  • JavaScript模块支持同时在默认export和具名export之间的晚绑定轮询。都可以工作。
  • JavaScript模块区分了存在于默认export(和它们的原型链)和其它具名export,避免冲突。
  • JavaScript模块使得仅仅通过语法就可以准确地判定你导入了哪些变得更加容易。增加了错误信息,但也使得创建类似browserify和JSHint这类工具更加的容易,并且在没有警告的情况下稳定的工作。

日志

这个库的将会更加的详细,但是这些也是我们全心全意要做的。

原文:JavaScript Modules

0
推荐阅读