购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.14 模块化Module

ECMA组织参考了众多社区模块化标准,在2015年发布了官方的模块化标准。这也是JS首次在语言标准的层面上实现了模块功能,逐步取代了之前的CommonJS和AMD规范,成为浏览器通用的解决方案。

2.14.1 ECMAScript 6的模块化特点

ECMAScript 6模块的设计思想是尽量静态化,使编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块都只能在运行时确定这些依赖关系及变量。例如CommonJS模块就是对象,输入时必须查找对象属性。

ECMAScript 6模块化的特点如下:

(1)一个ECMAScript 6的模块就是一个JS文件。

(2)模块只会加载和执行一次,如果下次再加载同一个文件,则直接从内存中读取。

(3)一个模块就是一个单例对象。

(4)模块内声明的变量都是局部变量,不会污染全局作用域。

(5)模块内部的变量或者函数可以通过export导出。

(6)模块与模块直接可以相互依赖和相互引用。

2.14.2 模块化开发的优缺点

模块化实现了把一个复杂的系统分成各个独立的功能单元,每个单元可以独立设计和自由组合,模块化的优点如下:

(1)减少命名冲突。

(2)避免引入时的层层依赖。

(3)可以提升执行效率。

(4)架构清晰,可灵活开发。

(5)降低耦合,可维护性高。

(6)方便模块功能调试、升级及模块间的组合拆分。

模块化也存在一些缺点:

(1)损耗性能。

(2)系统分层,调用链长。

(3)目前浏览器无法使用import导入模块,需要第三方打包工具的支持。

2.14.3 模块的定义

一个模块就是一个独立的文件,该文件内部的所有变量,外部无法获取,如果希望外部能够读取模块内部的某个变量,就必须使用关键字export输出该变量。

(1)export:用于规定模块的对外接口。

(2)import:用于输入其他模块提供的功能。

2.14.4 模块的导出

当定义好一个模块后,默认情况下,模块内部的内容在模块外不能直接访问,因此需要在模块中通过export关键字指定哪些内容是对外的,没有添加export关键字的内容是私有的,ES6中提供了多种模块内容的导出方式。

1.导出每个函数/变量

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

导出时只需要在变量或函数前面加export关键字,如代码示例2-168所示。

代码示例2-168 chapter02\es6_demo\14-module\main.js

调用模块,执行后的输出结果如图2-2所示。

图2-2 调用libs.js模块的执行结果

也可以直接导出一个列表,例如上面的libs.js可以改写,如代码示例2-169所示。

代码示例2-169 chapter02\es6_demo\14-module\libs.js

2.导出一个默认函数/类

导出一个默认函数或者类,这种方式比较简单,一般用于一个类文件,或者功能比较单一的函数文件。一个模块中只能有一个export default默认输出。

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

(1)不需要知道导出的具体变量名。

(2)导入(import)时不需要{}。

如代码示例2-170所示。

代码示例2-170 myFunc.js

导出一个类,如代码示例2-171所示。

代码示例2-171 MyClass.js

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

3.混合导出

混合导出,也就是前面介绍的第1种和第2种方式结合在一起的情况。例如Lodash之类的库采用这种组合方式,如代码示例2-172所示。

代码示例2-172 common.js

再例如lodash例子,如代码示例2-173所示。

代码示例2-173 lodash.js

4.别名导出

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

代码示例2-174 num.js

5.中转模块导出

有时候为了避免上层模块导入太多的模块,可能使用底层模块作为中转,直接导出另一个模块的内容,如代码示例2-175所示。

代码示例2-175 myFunc.js

6.几种错误的export用法

export只支持在最外层静态导出,并且只支持导出变量、函数、类,如下的几种用法都是错误的,如代码示例2-176所示。

代码示例2-176

2.14.5 模块的导入

import的用法和export的用法是一一对应的,但是import支持静态导入和动态导入两种方式,动态导入在兼容性上要差一些,目前仅Chrome浏览器和Safari浏览器支持。

1.导入整个模块

当export有多个函数或变量时,如导出多个内容,可以使用∗as关键字来导出所有函数及变量,同时as后面跟着的名称作为该模块的命名空间,如代码示例2-177所示。

代码示例2-177

2.按需导入单个或多个函数

从模块文件中导入单个或多个函数,与∗as namepage方式不同,这个是按需导入。如代码示例2-178所示。

代码示例2-178

3.使用as重设导入模块的名字

和export一样,也可以用as关键字设置别名,当导入的两个类的名字一样时,可以使用as来重设导入模块的名字,也可以用as来简化名称。如代码示例2-179所示。

代码示例2-179

4.为某些副作用导入库

有时候只想导入进来,不需要调用,这种情况很常见,例如在用Webpack构建时,经常导入.css文件,或者导入一个类库,如代码示例2-180所示。

代码示例2-180

5.动态import

静态import在首次加载时会把全部模块资源下载下来,但是,在实际开发时,有时候需要动态导入(dynamic import),例如当单击某个选项卡时才去加载某些新的模块,这个动态导入的特性浏览器也是支持的,如代码示例2-181所示。

代码示例2-181

ES 7的新用法,如代码示例2-182所示。

代码示例2-182

6.浏览器加载ES 6模块

当浏览器加载ES 6模块时也使用<script>标签,但是需要加入type="module"属性,代码如下:

上面的代码用于在网页中插入一个模块utils.js,由于type属性被设为module,所以浏览器才可以识别出是ES 6模块。浏览器对于带有type=module的脚本采用异步加载,不会堵塞浏览器,即等到整个页面渲染完,再执行模块脚本,等同于打开了script标签的defer属性,代码如下:

如果网页有多个<script type="module">,则它们会按照在页面出现的顺序依次执行。

<script>标签的async属性也可以打开,这时只要加载完成,渲染引擎就会中断渲染而立即执行。执行完成后,再恢复渲染,代码如下:

一旦使用了async属性,<script type="module">就不会按照在页面出现的顺序执行,而是只要该模块加载完成就执行该模块。

ES 6模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致,代码如下: KNUwQOEOFqhmmak1ifhZMBt4I4Lhs80EacUIZsX4Xpizx6EPSsJ4HBkNaPCg2SwH

点击中间区域
呼出菜单
上一章
目录
下一章
×