模块化系列课程(一):模块化的演变过程

183 阅读3分钟

模块化的意义及学习路径:

意义

模块化是前端重要开发范式,前端项目日益复杂,项目需要花大量时间管理。

模块化将复杂代码依据功能不同划分为不同的模块,降低维护成本,提高开发效率。

学习路径

  • 模块化演变过程介绍(了解历史、价值、及解决的问题)
  • 前端模块化标准,规范
  • 常用模块化打包工具
  • 结合案例应用模块化打包工具
  • 介绍常用模块化打包工具的优化技巧提高使用

演变过程

1、文件划分方式

每个功能、状态数据不同的文件,约定每个文件为不同模块,使用模块即使用JavaScript通过src引用,使用全局成员,例全局变量或全局对象

缺点:

  • 污染全局作用域
    模块内的所有成员可在外部任意更改、任意访问
  • 命名冲突问题
  • 无法管理模块依赖关系

早期模块化完全依靠约定,项目体量变大则不行

2、命名空间方式

每个文件向外部暴露一个全局对象,所有的对应的方法全部挂载在全局对象上。

文件目录大概如下:

——demo
————module-a.js
————module-b.js
————index.html
//module-a.js
var moudleA = {
    name: 'module-a',
    method1: function (params) {
        console.log('moduleA - mthod1');
    },
    method2: function (params) {
        console.log('moduleA - mthod2');
    }
};
//module-b.js
var moudleB = {
    name: 'module-b',
    method1: function (params) {
        console.log('moduleB-method1');
    },
    method2: function (params) {
        console.log('moduleB-method2');
    }
};
//index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <h1>模块化演变(第二阶段)</h1>
        <h2>每个模块只暴露一个全局对象,所有模块成员都挂载到这个对象中</h2>
        <script src="./module-a.js"></script>
        <script src="./module-b.js"></script>
        <script>
            moudleB.method1();
            console.log(moudleA.name);
            moudleA.name = 'changeNAME';   //在模块外部可以随意修改内部变量
            console.log(moudleA.name);
        </script>
    </body>
</html>

模块内给模块内每个成员增加了命名空间的方式,比第一种方法相比较优点是避免了变量名冲突,不过依旧存在缺点:

  • 仍然没有私有空间,内部成员在外部可随意访问、随意修改。
  • 各个模块直接的依赖关系不清晰

3、IIFE 立即执行函数

立即执行函数给模块提供私有空间,每个成员放入函数的私有空间中,需要暴露给外部的成员挂载在全局对象上实现。

实现了私有成员概念,私有成员模块只能在模块内部通过闭包的方式来实现,在外部是无法访问,实现了私有空间的安全。

——demo
————module-c.js
————index.html
//module-c
!(function (params) {
    var name = 'IFEE';
    function method1(params) {
        console.log('IFEE method1');
    }

    function method2(params) {
        console.log(name + 'IFEE method2');
    }

    window.moduleC = {
        method1: method1,
        method2: method2
    };
})();
//index.html
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
    </head>
    <body>
        <p>测试module-c引入Jquery模块改变p标签颜色</p>
        <script src="./module-c.js"></script>
        <script src="./module-d.js"></script>
        <script
            src="https://code.jquery.com/jquery-3.6.0.min.js"
            integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4="
            crossorigin="anonymous"
        ></script>
        <script>
            console.log(moduleC.name);  // moduleC is not defined。无法直接使用模块c中name值
            moduleC.method1(); //IFEE method1
        </script>
    </body>
</html>
  • 这种方式优势在于有自己的私有空间,模块内数据不会直接被外部随意使用,需要提供给外部使用的数据通过闭包形式提供给外部使用。
  • 可以清晰地表明模块间依赖关系;立即执行函数可传入参数,在立即执行时传入需要依赖的模块,例如下方代码:
    模块c需要依赖于jquery模块,可以传入jquery。下方代码为伪代码,具体实现还是需要用到下面的模块化知识。
//module-c.js
!(function ($) {
    var name = 'IFEE';
    function method1(params) {
        console.log('IFEE method1');
        $('p').css({ color: '#ff0011', background: 'blue' });
    }

    function method2(params) {
        console.log(name + 'IFEE method2');
    }

    window.moduleC = {
        method1: method1,
        method2: method2
    };
})(JQuery);