0

0

详解ES6 Modules

青灯夜游

青灯夜游

发布时间:2019-11-29 17:45:45

|

2517人浏览过

|

来源于segmentfault

转载

详解ES6 Modules

当下, 我们几乎所有的项目都是基于 webpack、rollup 等构建工具进行开发的,模块化已经是常态。

我们对它并不陌生,今天,我们就再系统的回顾一下ES6的模块机制, 并总结下常用的操作和最佳实践, 希望对你有所帮助。

一些简单的背景

随用随取, 是一种我们都希望实现的机制。

在 Javascript 中也一样,把一个大的 Javascript 程序分割成不同的部分, 哪个部分要被用到,就取那一部分。

【相关课程推荐:JavaScript视频教程

在很长一段时间内, NodeJS 拥有这样的能力, 后来, 越来越多的库和框架也拥有了模块化的能力, 比如 CommonJS, 或者基于AMD模型的实现(比如RequireJs),还有后续的Webpack, Babel等。

到2015年,一个标准的模块化系统诞生了,这就是我们今天要说的主角 - ES6 模型系统。

一眼看上去, 我们不难发现, ES6的模型系统和CommonJS语法非常的相似,毕竟ES6 的模型系统是从CommonJS时代走过来的, 深受CommonJS 影响。

看个简单的例子,比如在CommonJs中: (https://flaviocopes.com/commonjs/)

//file.js
module.exports = value;

// 引入value
const value = require('file.js')

而在ES6中:

// const.js
export const value = 'xxx';


import { value } from 'const.js'

语法是非常相似的。

下面我们就主要看 import 和 export,和几个相关的特性,了解ES6 Modules的更多方面。

模块化的好处

模块化的好处主要是两点:

1. 避免全局变量污染
2. 有效的处理依赖关系

随着时代的演进, 浏览器原生也开始支持es6 import 和 export 语法了。

1.png

先看个简单的例子:

<script type="module">
  import { addTextToBody } from '/util.js';

  addTextToBody('Modules are pretty cool.');
</script>

// util.js 
export function addTextToBody(text) {
  const p = document.createElement('p');
  p.textContent = text;
  document.body.appendChild(p);
}

如果要处理事件,也是一样, 看个简单的例子:

<button id="test">Show Message</button>
<script type="module" crossorigin src="/showImport.js"></script>

// showImport.js
import { showMessage } from '/show.js'

document.getElementById('test').onclick = function() {
  showMessage();
}

// show.js
export function showMessage() {
  alert("Hello World!")
}

如果你想跑这个demo, 注意要起个简单的服务:

$ http-server

否则,你会看到一个CORS抛错。

至于抛错的具体原因和其他细节,不是本文讨论的重点, 感兴趣的可以阅读如下链接了解详情。

https://jakearchibald.com/2017/es-modules-in-browsers/

严格模式

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode

'use strict' 声明我们都不陌生, 在es5 时代我们也经常使用, 一般是在文件顶部加这个声明,目的就是禁用Javascript中不太友好的一部分,有助于我们写更严谨的代码。

这个特性,在es6语法中是默认开启的, 如果代码里面有不太严格的代码,则会报错,例如:

2.png

下面是我从MDN中摘取的一些在严格模式中被禁用的部分:

  • Variables can’t be left undeclared
  • Function parameters must have unique names (or are considered syntax errors)
  • with is forbidden
  • Errors are thrown on assignment to read-only properties
  • Octal numbers like 00840 are syntax errors
  • Attempts to delete undeletable properties throw an error
  • delete prop is a syntax error, instead of assuming delete global[prop]
  • eval doesn’t introduce new variables into its surrounding scope
  • eval and arguments can’t be bound or assigned to
  • arguments doesn’t magically track changes to method parameters
  • arguments.callee throws a TypeError, no longer supported
  • arguments.caller throws a TypeError, no longer supported
  • Context passed as this in method invocations is not “boxed” (forced) into becoming an Object
  • No longer able to use fn.caller and fn.arguments to access the JavaScript stack
  • Reserved words (e.g protected, static, interface, etc) cannot be bound

exports 的几种用法

ES6模块只支持静态导出,你只可以在模块的最外层作用域使用export,不可在条件语句中使用,也不能在函数作用域中使用。

从分类上级讲, exports 主要有三种:

1、Named Exports (Zero or more exports per module)

2、Default Exports (One per module)

3、Hybrid Exports

exports 总览:

// Exporting inpidual features
export let name1, name2, …, nameN; // also var, const
export let name1 = …, name2 = …, …, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}

