码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 【ES6知识】ESModule 模块化


    文章目录

      • 一、前言:模块化背景
        • 1.1 后端模块化
      • 1.2 关于前端模块化
      • 二、模块的导出
      • 三、模块的导入
      • 四、应用模块到HTML中
      • 五、export default 默认导出命令
      • 六、重命名导出与导入
      • 七、创建模块对象
      • 八、动态加载模块

    一、前言:模块化背景

    JavaScript 程序本来很小——在早期,它们大多被用来执行独立的脚本任务,在你的 web 页面需要的地方提供一定交互,所以一般不需要多大的脚本。过了几年,我们现在有了运行大量 JavaScript 脚本的复杂程序,还有一些被用在其他环境(例如 Node.js)的需求。因此,近年来,有必要开始考虑提供一种将 JavaScript 程序拆分为可按需导入的单独模块的机制。

    原生JavaScipt案例合集
    JavaScript +DOM基础
    JavaScript 基础到高级
    Canvas游戏开发

    1.1 后端模块化

    Node.js 已经提供这个能力很长时间了,其是对 CommonJS 规范的实现。CommonJS 规范是为了解决 JavaScript 的作用域问题而定义的模块形式,可以使每个模块它自身的命名空间中执行。该规范的主要内容是,模块必须通过 module.exports 导出对外的变量或接口,通过 require() 来导入其他模块的输出到当前模块作用域中。

    // moduleA.js
    module.exports = function( value ){
        return value * 2;
    }
    
    • 1
    • 2
    • 3
    • 4
    // moduleB.js
    var multiplyBy2 = require('./moduleA');
    var result = multiplyBy2(4);
    
    • 1
    • 2
    • 3

    1.2 关于前端模块化

    在 ES6 前, 实现模块化使用的是 RequireJS 或者 seaJS(分别是基于 AMD 规范的模块化库, 和基于 CMD 规范的模块化库)。

    ES6 引入了模块化,其设计思想是在编译时就能确定模块的依赖关系,以及输入和输出的变量。ESModule被认为是大一统的模块化设计规范,有如下特点:

    • ES6 模块化自动开启严格模式,不管你是否在模块的头部加上 use strict;
    • 模块中可以导入和导出各种类型的变量,如函数、对象、字符串、数字、布尔值、类等
    • 每个模块都有自己的上下文,每一个模块内声明的变量都是局部变量,不会污染全局作用域
    • 每一个模块只加载一次(是单例的),若再去加载同目录下同文件,直接从内存中读取

    最新的浏览器开始支持原生的模块功能了,浏览器能够最优化加载模块,使它比使用库更有效率:使用库通常需要做额外的客户端处理。

    为了使模块可以在浏览器中正常地工作,你需要确保你的服务器能够正常地处理 Content-Type 头,其应该包含 JavaScript 的 MIME 类型 text/javascript。如果没有这么做,你可能会得到 一个严格 MIME 类型检查错误:“The server responded with a non-JavaScript MIME type(服务器返回了非 JavaScript MIME 类型)”,并且浏览器会拒绝执行相应的 JavaScript 代码。

    二、模块的导出

    使用 export 语句分别导出需要的内容,如下:

    export const name = 'square';
    
    export function draw(ctx, length, x, y, color) {
      ctx.fillStyle = color;
      ctx.fillRect(x, y, length, length);
    
      return {
        length: length,
        x: x,
        y: y,
        color: color
      };
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    当然你也可以在模块文件的末尾使用 export 加 {} 一次性的导出所有需要导出的信息(逗号分隔),如下:

    export { name, draw, reportArea, reportPerimeter };
    
    • 1

    三、模块的导入

    如果想要在模块外面使用其它模块中的功能,必须先导入它们才能使用,如下:

    import { name, draw, reportArea, reportPerimeter } from './modules/square.js';
    
    • 1

    导入后就能像定义在相同文件中的功能一样去使用它了,如下:

    let myCanvas = create('myCanvas', document.body, 480, 320);
    let reportList = createReportList(myCanvas.id);
    
    let square1 = draw(myCanvas.ctx, 50, 50, 100, 'blue');
    reportArea(square1.length, reportList);
    reportPerimeter(square1.length, reportList);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    四、应用模块到HTML中

    与常规脚本引入方式相似,但又一些显著的差异。

    首先,你需要把 type="module" 放到

    • 1
    • 你只能在模块内部使用 import 和export 语句,且导入的功能只在当前模块内生效(无法全局中获取)。
    • 本地测试 – 如果你通过本地加载 HTML 文件 (比如一个 file:// 路径的文件), 你将会遇到 CORS 错误,因为 JavaScript 模块安全性需要。你需要通过一个服务器来测试。
    • 加载一个模块脚本时不需要使用 defer 属性 (see