第十三章 模块化
模块系统为了兼容性,大量运用ES5作用域语法,即IIFE
script标签的type=module时,是strict模式
模块标准的分类
第三方标准(ES5)
CMD (Common Module Definition, 普通模块定义)
对应CommonJS实现,对标服务器环境
AMD (Asynchronous Module Definition, 异步模块定义)
对标受延迟限制的客户端环境
UMD(Universal Module Definition, 通用模块定义)
原生标准(ES6+)
ESM(ECMAScript Module)
浏览器环境
CMD模块
特点:
服务器端环境
同步加载
支持动态依赖加载(类似python
加载缓存(初次加载后生成缓存
全局单例
依赖全局对象require、export、module
不可直接在浏览器中使用;如需在浏览器中使用,需要提前打包
AMD模块
特点
受延迟限制的客户端环境(并不专门针对浏览器环境
函数包装模块定义
可以用字符串指定依赖
支持require、exports对象
支持动态依赖
UMD模块
目的:统一CJS和AMD系统,实现两个生态共存
流程:
- 启动时检测模块系统
- 对需要的模块系统进行配置
- 将逻辑封装在IIFE中
ESM模块系统
模块标签:
给script添加type=module,浏览器将会把该脚本当为模块执行;
执行顺序同defer相同——可以类比为宏任务,立即下载文件,文档解析完毕后执行;
单设置defer的效果:原始script优先加载和执行->defer按顺序加载->等待文档解析完毕后执行
单设置async的效果:原始script优先加载和执行->async无序加载->不等待文档解析,直接执行
模块层面上,同步加载执行
模块加载为单例——多次加载只视为一次
*设置type为module后,import只能在外部的js文件中使用,不能在内联script中使用
*在script上设置nomodule属性以解决浏览器兼容问题
模块加载
整个流程类似于AMD加载
- 浏览器异步加载整个依赖图
- 确定入口模块->确定依赖->请求资源
- 递归加载子依赖
特点:
- 支持循环依赖(所有模块都支持) / 单例加载 / 子依赖 /
- 默认为严格模式
- 顶级this为undefined,var声明不会到window对象中
- 异步加载 && 异步执行
- 独立作用域
导入与导出
导出:默认导出 && 具名导出 && 别名导出 / 转移导出
导入: 默认导入 && 成员导入 && 别名导入 && 批量导入&&直接导入
将功能分割成不同独立的文件
划分独立作用域
历史演变
AMD require.js =>CMD sea.js=>COMMONJS =>NODE.JS=>UMD
模块练习
模块分导出部分和导入部分
向外部提供数据,称之为导出
moudle.define(a,b,function(){
return {
xxx
}
})
写入新方法,成为导入。
moulde.define(c,d,function(){})
模块只有在初始化的时候才会执行
后续使用的时候是使用初始化之后的结果
初始化过程也是按顺序执行,如果拥有多个存在依赖的模块,那么会按照先后顺序共用一个模块。相当于传址共用一片内存地址。
let demo = (function () {
const mouduleList = {};
function define(name, modules, action) {
modules.map((m, i) => {
modules[i] = mouduleList[m]
})
mouduleList[name] = action.apply(null, modules);
}
return { define };
})()
demo.define("yang", [], function () {
return {
first(arr) {
return arr[0];
},
max(arr, key) {
return arr.sort((a, b) => b[key] - a[key])[0];
}
}
});
demo.define("lesson", ["yang"], function (yang) {
let data = [{ lesson: "english", price: 50 }, { lesson: "math", price: 250 }, { lesson: "arts", price: 75 }];;
let res = yang.max(data, 'price');
console.log(res);
});
属性名,函数依赖,主体功能
模块使用
前提:script标签添加 module属性
<script></script type="module">
导入:
import{obj1} from '文件路径&文件名'
导出:
export{obj1,obj2,obj3...}
tips:导入中路径必须有./ ../等严格遵循规范
import {demo} from "./jsPracticeCode.js"
demo.show()
想要使用的前提是:1、scirpt拥有module标签 2、属性设置导出 3、属性导入
模块延迟解析与严格模式
模块延迟解析:
放哪都行,后解析。
使用模块时,默认为严格模式
作用域
*模块具有其独立作用域
预解析
无论输出/调用多少次,只解析一次。
file1:
<script type="module">
import { obj } from "./ts.js"
console.log(obj.get());
</script>
file2:
class lesson {
data = [];
init() {
this.data = [
{ name: "js" }, { name: "vue.js" }
]
}
get() {
return this.data;
}
}
let obj = new lesson();
obj.init();
export { obj };
导入与导出
具名导出:
func_file:
export let text = "text file";
export function get_a() {
return "a";
}
export class Render {
static render() {
return "user render"
}
}
main_file:
<script type="module">
import { text,get_a,Render} from "./ts.js"
console.log(get_a());
console.log(text);
console.log(Render.render())
<_/script>
批量导入
import * as obj from "address"
导出的是address文件所有导出的接口
别名使用
导入别名:
import {obj} as xxx from "";
导出别名:
export {obj} as xxx from ""
默认导出
export default xxxxxx
export {xxx as default } from ""
接收时可以随便使用变量来接收
import whatever from ""
混合导出:
具名与默认混合
import 默认,{具名} from "xxx"
不带花括号是默认导出,加上花括号为具名导出
全部与默认混合:
import * as api from "xxx"
api.default()为调用默认
独立模块*n=>模块包=>导入模块包
批量导出:
tool_file
import * as a1 from "";
import * as a2 from "";
use_file
import * as api
api.a1.xxx
api.a2.xxx
规范
一般是文件名与模块相关联
公司有要求必须要根据公司的
一般为醒目易懂的英文名称
按需加载
按需加载是动态加载
import()函数可以按需加载(判断条件)
WebPack打包
将写好的es6+代码转换为兼容es5的代码。
需要下载node.js
记得先配置完再重启
webpack打包工具