// Export list
export { name1, name2, …, nameN };

// Renaming exports
export { variable1 as name1, variable2 as name2, …, nameN };

// Exporting destructured assignments with renaming
export const { name1, name2: bar } = o;

// Default exports
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };

// Aggregating modules
export * from …; // does not set the default export
export * as name1 from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from …;
export { default } from …;

下面我就介绍一下常见的 exports用法。

1. Named exports (导出每个函数/变量)

PixVerse
PixVerse

PixVerse是一款强大的AI视频生成工具,可以轻松地将多种输入转化为令人惊叹的视频。

下载

具名导出,这种方式导出多个函数,一般使用场景比如 utils、tools、common 之类的工具类函数集,或者全站统一变量等。

只需要在变量或函数前面加 export 关键字即可。

//------ lib.js ------
export const sqrt = Math.sqrt;

export function square(x) {
    return x * x;
}
export function diag(x, y) {
    return sqrt(square(x) + square(y));
}

//------ main.js 使用方式1 ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5

//------ main.js 使用方式2 ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5

我们也可以直接导出一个列表,例如上面的lib.js可以改写成:

//------ lib.js ------
const sqrt = Math.sqrt;
function square(x) {
    return x * x;
}
function add (x, y) {
    return x + y;
}
export { sqrt, square, add }

2. Default exports (导出一个默认 函数/类)

这种方式比较简单,一般用于一个类文件,或者功能比较单一的函数文件使用。

一个模块中只能有一个export default默认输出。

export default与export的主要区别有两个:

不需要知道导出的具体变量名, 导入(import)时不需要{}.

//------ myFunc.js ------
export default function () {};

//------ main.js ------
import myFunc from 'myFunc';
myFunc();

导出一个类

//------ MyClass.js ------
class MyClass{}

export default MyClass;

//------ Main.js ------
import MyClass from 'MyClass';

注意这里默认导出不需要用{}。

3. Mixed exports (混合导出)

混合导出,也就是 上面第一点和第二点结合在一起的情况。比较常见的比如 Lodash,都是这种组合方式。

//------ lib.js ------
export var myVar = ...;
export let myVar = ...;
export const MY_CONST = ...;

export function myFunc() {
  // ...
}
export function* myGeneratorFunc() {
  // ...
}
export default class MyClass {
  // ...
}

// ------ main.js ------
import MyClass, { myFunc } from 'lib';

再比如lodash例子:

//------ lodash.js ------
export default function (obj) {
  // ...
};
export function each(obj, iterator, context) {
  // ...
}
export { each as forEach };

//------ main.js ------
import _, { forEach } from 'lodash';

4. Re-exporting (别名导出)

一般情况下,export输出的变量就是在原文件中定义的名字,但也可以用 as 关键字来指定别名,这样做一般是为了简化或者语义化export的函数名。

//------ lib.js ------
export function getUserName(){
  // ...
};
export function setName(){
  // ...
};

//输出别名,在import的时候可以同时使用原始函数名和别名
export {
  getName as get, //允许使用不同名字输出两次
  getName as getNameV2,
  setName as set
}

5. Module Redirects (中转模块导出)

有时候为了避免上层模块导入太多的模块,我们可能使用底层模块作为中转,直接导出另一个模块的内容如下:

//------ myFunc.js ------
export default function() {...};
 
//------ lib.js ------
export * from 'myFunc';
export function each() {...};
 
//------ main.js ------
import myFunc, { each } from 'lib';

export 只支持在最外层静态导出、只支持导出变量、函数、类,如下的几种用法都是错误的。

