# 模块化

# 1. 关于模块化

所有的编程语言都会面对『模块化』的需求。因为,大家都会遇到 2 个问题:

  • 命名冲突

  • 文件依赖

通过『模块化』可以解决上述两个问题:

  • 模块化就是把单独的一个功能封装到一个模块(文件)中,模块之间相互隔离,但是可以通过特定的接口公开内部成 员,也可以依赖别的模块。

  • 模块化开发的好处:方便代码的重用,从而提升开发效率,并且方便后期的维护。

虽然,我们更常见的是在前端『工程化的项目』中大量使用到模块化,但是,现在的浏览器也支持在普通的、静态的 html 文件中使用模块化。

你只需要做 2 件事情:

  1. 将 js 代码写在独立的 .js 文件中,并 export 你想要导出的内容;

  2. 在 .html 文件中使用 <script type="module"></script> 引入它,并正确使用 import 即可。

警告

在静态 html 页面中使用多模块,.html 文件不能以双击的方式在浏览器中直接打开,要把它运行访问。最简单的方式,就是在 vscode 中安装 liverserver 插件运行。

# 2. JavaScript 的模块化

JavaScript 引入模块化的概念比较晚。在 ES6 提出模块化规范之前,社区中已经尝试并提出了 AMD、CMD、CommonJS 等模块化规范。不过这些非官方的模块化标准存在一定的局限性。

因此,ES6 语法规范中,在语言层面上定义了 ES6 模块化规范,是浏览器端与服务器端通用的模块化开发规范。

ES6 模块化规范中定义:

  • 每个 js 文件都是一个独立的模块

  • 导入模块成员使用 import 关键字

  • 暴露模块成员使用 export 关键字

『**暴露模块成员**』这个说法有些奇怪,但是其它语言也实现了同样的功能,只不过不是叫这个名字。例如,C++ 和 Java 是以『**访问权限**』实现的类似功能,而 C 语言是以『**本地(静态)变量**』实现的类似功能。

# 3. export 命令

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

输出(export)变量有 2 种写法:

  1. 单个输出

    export var firstName = 'Michael';
    export var lastName = 'Jackson';
    export var year = 1958;
    

    这种写法中,对于(你所想要输出的)每个变量要单独书写 export(输出几个变量,就要写几个 export

  2. 统一输出

    var firstName = 'Michael';
    var lastName = 'Jackson';
    var year = 1958;
    
    export {firstName, lastName, year};
    

    这种写法中,你只要书写一个 export ,将你所想要输出的变量都写在这里,写在一起。

export 除了输出变量,还可以输出函数。也分为单个输出和同意输出 2 种写法。

  1. 单个输出

    export function one(a, b) { ... }
    export function two() { ... }
    
  2. 统一输出

    function one(a, b) { ... }
    function two() { ... }
    
    export { one, two };
    

# 4. import 命令

使用 export 定义模块的对外接口以后,其它 JS 文件就可以通过 import 来加载这个模块。

import 命令用于加载其它的文件,并从中输入变量。

需要强调的一点是,并非人家 export,你就能直接使用,你需要 import 别人 export 出来的变量和方法,而后你才能用。

import 命令接受一对大括号,里面指定从其它模块导入的变量名(或函数名)大括号里面的变量名必须与被导入模块 export 的“东西”的名称相同

import { firstName, lastName, year } from './xxx.js';

import 后面的 from 指定模块文件的位置,可以使用相对路径,也可以使用绝对路径。.js 后缀可以省略。

import 命令输入的变量都是『只读』的,即,不允许再加载它的脚本中改写它。如果导入的是一个对象,虽然改写对象的属性是允许的,但是仍不推荐这样使用。

建议凡是 import 的变量,都当作完全只读。若无必要,轻易不要改变它(和它的属性)

# 5. export default 命令

在使用 import 命令时,import 方需要明确知道 export 方对外导出的变量名或函数名,否则无法加载。这就需要 import 方去阅读 export 方的源码(或文档)

很显然,这样是很不方便的。更有甚者,export 方对外导出的是匿名对象,或匿名类,这里压根就没名字!例如:

export { ... }
export function() { ... }

这种情况下,你又怎么可能获得它所导出的变量(匿名对象)和函数?

为了给用户提供方便,不用阅读源码(或文档)就能加载模板,可以使用 export default 命令,为模块指定默认输出。

export default { ... }export default function () { ... }

这样,import 方就可以『以任意的名字』去接受这个匿名对象(或匿名函数)

import obj from 'xxx.js'import sayHello from 'xxx.js'

逻辑上等同于:

const obj = { ... }
const sayHello = function () { ... }

需要注意的是,使用 export default 时,对应的 import 命令后面不使用大括号;使用 export 时,对应的 import 需要使用大括号。

毫无疑问,一个模块只能有一个默认输出 export default