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

先看个简单的例子:



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

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




// 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 (导出每个函数/变量)

包阅AI
包阅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教程 栏目,欢迎学习!

相关专题

更多
js获取数组长度的方法
js获取数组长度的方法

在js中,可以利用array对象的length属性来获取数组长度,该属性可设置或返回数组中元素的数目,只需要使用“array.length”语句即可返回表示数组对象的元素个数的数值,也就是长度值。php中文网还提供JavaScript数组的相关下载、相关课程等内容,供大家免费下载使用。

544

2023.06.20

js刷新当前页面
js刷新当前页面

js刷新当前页面的方法:1、reload方法,该方法强迫浏览器刷新当前页面,语法为“location.reload([bForceGet]) ”;2、replace方法,该方法通过指定URL替换当前缓存在历史里(客户端)的项目,因此当使用replace方法之后,不能通过“前进”和“后退”来访问已经被替换的URL,语法为“location.replace(URL) ”。php中文网为大家带来了js刷新当前页面的相关知识、以及相关文章等内容

372

2023.07.04

js四舍五入
js四舍五入

js四舍五入的方法:1、tofixed方法,可把 Number 四舍五入为指定小数位数的数字;2、round() 方法,可把一个数字舍入为最接近的整数。php中文网为大家带来了js四舍五入的相关知识、以及相关文章等内容

727

2023.07.04

js删除节点的方法
js删除节点的方法

js删除节点的方法有:1、removeChild()方法,用于从父节点中移除指定的子节点,它需要两个参数,第一个参数是要删除的子节点,第二个参数是父节点;2、parentNode.removeChild()方法,可以直接通过父节点调用来删除子节点;3、remove()方法,可以直接删除节点,而无需指定父节点;4、innerHTML属性,用于删除节点的内容。

470

2023.09.01

JavaScript转义字符
JavaScript转义字符

JavaScript中的转义字符是反斜杠和引号,可以在字符串中表示特殊字符或改变字符的含义。本专题为大家提供转义字符相关的文章、下载、课程内容,供大家免费下载体验。

393

2023.09.04

js生成随机数的方法
js生成随机数的方法

js生成随机数的方法有:1、使用random函数生成0-1之间的随机数;2、使用random函数和特定范围来生成随机整数;3、使用random函数和round函数生成0-99之间的随机整数;4、使用random函数和其他函数生成更复杂的随机数;5、使用random函数和其他函数生成范围内的随机小数;6、使用random函数和其他函数生成范围内的随机整数或小数。

990

2023.09.04

如何启用JavaScript
如何启用JavaScript

JavaScript启用方法有内联脚本、内部脚本、外部脚本和异步加载。详细介绍:1、内联脚本是将JavaScript代码直接嵌入到HTML标签中;2、内部脚本是将JavaScript代码放置在HTML文件的`<script>`标签中;3、外部脚本是将JavaScript代码放置在一个独立的文件;4、外部脚本是将JavaScript代码放置在一个独立的文件。

655

2023.09.12

Js中Symbol类详解
Js中Symbol类详解

javascript中的Symbol数据类型是一种基本数据类型,用于表示独一无二的值。Symbol的特点:1、独一无二,每个Symbol值都是唯一的,不会与其他任何值相等;2、不可变性,Symbol值一旦创建,就不能修改或者重新赋值;3、隐藏性,Symbol值不会被隐式转换为其他类型;4、无法枚举,Symbol值作为对象的属性名时,默认是不可枚举的。

544

2023.09.20

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

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

精品课程

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

共57课时 | 7.8万人学习

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

共16课时 | 1.9万人学习

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

共19课时 | 3万人学习

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

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