原文地址
这篇文章探索ES6 modules,在编译器的帮助下展示他们如何被使用到今天
几乎每种语言都有模块的概念- 一种在另一个文件中包含功能性的声明的文件。一般的,开发者创建一个封装好的表示处理相关任务的代码库。这个库可以被其他应用或者其他模块引用。
模块的益处:
- 代码可以被分割成更小的独立功能文件
- 同一个模块可以被多个应用共享
- 理想情况,模块不需要其他开发者检查其可用性,因为他们已经被证实可以运行。
- 代码里引用一个模块可以理解这是一个依赖。如果这个模块文件修改了或者移动了,问题会立马显现。
- 模块代码通常帮助我们彻底根除命名冲突。module1中的x()函数不会与modules2中的x()函数发生冲突。使用了类似命名空间的选项,因此调用变成了module1.x()和module2.x().
javascript的模块在哪里
几年前开始web开发的人会惊奇的发现在JavaScript中没有模块的概念。在另一个js文件中是不可能直接引用或者包含一个JavaScript文件的。因此,开发人员寻找其他选择。
多个html
在2018年,平均每个网页使用了25个分割的scripts,但这不是一个切实的解决方案:
- 每个脚本会发起一个新的HTTP请求,这会影响页面的性能。HTTP/2在某种程度上缓解了这个问题,但这对其他域(例如CDN)上引用的脚本无济于事
- 每个脚本在运行时都会阻塞进一步的处理
- 依赖管理是一个人工处理的过程.在代码中,如果lib1.js引用lib2.js的代码,这部分代码可能出错,因为它没有加载。这有可能损害深层次的JavaScript程序。
- 函数可能覆盖其他函数,除非使用合适的模块模式。早期的JavaScript库因使用全局函数名称或覆盖原生方法而臭名昭著。
脚本级联
解决多个
或内联:
无论模块在页面或其他模块中被引用多少次,模块都会被解析一次。
服务器注意事项
模块必须使用MIME类型application/javascript提供服务。大多数服务器将自动执行此操作,但对动态生成的脚本或.mjs文件保持警惕(请参阅下面的Node.js部分)。
常规
模块背后
没有模块支持的浏览器将不会运行type="module"脚本。可以为备用脚本提供nomodule属性,而模块兼容的浏览器会忽略该属性。例如:
您应该在浏览器中使用模块吗?
浏览器支持正在增长,但切换到ES6模块可能还为时过早。目前,最好使用模块捆绑器来创建一个可在任何地方使用的脚本。
在Node.js中使用ES6模块
当Node.js在2009年发布时,对于任何运行时不提供模块来说都是不可思议的。 采用CommonJS,这意味着可以开发Node软件包管理器npm。 从那时起,使用量呈指数增长。
CommonJS模块的编码方式类似于ES2015模块。 使用module.exports而不是export:
// lib.js
const PI = 3.1415926;
function sum(...args) {
log('sum', args);
return args.reduce((num, tot) => tot + num);
}
function mult(...args) {
log('mult', args);
return args.reduce((num, tot) => tot * num);
}
// private function
function log(...msg) {
console.log(...msg);
}
module.exports = { PI, sum, mult };
require(而不是import)用于将此模块拉入另一个脚本或模块:
const { sum, mult } = require('./lib.js');
console.log( sum(1,2,3,4) ); // 10
console.log( mult(1,2,3,4) ); // 24
require还可以导入所有项目:
const lib = require('./lib.js');
console.log( lib.PI ); // 3.1415926
console.log( lib.add(1,2,3,4) ); // 10
console.log( lib.mult(1,2,3,4) ); // 24
所以ES6模块很容易在Node.js中实现,对吗? 呃没有。
ES6模块位于Node.js 9.8.0+中的标志后面,直到至少版本10才能完全实现。 语法,它们以根本不同的方式工作:
- 在执行代码之前,已预先解析了ES6模块,以解决进一步的导入问题。
- CommonJS模块在执行代码时按需加载依赖项。
在上面的示例中没有什么区别,但是请考虑以下ES2015模块代码:
// ES2015 modules
// ---------------------------------
// one.js
console.log('running one.js');
import { hello } from './two.js';
console.log(hello);
// ---------------------------------
// two.js
console.log('running two.js');
export const hello = 'Hello from two.js';
ES2015输出
running two.js
running one.js
hello from two.js
使用CommonJS编写的类似代码:
// CommonJS modules
// ---------------------------------
// one.js
console.log('running one.js');
const hello = require('./two.js');
console.log(hello);
// ---------------------------------
// two.js
console.log('running two.js');
module.exports = 'Hello from two.js';
CommonJS输出
running one.js
running two.js
hello from two.js
在某些应用程序中,执行顺序可能很关键,如果将ES2015和CommonJS模块混合在同一文件中,会发生什么情况? 要解决此问题,Node.js将仅允许文件扩展名为.mjs的ES6模块。 扩展名为.js的文件将默认为CommonJS。 这是一个简单的选项,可消除大部分复杂性,并有助于代码编辑器和lint。
是否应该在Node.js中使用ES6模块?
ES6模块仅适用于Node.js v10及更高版本(于2018年4月发布)。 转换现有项目不太可能带来任何好处,并且会使应用程序与Node.js的早期版本不兼容。
对于新项目,ES6模块提供了CommonJS的替代方法。 语法与客户端编码相同,并且可以提供更容易的同构JavaScript路由,该JavaScript可在浏览器或服务器上运行。
模块竞争
标准化的JavaScript模块系统花了很多年才能实现,甚至花了更长的时间才能实现,但问题已得到纠正。从2018年中期开始,所有主流浏览器和Node.js都支持ES6模块,尽管在每个人升级时都可能会有切换滞后的情况。
立即学习ES6模块,以使您明天的JavaScript开发受益。