【ES6知识】ESModule 模块化

文章目录

一、前言:模块化背景

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;
}

// moduleB.js
var multiplyBy2 = require('./moduleA');
var result = multiplyBy2(4);

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
  };
}

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

export { name, draw, reportArea, reportPerimeter };

三、模块的导入

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

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

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

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);

四、应用模块到HTML中

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

首先,你需要把 type="module" 放到 <script> 标签中,来声明这个脚本是一个模块:

<script type="module" src="main.js"></script>
  • 你只能在模块内部使用 importexport 语句,且导入的功能只在当前模块内生效(无法全局中获取)。
  • 本地测试 -- 如果你通过本地加载 HTML 文件 (比如一个 file:// 路径的文件), 你将会遇到 CORS 错误,因为 JavaScript 模块安全性需要。你需要通过一个服务器来测试。
  • 加载一个模块脚本时不需要使用 defer 属性 (see <script> attributes) 模块会自动延迟加载

五、export default 默认导出命令

到目前为止我们导出的功能都是由 named exports 组成 ---- 每个项目(无论是函数,常量等)在导出时都由其名称引用,并且该名称也用于在导入时引用它。

还有一种导出类型叫做 default export ---- 这样可以很容易地使模块提供默认功能,并且还可以帮助 JavaScript 模块与现有的 CommonJS 和 AMD 模块系统进行互操作。特点如下:

  • 在一个文件或模块中,export、import 可以有多个,export default 仅有一个

  • export default 中的 default 是对应的导出接口变量,后面不能再跟变量的声明语句

  • export default 方式导出的数据,在导入时不需要加 {}

  • export default 方式向外暴露的成员,可以使用任意变量来接收

    var a = "My name is Tom!";
    export default a; // 仅有一个
    export default var c = "error";
    // error,default 已经是对应的导出变量,不能跟着变量声明语句

    import b from "./xxx.js"; // 不需要加{}, 使用任意变量接收

六、重命名导出与导入

在你的 importexport 语句的大括号中,可以使用 as 关键字跟一个新的名字,来改变你在顶级模块中将要使用的功能的标识名字。因此,例如,以下两者都会做同样的工作,尽管方式略有不同:

// inside module.js
export {
  function1 as newFunctionName,
  function2 as anotherNewFunctionName
};

// inside main.mjs
import { newFunctionName, anotherNewFunctionName } from '/modules/module.mjs';

// inside module.js
export { function1, function2 };

// inside main.mjs
import { function1 as newFunctionName,
         function2 as anotherNewFunctionName } from '/modules/module.mjs';

七、创建模块对象

上面的方法工作的挺好,但是有一点点混乱、亢长。一个更好的解决方是,导入每一个模块功能到一个模块功能对象上。可以使用以下语法形式:

import * as Module from '/modules/module.js';

这将获取 module.js 中所有可用的导出,并使它们可以作为对象模块的成员使用,从而有效地为其提供自己的命名空间。例如:

Module.function1()
Module.function2()

八、动态加载模块

浏览器中可用的 JavaScript 模块功能的最新部分是动态模块加载。这允许您仅在需要时动态加载模块,而不必预先加载所有模块。这有一些明显的性能优势。

这个新功能允许您将 import() 作为函数调用,将其作为参数传递给模块的路径。它返回一个 promise,它用一个模块对象来实现(参见创建模块对象),让你可以访问该对象的导出,例如

import('/modules/myModule.js')
  .then((module) => {
    // Do something with the module.
  });
相关推荐
我要洋人死35 分钟前
导航栏及下拉菜单的实现
前端·css·css3
科技探秘人1 小时前
Chrome与火狐哪个浏览器的隐私追踪功能更好
前端·chrome
科技探秘人1 小时前
Chrome与傲游浏览器性能与功能的深度对比
前端·chrome
JerryXZR1 小时前
前端开发中ES6的技术细节二
前端·javascript·es6
七星静香1 小时前
laravel chunkById 分块查询 使用时的问题
java·前端·laravel
q2498596931 小时前
前端预览word、excel、ppt
前端·word·excel
小华同学ai1 小时前
wflow-web:开源啦 ,高仿钉钉、飞书、企业微信的审批流程设计器,轻松打造属于你的工作流设计器
前端·钉钉·飞书
Gavin_9151 小时前
【JavaScript】模块化开发
前端·javascript·vue.js
懒大王爱吃狼2 小时前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
逐·風6 小时前
unity关于自定义渲染、内存管理、性能调优、复杂物理模拟、并行计算以及插件开发
前端·unity·c#