模块化概念
模块化开发可以提高代码的可维护性、可读性和复用性,同时降低开发和调试的复杂性,把业务根据功能分开写,解决变量命名的冲突,可以开放部分接口给类(例如调用模块里的一个函数)也更适合团队协作
模块化的开发方式可以提供代码复用率,方便进行代码的管理。通常来说,一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数。目前流行的js模块化规范有CommonJS、AMD、CMD以及ES6的模块系统。
开发一个模块管理工具
在这里我们自己写一个,可以更好的理解模块管理
实现模块的添加、定义、依赖,使用容器管理模块
其他想说的都在代码里了:
let module = (function () {
const moduleList = {}//存储模块
function define(name, modules, action) {
modules.map((m, i) => {
modules[i] = moduleList[m]
})//map方法的参数,m是模块,i是编号,从容器里拿出来
moduleList[name] = action.apply(null, modules)//在模块容器里放一个模块,初始化的时候会执行
//console.log(moduleList)
}
return { define }
})()//一个立即执行函数
//module.define(name, modules, function (){})是在定义模块,name是模块的名字,modules表示依赖哪个模块,function表示模块的具体动作
module.define('count', [], function () {//[]表示不依赖别的模块
return {
first(arr) {//返回第一个元素的功能
return arr[0]
},
max(arr, key) {//返回最大元素的功能
return arr.sort((a, b) => b[key] - a[key])[0]
}
}//return,导出
})//count模块,对外输出了两个函数
module.define('lesson', ['count'], function (result) {//['count']是在导入,result是count的结果
let data = [
{ name: 'js', price: 199 }, { name: 'mysql', price: 78 }
]
console.log(result.max(data, 'price'))//{name: 'js', price: 199}
})
//我是一条分割线,下面的是有模块依赖的使用
module.define('User', [], function () {
return {
site: '我是初始值',
url: '我是初始url'
}
})
module.define('change', ['User'], function (User) {
User.site = '初始值在change模块被改变了'
})//模块的改变是全局的
module.define('show', ['User'], function (User) {
console.log('我是show模块', User)
})
模块的基本使用
js的模块部分写在外面的部分,在访问的时候只能访问对外开发的部分,并且表明自己引用的是哪部分:
<script type="module">
import { title, url, show } from "./hd.js"//访问./hd.js里的tittle、url、show
show();
</script>
这是模块,可以在模块内决定谁开放谁不开放
class Lesson {
data = [];
init() {
this.data = [{ name: "js" }, { name: "vue.js" }];
}
get() {
return this.data;
}
}
let obj = new Lesson();
obj.init();
export { obj };//开放obj
模块延迟解析与严格模式
模块的延迟解析(Deferred Parsing)是 JavaScript 模块(type="module"
)在浏览器中的一种行为特性。它指的是浏览器在加载模块时,不会立即解析和执行模块代码,而是延迟到文档解析完成后才进行。
模块的延迟解析特性:
延迟执行:当浏览器遇到
<script type="module">
时,它会异步加载模块文件,但不会立即执行模块代码,模块代码的执行会被延迟到整个 HTML 文档解析完成之后按顺序执行:模块脚本会按照它们在文档中出现的顺序依次执行,即使模块是异步加载的,类似于<script>里的defer
不会阻塞dom解析:模块脚本不会阻塞 HTML 文档的解析和渲染。
适用于依赖dom的脚本:由于模块代码的执行被延迟到文档解析完成后,模块中可以安全地访问和操作 DOM 元素,而不需要等待
DOMContentLoaded
事件。
<script type="module">//模块脚本写法,浏览器会将其作为 ES6 模块处理。
import {} from "./hd.js"//从./hd.js模块导入内容
console.log(this)//模板脚本为严格模式,this为undefined
</script>
<script>//普通脚本写法
console.log(this)//普通脚本this为window
</script>
<body>
<button>后盾人</button>
</body>
它确保了模块代码的执行不会阻塞文档的解析和渲染,同时保证了模块可以安全地操作 DOM。这种特性使得模块脚本更适合在现代 Web 开发中使用,尤其是在需要操作 DOM 的场景中。
作用域在模块的体现
每个模块就是一个独立的作用域,不同的模块访问不到彼此的变量
<script type="module">
let site = '荷叶饭'
</script>
<script type="module">
console.log(site)
</script>
两个外部的模块作用域也是不同的,互相直接调用也会报错
但是export出来就可以被访问
模块预解析
模块无论加载多少次,只会在第一次的时候执行
只解析一次是有好处的,可以起到初始化的作用
class lesson {
data = []
init() {
this.data = [{ name: 'js' }, { name: 'mysql' }]
}
get() {
return this.data
}
}
let obj = new lesson()
obj.init()
export { obj }
模块的具名导出与导入
具名和匿名比,就是有具体的名字
export let site = '后盾人'
export function show() {
return 'show function'
}
export class User {
static render() {
return 'user static render'
}
}
这是具名的引入和导出
批量导入
一个一个导入:
import { site, show, User } from "./modules/m7.js";
console.log(site);
console.log(show());
console.log(User.render());
批量导入的格式
<script type="module">
import *as api from './0204model.js'
console.log(api)
</script>
api里的内容
打印里面的内容:
import *as api from './0204model.js'
console.log(api.site)
console.log(api.show())
console.log(api.User.render())
导入和导出的别名使用
导入的变量可以起个别名方便使用:
import { site as othername } from './0204model.js'
console.log(othername)//后盾人
导出的时候也可以起别名(逆天语法):
let site = '后盾人'
export { site as hd }
导入也可以给导出起过别名的的变量起别名:
//模块导出
let site = '后盾人'
export { site as hd }
//导入
import { hd as othername } from './0204model.js'
console.log(othername)//后盾人
default默认导出
一般导出加{}是一次导出多个值,也可以使用默认导出
export default class User {
static render() {
return 'user static render'
}
}
import User from './0204model.js'//默认导出的内容赋值给User
console.log(User)//所以User是一个类
也可以单独写一个默认导出:
//export default class User {
// static render() {
// return 'user static render'
// }
//}
class User {
static render() {
return 'user static render'
}
}
export {User as default}
默认导出就导出来一个,所以用什么名字导入是随意的:
import User from './0204model.js'//默认导出的内容赋值给User
console.log(User)//所以User是一个类
import sjb from './0204model.js'//换个名字也可以
console.log(sjb)//User是一个类
这样和具名导出是不一样的,一次只导出来一个哦
混合导入导出
上面讲了默认和具名,混着用就是混合导入导出
import * as api from "./modules/m11.js";
console.log(api.default.render());
console.log(api.site);
后盾人老师更建议具名导出,因为默认导出会随意起名字,具名导出可以给名字指定规范
一般让模块名和具名的名字相关,方便阅读开发
模块的合并导出
一个也业务同时用到很多模块的时候怎么处理?
可以设计一个‘中间站’,这个中间站导入所有要用的模块再导出
import { site,url } from "./0202model.js";
import User,{ web } from "./0204model2.js";
export{site,url User,web}
如果不同模块有重复命名的问题,可以把同一个模块里的数据包在一起,另一个模块的数据包在一起
下面的代码是把两个模块的数据发到中间站合并位两个组,中间站再发回html部分,合并为一个组
//group1
export let web='http://'
export default class User{
static show(){
return 'use static method show'
}
}
//group2
let site='111'
let url='uuu'
export {site,url}
//中间站
import *as group1 from "./0204model.js";
import *as group2 from "./0204model2.js";
export { group1, group2 }
//html
<script type="module">
import *as api from './temp.js'
console.log(api)
</script>
按需加载动态模块
有时候可能需要这堆模块,比如提交表单的模块;有时候需要那堆模块,收集信息的模块
这时候就需要按需加载
使用import函数,import()
是 JavaScript 中的一种动态导入语法,它允许你在运行时异步加载模块。import()
返回一个 Promise
,成功时解析为模块对象,失败时返回错误。
<body>
<button>houdunren</button>
</body>
<script type="module">
//import
document.querySelector("button").addEventListener("click", () => {
import("./0204model2.js").then(({ site, url }) => {
console.log(site, url);
});
});
</script>
点击按钮时,按需加载模块
模块管理工具把es6语法->es5语法
WEBPACK构建项目的软件安装:安装node.js->安装webpack
安装明天再说