 
            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"这里会
导入该模块,并执行模块中的代码!
 
            
        