模块化是一种项目的构架模式, 这种构架模式让JS代码重用性变得非常高,让项目构架的一些复杂问题全部得以解决。 例如,多个script标签不会再出现了,我们只要用一个script标签进行引入就可以了。
为了减少系统耦合度,提高内聚,减少资源依赖
便于维护,功能复用性更强
解决独立作用域、依赖管理、api暴露、按需加载与执行、安全合并等问题
如何模块化
- 封装之后的入口和出口
- 获取模块
模块化的规范 - 引入模块和定义模块的方式
- AMD:第三方规范
- 依赖前置的加载方式
- 规范化文件是:require.js
- 定义
- 引入
- CMD:第三方规范,已经被抛弃了
- 按需加载的方式
- 规范化文件是:sea.js
- CommonJs:后端的模块化规范:node官方的模块化规范
- 依赖前置的加载方式
- 引入:const 模块对象 = require(“模块名”)
- 定义:
- ES6:ECMAScript的官方推出的模块化规范
- 依赖前置的加载方式
- 定义
- 引入
require.js的使用(AMD第三方规范)
-
基本语法
-
require(参数1,参数2)
- 参数1:数组,数组内放置要引入的模块的路径
- 参数2:回调函数,表示所有的模块引入完成后,才会执行的功能
- 回调函数的形参和引入的模块一一对应,表示引入模块的出口
- 引入模块,可以在一个文件内出现多次,因为可能会存在,引入不同批次的模块,实现不同的功能
-
define(参数1,参数2)
- 参数1:数组,数组内放置定义本模块时,要依赖的其他模块的路径,可选
- 参数2:回调函数,表示要定义的模块的功能,会在所有的模块引入完成后,才会执行
- 回调函数的形参和引入的依赖模块一一对应,表示引入模块的出口
- 定义模块一般为一个独立的js文件,一个js文件内,只有一个define(参数1,参数2)函数
-
-
简单使用
- 在小模块中只定义功能,不执行,暴露到外部,在外部执行(jq必须加载完毕)
-
高级使用
- main文件的概念
- 如果使用独立的script标签的src引入main文件,此时main文件中的小模块的路径起始点,以当前html文件开始
- 如果使用引入了require.js的script标签的data-main属性,引入main文件,此时main文件中的小模块的路径起始点,以main文件开始
- 路径的配置
- 提前配置路径的起始点
- 设置基路径
- 给小模块起别名
- main文件的概念
// 可以提前配置路径的起始点
require.config({
// 设置基路径
baseUrl:"./modules",
// 给小模块起别名
paths:{
rc: "randomColor",
jq: "https://lib.baomitu.com/jquery/3.6.0/jquery"
}
})
require(["rc","jq"], function(rc){
console.log(rc);
console.log($);
})
- 模块之间的引入和执行顺序
- 引入:不分顺序
- 执行:异步执行
- 所有的回调函数,是同步执行
- 在主模块的回调函数中执行小模块的功能,在小模块的功能中可以使用其他模块的任意全局变量
require.config({
baseUrl:"./modules",
paths:{
abc: "jq_test",
jq: "https://lib.baomitu.com/jquery/3.6.0/jquery"
}
})
// require中的回调函数里的_是占位符,为了使用回调函数后面的参数,因为参数是一一对应的,依赖前置
require(["jq","abc"], function(_, abc){
console.log("小模块全部加载完毕");
// 在主模块的回调函数中执行小模块的功能,在小模块的功能中可以使用其他模块的任意全局变量
abc();
console.log($);
})
总结
- 功能单独写到js文件中,使用define包装
- 使用require函数引入使用define包装之后的js文件
ES6模块化
ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。
ES6模块的使用:
使用export规定模块的对外接口,使用import用于输入其他模块提供的功能
语法
定义模块export语法:(module.js文件)
export var a = 10;
var b = 20;
export {b};
var c = "admin"
export {c as name};
// 上面的写法等同于下面的写法
export {a,b,c};
export {a as num1, b as num2, c as name};
export default {num1:a, num2:b, name:c}
加载模块import语法:(main.js文件)
import {a} from "./module.js";
import {b} from "./module.js";
import {c} from "./module.js";
import {a,b,c} from "./module.js";
import {num1,num2,num3} from "./module.js”;
// 上面的写法等同于下面的写法
import {num1 as n1, num2 as n2, num3 as n3} from "./module.js";
import obj from "./module.js";
注意点
1.export和import关键字,只能存在于顶层作用域内,不能存在局部或块级作用域
2.在ES6的模块化中,所有语法自动处在严格模式下
3.export是声明关键字,声明一个对外接口
4.export声明的接口必须和模块内部的变量建立一一对应的关系
5.export声明的接口与对应的值是动态绑定,即可以拿到模块内部实时修改的值
5.export和import时,都可以使用as关键字,重命名接口
6.使用default关键字,设置默认接口,一个模块中只允许出现一次default
7.import在使用接口时,必须将接口放在花括号内,除非export暴露接口时使用了default关键字
8.import加载的接口是只读的,不允许被修改,如果接口是对象,可以修改属性
9.import具有提升效果
10.由于import是静态执行,所以不能使用表达式和变量
11.当import后没有接收接口,会执行整个模块文件
12.可以使用通配符*加载整个模块的接口(需要配合as使用),返回一个模块对象
13.ES6的模块化不是对象,而是通过export输出对应的代码,再通过import输入
14.import加载模块的输入接口是静态加载,指定接口的情况下,只加载接口部分
15.ES6的模块化语法比ES6的常规语法的兼容性还要差所以必须要处于服务器环境
16.引入主模块的script标签必须设置type属性,值为module
使用ES6模块化的步骤
- 主HTML文件引入主js文件
<script src="./js/main.js" type="module"></script>
,其中,script标签必须设置type属性,值为module - 主js文件main.js指定引入模块,模块文件中指定输出
- 在服务器环境下运行
实例
主index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
正在测试ES6的模块化
</body>
<script src="./js/main.js" type="module"></script>
</html>
主main.js
console.log("主模块被引入")
// 引入了模块a的功能
import {cuihua as alis} from "../modules/hello.js";
console.log(alis);
// 引入的模块值改变主模块的值也会跟着改变
setTimeout(() => {
console.log(alis);
}, 1000);
import {b, c} from "../modules/hello.js";
console.log(b)
console.log(c)
// import在使用接口时,必须将接口放在花括号内,除非export暴露接口时使用了default关键字
import obj from "../modules/hello.js";
console.log(obj);
// *代表通配符
import * as obj from "../modules/hello.js";
console.log(obj.cuihua)
console.log(obj.b)
console.log(obj.c)
import {d} from "../modules/hello.js";
console.log(d);
// 可以修改对象的属性
d.name = "hahaha"
console.log(d);
import "../modules/world.js";
模块hello.js
var a = 10;
// 暴露了一个a
export {a as cuihua};
setTimeout(() => {
a = 20;
}, 1000);
var b = "hello world"
export {b};
var c = function(){
console.log(123123)
}
export {c};
var d = {
name:"adminb",
age:189,
sex:"男"
}
export {d};
// 使用default关键字,设置默认接口,一个模块中只允许出现一次default
export default {a,b,c};
模块world.js
console.log("这是一个炫酷的功能")