JavaScript模块化
本文最后更新于 2025-01-07,文章内容可能已经过时。
模块化概念
模块化
是一种规范
,将一个文件模块拆分
成多个文件模块
,即可称为模块化
!
模块化
,通过模块拆分
可减少单独文件的代码量
,分成多个模块
后,代码维护性更好
,且每个模块代码之间都是相互隔离
的,避免了变量名全局污染的可能性
,另外也可以将重复的模块代码单独抽离成一个子模块
,并供其它模块去使用
,提高代码的复用性
!
没有模块化的一些问题
变量全局污染
变量
全局污染
,就是变量命名冲突
的问题!当在一个
html文件
中,同时引入两个js文件
时,分别是a.js
和b.js
,这时两个js文件
内部有相同的变量名
时,则后面的变量会覆盖前面的变量
!
a.js
function getData(){
return "我是a.js文件";
}
b.js
function getData(){
return "我是b.js文件";
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css/app.css">
</head>
<body>
<script src="./a.js"></script>
<script src="./b.js"></script>
</body>
</html>
这里看到,
b.js文件
在a.js文件之后
引入,且内部变量
会覆盖a.js文件中的变量
!最终会打印:
我是b.js文件
!
文件依赖引入混乱
当在一个
真实的开发环境
时,且一个html文件
会引入大量的外部script链接
,且每个链接之间
都产生互相依赖
,且其中一个链接位置
一旦发生变化
,便会导致整个程序崩溃,无法正常运行
!
数据安全问题
一个
html
中引入的js外部链接
,其每个js文件
中的数据变量都是共享
的,例如在a.js文件中定义数据变量
,在b.js文件中可以直接获取
,这样的数据是不安全
的!
a.js
let user = {
name: "张三",
age: 18,
address: "北京市"
}
function getData(){
return "我是a.js文件";
}
b.js
b.js 在 a.js 后边引入,并可以直接操作 a.js 中定义的变量数据!
function getData(){
user.name = "李四"
console.log("a.js user", user.name)
return "我是b.js文件";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="./css/app.css">
</head>
<body>
<script src="./a.js"></script>
<script src="./b.js"></script>
</body>
</html>
模块化规范
模块化规范
,主要就是对文件模块代码
进行分别导出
,并在其它文件内部
进行模块导入
!
模块化规范
主要有四种
,分别为(CommonJs ESModule AMD CMD)!
CommonJs 是 Nodejs中提出的模块化规范,在Nodejs中用的比较多!
ESModule 是 ECMAScript提出的模块化规范,在浏览器中用的比较多!
CommonJs
CommonJs
是Nodejs
中提出的模块化规范
,通过module.exports
可以对模块内容进行导出
,通过require方法
对模块进行导入
!
CommonJs 模块化是非官方标准
,Nodejs
是2009年
提出的,主要用来开发服务器
,当时js还没有模块化概念
,所以社区
就提出了CommonJs模块化
,最后被NodeJs
采纳使用!
初体验
student.js
let name = "张三";
let slogan = "让自己走起路来都带风!";
function getTel() {
return "137299182731";
}
function getHobby() {
return ["学习", "旅游", "羽毛球"];
}
exports.name = name;
exports.slogan = slogan;
exports.getTel = getTel;
school.js
let name = "尚硅谷";
let slogan = "让天下没有难学的技术";
function getTel(){
return "010-12345678";
}
function getCities(){
return "北京市海淀区西二旗";
}
exports.name = name;
exports.slogan = slogan;
exports.getTel = getTel;
index.js
使用
require方法
导入模块
const student = require("./student.js");
const school = require("./school.js");
console.log(student.name); // 张三
console.log(student.slogan); // 让自己走起路来都带风!
console.log(student.getTel()); // 13800138000
console.log(school.name); // 尚硅谷
这里使用了
exports
对模块内部变量
做了一个导出
,exports
是一个对象
,相当于给对象内部添加属性
!
导出数据
导出数据
有两种方式
:exports
和module.exports
- exports.attr = value
- module.exports = value
注意事项:
每个
模块
中都有一个空对象{}
,在模块
中可以通过this exports 和 module.exports
来访问这个空对象
,并且三个都是指向一个对象的引用地址
!let name = "尚硅谷"; let slogan = "让天下没有难学的技术"; function getTel(){ return "010-12345678"; } function getCities(){ return "北京市海淀区西二旗"; } console.log(this) console.log(exports) console.log(module.exports) // 指向同一个对象地址 console.log(this === module.exports && exports === module.exports) // true
无论
修改某个对象中的值
,最后向外导出的模块
最终都是module.exports
为准!使用
exports
时不能直接对其进行赋值
!// exports = "value"; // 不允许 exports.value = "value"; // 必须是这样 module.exports = "value"; // 也可以这样
exports = "value"
无论怎么修改,导出的对象
都是module.exports
导入数据
导入数据
,通过require
来引入模块
,引入的模块代码内部会被包裹成一个自执行函数
,并获取module.exports
的值,返回出去
!
const { name, slogan } = require("./student.js");
const { name:stuName, getTel } = require("./school.js");
console.log(stuName); // 张三
console.log(slogan); // 让自己走起路来都带风!
console.log(getTel()); // 13800138000
console.log(name); // 尚硅谷
注意:
require("./student.js")
中的路径./
表示为当前目录
,是一个相对路径
,且不能被省略
!
相对路径
在内部中,会转换为绝对路径
,根据路径获取模块文件内容
,最后包裹成自执行函数
,获取module.exports
值作为返回值
!
扩展
在
每个模块
中是如何能获取,exports
和module.exports
对象的?- 其实
每个模块都是一个函数体
,内部会将模块代码
通过函数
进行包裹并执行
,并获取module.exports
中的值!
function (exports, require, module, __filename, __dirname) { let school_name = "尚硅谷"; let slogan = "让天下没有难学的技术"; function getTel(){ return "010-12345678"; } function getCities(){ return "北京市海淀区西二旗"; } module.exports = { school_name, slogan, getTel, getCities, }; console.log(arguments.callee.toString()); }
arguments.callee.toString()
callee
可以获取函数本身函数体!
- 其实
在
浏览器
中使用CommonJs规范
,本质上是不可以的,需要通过browserify工具
来对文件进行编译
后才能让浏览器识别
!全局安装
browserify
:npm i browserify -g
编译文件
browserify index.js -o build.js
将
index.js
编译输出
结果为build.js
!页面中使用
<script src="./build.js"></script>
ESModule
ESModule模块化
是官方定义的模块化标准
,通过指定的模块语法来实现模块化!
初体验
使用
export
对变量
进行导出!
school.js
export let school_name = "尚硅谷";
export let slogan = "让天下没有难学的技术";
export function getTel() {
return "010-12345678";
}
function getCities() {
return "北京市海淀区西二旗";
}
student.js
export let name = "张三";
export let slogan = "让自己走起路来都带风!";
export function getTel() {
return "137299182731";
}
function getHobby() {
return ["学习", "旅游", "羽毛球"];
}
index.js
使用
import xx from "./xxx.js"
语法来导入模块代码
!
import * as student from './student.js';
import * as school from './school.js';
console.log(student.name);
console.log(school.school_name);
这里的
*
表示所有,as student
表示,将导入的模块对象
放入到一个别名为student
!
Node中运行ES模块
- 将
所有模块文件
的.js
后缀改为.mjs
! - 可以在
package.json
中增加type属性
,且值为module
即可!
导出数据
使用
export关键字
可以对模块中的数据进行导出
!
分别导出
分别导出
,就是在每个声明变量
和函数前
使用export关键字进行导出
!
export let school_name = "尚硅谷";
export let slogan = "让天下没有难学的技术";
export function getTel() {
return "010-12345678";
}
export const funName = () => {
console.log("funName", funName)
}
function getCities() {
return "北京市海淀区西二旗";
}
统一导出
统一导出
,在代码尾部使用export关键字
并跟一个类似于对象结构
的花括号
,对声明变量名进行导出
!
注意: export 后面 {} 花括号里面的内容不是以"键值对"形式去书写的,它类似于对象,但并不是属于对象范畴!
let school_name = "尚硅谷";
let slogan = "让天下没有难学的技术";
function getTel() {
return "010-12345678";
}
const funName = () => {
console.log("funName", funName)
}
function getCities() {
return "北京市海淀区西二旗";
}
export {
school_name,
slogan,
funName
}
默认导出
默认导出
,在export关键字
后面增加default关键字
,后面可以跟字面量表达式
!
默认导出
,只能在模块中存在一个,且不能有多个默认导出
!
let school_name = "尚硅谷";
let slogan = "让天下没有难学的技术";
function getTel() {
return "010-12345678";
}
const funName = () => {
console.log("funName", funName)
}
function getCities() {
return "北京市海淀区西二旗";
}
export default {
school_name,
slogan,
funName
}
这里会导出一个
default
对象!
export default function (){
console.log("aaa")
}
也可以
默认导出一个字面量
混入导出
混入导出
,同时可以使用分别导出
,统一导出
,默认导出一起使用
!
export let school_name = "尚硅谷";
export let = "让天下没有难学的技术";
export function getTel() {
return "010-12345678";
}
export const funName = () => {
console.log("funName");
};
function getCities() {
return "北京市海淀区西二旗";
}
export default getCities;
index.js
import * as student from './student.js';
import * as school from './school.js';
console.log(student.name);
console.log(school.school_name);
console.log(school.default);
console.log(school.funName());
导入数据
通过
import关键字
可以对模块进行导入
,需要注意
的是,导入的数据都是常量
,且无法被修改
!TypeError: Assignment to constant variable.
import { school_name as name, slogan } from "./school.js" // school_name = "new name"; 不能去修改值
导入全部
导入全部
,是指分别导入
以及默认导入
都会被加载进来
!
import * as school from "./school.js"
命名导入
只能用于分别导出
,默认导出不能用
!
import { school_name as name, slogan } from "./school.js"
默认导入
export default
import school from "./school.js"
混入导入
school.js
export let school_name = "尚硅谷";
export let slogan = "让天下没有难学的技术";
export function getTel() {
return "010-12345678";
}
const funName = () => {
console.log("funName");
};
function getCities() {
return "北京市海淀区西二旗";
}
export { funName }
export default getCities;
import getCities, { school_name, slogan, getTel, funName } from "./school.js";
console.log(getCities);
console.log(school_name);
console.log(slogan);
console.log(getTel());
funName();
动态导入
动态导入,也是按需导入,当触发某一个时机时,可以加载某一个模块!
import可以当做一个函数去执行,返回一个promise!
const btn = document.querySelector('button');
btn.onclick = dsyncImport;
async function dsyncImport() {
let data = await import('./school.js');
console.log("dsyncImport", data);
}
import不接收数据
当一个
模块没有导出语句
时,也可以通过import语法将模块进行导入
并执行模块中的代码
!
import "./school.js"
这里会
导入该模块
,并执行模块中的代码
!