模块化

将不同的功能拆分成独立的文件,可以开放部分接口给外部

手写一个模块管理引擎

1const module = (function () {
2  const moduleList = {};
3  function define(name, modules, action) {
4    modules.map((m, i) => {
5      modules[i] = moduleList[m];
6    });
7    // 模块初始化
8    moduleList[name] = action.apply(null, modules);
9  }
10  return { define };
11})();
12
13module.define("computed", [], function () {
14  // 导出
15  return {
16    first(arr) {
17      return arr[0];
18    },
19    max(arr, key) {
20      return arr.sort((a, b) => b[key] - a[key])[0];
21    },
22  };
23});
24
25module.define("lession", ["computed"], function (computed) {
26  // 导入computed
27  let data = [
28    {
29      name: "js",
30      price: 100,
31    },
32    {
33      name: "mysql",
34      price: 199,
35    },
36  ];
37  const result = computed.max(data, "price");
38  console.log(result);
39});

模块的基本使用

模块无论加载多少次,只会在第一次时执行

1const name = "dancy";
2const age = 18;
3export { name };
1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>模块使用</title>
7  </head>
8  <body>
9    <script type="module">
10      import { name, age } from "./module.js";
11      console.log(name); // dancy
12      console.log(age); // Uncaught SyntaxError: The requested module './module.js' does not provide an export named 'age'
13    </script>
14  </body>
15</html>

:::

模块延迟解析与严格模式

模块默认是在最后解析

1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>模块</title>
7  </head>
8  <script type="module">
9    console.log(document.querySelector("button"));
10  </script>
11  <body>
12    <button>按钮</button>
13  </body>
14</html>

使用模块时默认是严格模式

1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>模块</title>
7  </head>
8  <script type="module">
9    console.log(this); // 后执行,打印结果为:undefined
10  </script>
11  <script>
12    console.log(this); // 先执行,打印结果为:window
13  </script>
14  <body></body>
15</html>

模块的作用域

每一个模块都是一个独立的作用域

1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>模块</title>
7  </head>
8  <script>
9    let site = "多面体";
10  </script>
11  <script>
12    console.log(site); // 多面体
13  </script>
14  <body></body>
15</html>
1<!DOCTYPE html>
2<html lang="en">
3  <head>
4    <meta charset="UTF-8" />
5    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6    <title>模块</title>
7  </head>
8  <script>
9    // 顶级作用域
10    let name = "dancy";
11  </script>
12  <script type="module">
13    // 独立作用域
14    let site = "多面体";
15    console.log(name); // dancy
16  </script>
17  <script>
18    console.log(site); // Uncaught ReferenceError: site is not defined
19  </script>
20  <script type="module">
21    console.log(site); // Uncaught ReferenceError: site is not defined
22  </script>
23  <body></body>
24</html>

:::

预解析的必要性

模块化演变过程

  1. 文件划分方式:完全依靠约定,每个文件就是一个独立的模块,使用模块时通过script标签引入,然后在代码中直接调用模块中的全局成员,成员有可能是变量,也有可能是一个函数

    • 污染全局作用域:模块中的所有成员都可以在模块外部被任意访问或修改

    • 命名冲突问题:模块一旦多了就容易产生命名冲突

    • 无法管理模块依赖方式

  2. 命名空间方式:每个模块只暴露一个全局的对象,所有的模块成员都挂载在这个对象下

1let moduleA = {
2  name: "dongxu"
3}
  • 污染全局作用域
  • 无法管理模块依赖方式
  1. IIFE:使用立即执行函数的方式为模块提供私有空间

    具体做法就是将模块中的每一个私有成员都放在一个函数提供的私有作用域当中,对于需要暴露给外部的成员,可以通过挂载到全局对象上去实现,这种方式实现了私有成员的概念,确保了私有变量的安全

1;(function (){
2	let name = "dongxu"
3  window.moduleA = {
4    name: name
5  }
6})()

模块化规范

常用的模块化打包工具

模块化概述

什么是模块化

  • 将程序文件依据一定规则拆分成多个文件,这种编码方式就是模块化的编码方式。

  • 拆分出来的每个文件就是一个模块,模块中的数据都是私有的,模块之间互相隔离

  • 同时也能通过一些手段,可以把模块内的指定数据「交出去」,供其他模块使用。

为什么需要模块化

随着应用的复杂度越来越高,其代码量和文件数量都会急剧增加,会逐渐引发以下问题:

  1. 全局污染问题
  2. 依赖混乱问题
  3. 数据安全问题

有哪些模块化规范

  1. CommonJS - 服务端应用广泛
  2. AMD
  3. CMD
  4. ES6 模块化 - 浏览器端应用广泛

导入与导出的概念

模块化的核心思想就是:模块之间是隔离的,通过导入导出进行数据和功能的共享。

  • 导出(暴露):模块公开其一部分(如变量、函数等),使这些内容可以被其他模块使用
  • 导入(引入):模块引入和使用其他模块导出的内容,以重用代码和功能。

CommonJS规范

在 CommonJS 标准中,导出数据有两种方式:

  • 第一种方式:module.exports = value

  • 第二种方式:exports.name = value

注意点:

  1. 每个模块内部的:thisexportsmodule.exports 在初始化时,都指向同一个空对象,该空对象就是当前模块导出的数据。

    ![image-20240810114816856](/Users/dongxu/Library/Application Support/typora-user-images/image-20240810114816856.png)

  2. 无论如何修改导出对象,最终导出的都是 module.exports 的值

1// 导出
2exports.name = name
3
4module.exports = {
5  name
6}
7
8// 导入
9const a = require('./a.js')