美文网首页
JS 模块化

JS 模块化

作者: elle0903 | 来源:发表于2023-02-15 11:12 被阅读0次

前言

我们知道,JavaScript 最开始诞生,只是作为嵌入网页的脚本语言来使用,那时候,它最重要的一个功能,就是帮助浏览器在不跟后端通信的情况下,验证用户提交表单的合法性。

可是随着时间的发展,业务场景逐渐复杂,尤其 ajax 诞生之后,前端的能力愈发强大。管理十人团队的经验显然不能用来管理万人团队,维护百行代码的方式,自然也不适用于开发周期长、开发难度高,甚至涉及多人协作的中大型项目。

这种情况下,JS 模块化的呼声愈发响亮。

但那个时候,JS 语言本身是没有模块化这个概念的,于是各种模块化方案被依次推出。

本文将通过介绍 JS 模块化的发展历史,来逐个了解那些曾经流行过,或者正在流行的 JS 模块化规范和实现。

废话不多说,我们直接开始吧。先来看看第一个部分。

JS 模块化的发展历史

JS 模块化的发展历史,遵循所有新生事物的历史发展轨迹:在迎来正式规范之前,经历一段野蛮生长的时期。具体来说,它可以被分成如下三个阶段:

  1. CommonJS 出现前的史前混乱时期
  2. ES6 Module 出现前的大混战时期
  3. 现在:ES6 Module 的亮相

下面,我们逐项来了解。

CommonJS 出现前的史前混乱时期

从标题可以看出,这个时期的 JS 模块化是没有任何规范可言的,用最形象的四个字来形容,那就是:随 心 所 欲

最开始大家通过拆分文件,来完成模块的划分。

好比,一个网页有那么几个功能区,功能区 A 由小明负责,功能区 B 划分给小刚,他们各自新建文件去完成功能,最后统一汇总到 HTML。这时候的文件目录,差不多长这样。

|- index.html
|- src
   |- A
       |- index.js
       |- index.css
   |- B
       |- index.js
       |- index.css

这样做看起来还算简洁明了,但也只是看起来,它很快迎来问题。

因为所有 JS 代码的执行环境都是全局作用域,通过 var 声明的变量也都是全局变量,如果小明和小刚不小心,声明了同一个名称的变量,页面就会出错。

// src/A/index.js
var name = 'a';

// src/B/index.js
var name = 'b';

假设 A 文件先加载,B 文件在后,那么当小明尝试使用 name 时,他就会发现,这个变量已经被小刚污染。但这个问题也是不能解决,给变量添加命名空间就可以

我们给小明分配一个命名空间叫做 A,给小刚分配一个命名空间叫做 B,于是代码变成了如下模样:

// src/A/index.js
app.A = {
    name: 'a'
};

// src/B/index.js
app.B = {
    name: 'b'
};

这样的代码虽然写起来很恶心,相当恶心,但也不是不能接受,至少它解决了全局作用域下,变量命名混乱的问题。
可是!
随着小明的代码越写越多,小刚的代码越写越长,他们会慢慢发现:欸?我为什么要把所有变量都挂载到命名空间下面啊?明明有些变量我只在自己的文件里用到,根本不需要暴露给外面啊!

这个问题也容易解决,闭包!

好了,我们再来改一下代码。

// src/A/index.js
app.A = (function(){
    var name = 'a';
    var age = 18; // 不暴露的私有变量
    return {
        getName: function(){
            return name;
        }
    }
})();

// src/B/index.js
app.B = (function(){
    var name = 'b';
    var age = 19; // 不暴露的私有变量
    return {
        getName: function(){
            return name;
        }
    }
})();

通过一个立即执行的函数(IIFE),小明和小刚将他们需要导出的方法和变量,作为函数的返回值,暴露给命名空间,其余的,都隐藏在闭包内部。

这是一个相对完善的做法,虽然离完美还差了很远很远,很远很远。