`错误`的export用法:

//直接输出变量的值
export 'Mark';

// 未使用中括号 或 未加default
// 当只有一个导出数,需加default,或者使用中括号
var name = 'Mark';
export name;

//export不要输出块作用域内的变量
function () {
  var name = 'Mark';
  export  { name };
}

import的几种用法

import的用法和export是一一对应的,但是import支持静态导入和动态导入两种方式,动态import支持晚一些,兼容性要差一些。

3.png

下面我就总结下import的基本用法:

1. Import All things

当export有多个函数或变量时,如文中export的第一点,可以使用 * as 关键字来导出所有函数及变量,同时 as 后面跟着的名称做为 该模块的命名空间。

//导出lib的所有函数及变量
import * as lib from 'lib';

//以 lib 做为命名空间进行调用,类似于object的方式
console.log(lib.square(11)); // 121

2. Import a single/multiple export from a module

从模块文件中导入单个或多个函数,与 * as namepage 方式不同,这个是按需导入。如下例子:

//导入square和 diag 两个函数
import { square, diag } from 'lib';

// 只导入square 一个函数
import { square } from 'lib';

// 导入默认模块
import _ from 'lodash';

// 导入默认模块和单个函数,这样做主要是简化单个函数的调用
import _, { each } from 'lodash';

3. Rename multiple exports during import

和 export 一样,也可以用 as 关键字来设置别名,当import的两个类的名字一样时,可以使用 as 来重设导入模块的名字,也可以用as 来简化名称。
比如:

// 用 as 来 简化函数名称
import {
  reallyReallyLongModuleExportName as shortName,
  anotherLongModuleName as short
} from '/modules/my-module.js';

// 避免重名
import { lib as UserLib} from "alib";
import { lib as GlobalLib } from "blib";

4. Import a module for its side effects only

有时候我们只想import一个模块进来,比如样式,或者一个类库。

// 导入样式
import './index.less';

// 导入类库
import 'lodash';

5. Dynamic Imports

静态import在首次加载时候会把全部模块资源都下载下来.

我们实际开发时候,有时候需要动态import(dynamic import)。

例如点击某个选项卡,才去加载某些新的模块:

// 当动态import时,返回的是一个promise
import('lodash')
  .then((lodash) => {
    // Do something with lodash.
  });

// 上面这句实际等同于
const lodash = await import('lodash');

es7的新用法:

async function run() {
    const myModule = await import('./myModule.js');

    const { export1, export2 } = await import('./myModule.js');

    const [module1, module2, module3] =
        await Promise.all([
            import('./module1.js'),
            import('./module2.js'),
            import('./module3.js'),
        ]);
}

run();

总结

以上, 我总结了ES6 Module 的简单背景和 常见的import , export 用法, 但这远远不是它的全部, 篇幅有限,如果想了解更多, 可以看下面的延伸阅读部分(质量都还不错, 可以看看)。

本文来自 js教程 栏目,欢迎学习!

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

106

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

197

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

233

2025.12.24

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

382

2023.10.25

go中interface用法
go中interface用法

本专题整合了go语言中int相关内容,阅读专题下面的文章了解更多详细内容。

78

2025.09.10

数据库Delete用法
数据库Delete用法

数据库Delete用法:1、删除单条记录;2、删除多条记录;3、删除所有记录;4、删除特定条件的记录。更多关于数据库Delete的内容,大家可以访问下面的文章。

287

2023.11.13

drop和delete的区别
drop和delete的区别

drop和delete的区别:1、功能与用途;2、操作对象;3、可逆性;4、空间释放;5、执行速度与效率;6、与其他命令的交互;7、影响的持久性;8、语法和执行;9、触发器与约束;10、事务处理。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

222

2023.12.29

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Node.js 教程
Node.js 教程

共57课时 | 13.4万人学习

【web前端】Node.js快速入门
【web前端】Node.js快速入门

共16课时 | 2.1万人学习

Node.js-前端工程化必学
Node.js-前端工程化必学

共19课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号