上面例子中,如果 A 先于 B 加载,那么理所当然的,B 可以使用 A 所暴露的变量和方法,A 却不能使用 B 的。
这是一个依赖管理的问题,而当页面的复杂度进一步提升,参与开发的人员进一步变多,模块之间的依赖也将变得更加难以维护。举个例子:小明依赖小刚、小刚依赖小美、小美依赖小帅、小帅又依赖了小刚,这时候,你到底该先加载谁?

但这个方法还是在一定时期内,被很多团队使用了相当长一段时间,直到 CommonJS 规范的出现。

ES6 Module 出现前的大混战时期

1. CommonJS

2009年,JavaScript 社区提出了一个名叫 CommonJS 的标准,通过它,社区希望实现让 JS 运行在任何平台的伟大愿景。

JS 模块化的规范标准也被蕴含在其中,至此, JS 模块化大混战的帷幕也正式被拉开。

首先,2009 年 5 月,Node.js 出世了。

CommonJS 规范推动过程中,Node.js 应运而生,同时,Node.js 的出世也标志着 JS 模块化编程的正式诞生。

CommonJS 规定,每个模块内部有两个变量可以使用,require 和 module,开发者通过 module.exports 来完成接口和变量的导出,通过 require 来实现模块的导入。像这样:

// src/A/index.js
var name = 'a';
var age = 18;

// 导出一个获得私有变量 name 的方法。
module.exports = {
    getName: function () {
        return name;
    }
};

// src/B/index.js
var A = require('../A/index.js');
var name = 'b';
var age = 19;

console.log(A.getName());// a

// 导出一个获得私有变量 name 的方法。
module.exports = {
    getName: function () {
        return name;
    }
};

这个规范的实现是如此优雅、如此便捷,以至于,它刚刚推出,便受到社区的一致欢迎,但它也不是没有问题,它还有很多问题,最明显一个就是:

它跟前端没关系。

是的,我们都知道,Node.js 是运行在服务端的 JS,那么, 除非你有转去服务端的打算,否则,这玩意儿的出现,跟你没半毛钱的关系也没有。

他们的欢呼跟你没关,他们的雀跃只让你觉得:好酸啊。

不过俗话也说了,有问题就会有答案,有了 CommonJS + Node.js 这个先例,我们聪明又勤劳的前端开发者们,很快根据自身经验,摸索出了一套适用于前端开发的新规范。

它跟 CommonJS 最大的一个区别在于,它是异步加载模块的,所以,我们称它为:AMD( Asynchronous Module Definition,异步模块定义)。

2. AMD

它通过一个 define 函数,来实现对模块的定义和引用,基于该规范,RequireJs 被捯饬出来。你可以这样使用它:

<script src="require.js"></script>
<script src="src/A/index.js"></script>

此前的 JS 文件,也会被修改成如下模样:

// src/A/index.js
define(function () {
    var name = 'a';
    var age = 18; // 私有变量,不暴露

    return {
        getName: function () {
            return name;
        }
    };
});

// src/B/index.js
define(['../src/A/index.js'], function (A) {
    var name = 'b';
    var age = 19; // 私有变量,不暴露

    console.log(A.getName()); // a
    return {
        getName: function () {
            return name;
        }
    };
});

它解决了互相依赖的问题,按需加载也被支持,它在很大程度上,解决了此前模块管理混乱的问题。

但它并不是结束。

很快有人提出,RequireJs 虽然能用,却并非最优,按照这个规范编写出来的代码,既不够简洁,也不够优雅。他们希望能够像 Node.js 编写模块那样,在前端也实现自然直观的代码组织,基于这个想法,社区又多出一个 CMD 规范和基于该规范 sea.js。

3. CMD

Sea.js 官网这么介绍 Sea.js的:

Sea.js 追求简单、自然的代码书写和组织方式,具有以下核心特性:

  1. 简单友好的模块定义规范:Sea.js 遵循 CMD 规范,可以像 Node.js 一般书写模块代码。
  2. 自然直观的代码组织方式:依赖的自动加载、配置的简洁清晰,可以让我们更多地享受编码的乐趣。

按照这个规范编写出来的代码长这样:

// src/A/index.js
define(function (require, exports, module) {
    var name = 'a'
    var age = 18; // 私有变量,不暴露

    exports.getName = function () {
        return name;
    };
});

// src/B/index.js
define(function (require, exports, module) {
    var A = require('../A/index.js');

    var name = 'b'
    var age = 19; // 私有变量,不暴露

    console.log(A.getName()); // a

    exports.getName = function () {
        return name;
    };
});

在这之后,还有基于 CommonJS 规范的 Webpack,它让前端开发者可以像书写 Node.js 的代码那样,编写前端页面。

可以说,在 ES6 Module 出现之前,很长一段时间内,JS 模块化都处在这三种规范的混战之中。

然后时间来到 2015,es6 正式推出。

ES6 Module

前面提到的 CommonJS 、AMD 和 CMD 看起来天差地别,但它们其实也有不少共同之处:

  1. 它们都在代码运行后,才能确定导出的内容。
  2. 它们都是社区的开发者们,制定的规范方案。并不是语言层面的标准。

ES6 Module 在语言标准的层面上,实现了模块化功能,而且实现得相当简单,完全可以取代 CommonJS、CMD 和 AMD,成为浏览器和服务器通用的模块解决方案。

它主要由两个命令构成:export 和 import,export 负责导出,import 负责引入,在支持 ES6 Module 的环境下,你可以像这样编写代码:

// src/A/index.js
let name = 'a'
let age = 18; // 私有变量,不暴露

let getName = function () {
    return name;
};

export { getName };

// src/B/index.js
let A = import '../A/index.js';

let name = 'b'
let age = 19; // 私有变量,不暴露

console.log(A.getName()); // a

let getName = function () {
    return name;
};

export { getName };

目前,Node.js 已经全面支持 ES6 Module,现代浏览器也基本上实现了对这一语法糖的支持。

以上就是本文的全部内容啦,感谢看到这里的大小可爱,大家有缘江湖再见!

参考链接

相关文章

  • 模块化开发

    js模块化开发vue模块化开发

  • Javascript 模块化

    Javascript 模块化发展的历史精读 js 模块化发展直接定义依赖 (1999): 由于当时 js 文件非常...

  • js模块化规范

    title: js模块化date: 2019-01-30 17:49:22tags: js 1.无模块化 缺点:1...

  • js 模块化

    尚硅谷_JS模块化 笔记

  • 06Vue的前端工程化

    Vue的前端工程化 一 模块化规范 1.1模块化规范举例 浏览器端JS模块化规范:AMD,CMD 服务器端JS模块...

  • 模块化开发

    什么是模块化? 模块化就是讲js文件按照功能分离,根据需求引入不同的文件中。源于服务器端。 js模块化方案有AMD...

  • 前端模块化

    在学node.js, 实际上就是基于common.js开发的,所以了解了一下模块化开发。 JS的模块化初衷和所有语...

  • 04-webpack核心基础-ES6模块化

    一、模块化概述 在ES6出现之前,JS不像其他语言拥有“模块化”这一概念,于是为了支持JS模块化我们使用类、立即执...

  • 前端javascript模块化

    JavaScript js 模块化 关于js模块化的理解写法一 写法二 写法三 写发四(不推荐) 写发五 (错误写...

  • 做了这么久的前端开发,听过Css模块化开发么?

    说起前端模块化开发,大部分人可能只会想到js模块化开发吧,网上也确实有各种各样的js模块化方法,但是鲜有谈论Css...

网友评论

      本文标题:JS 模块化

      本文链接:https://www.haomeiwen.com/subject/skkekdtx.html