模块化标准【AMD】【CMD】【CommonJS】【ES6】以及 module.exports、exports、export、export default、require、import 间的理解概论

一、模块化(原因和概念)

先介绍一下模块化是个什么东西,解决了什么。
模块化概念和分类这个部分转自别人转的,也不知道原博在哪,但是写得很不错,所以拿来分析总结一下。

JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着 web2.0 时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀
这时候JavaScript作为嵌入式的脚本语言的定位动摇了,JavaScript却没有为组织代码提供任何明显帮助,甚至没有类的概念,更不用说模块(module)了,JavaScript极其简单的代码组织规范不足以驾驭如此庞大规模的代码
既然JavaScript不能处理好如此大规模的代码,我们可以借鉴一下其它语言是怎么处理大规模程序设计的,在Java中有一个重要带概念——package,逻辑上相关的代码组织到同一个包内,包内是一个相对独立的王国,不用担心命名冲突什么的,那么外部如果使用呢?直接import对应的package即可import java.util.ArrayList;
遗憾的是JavaScript在设计时定位原因,没有提供类似的功能,开发者需要模拟出类似的功能,来隔离、组织复杂的JavaScript代码,我们称为模块化。
一个模块就是实现特定功能的文件,有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范,各行其是就都乱套了

简单来说就是以前前端简单,项目工程也小,现在前端突然发展开来,随之一些需求及项目复杂度也增加,js语言原本的定位不足支持这些工程,就需要向一些成熟的老大哥学习(java等),模块化使代码从一坨变成规整的一块一块。

利于维护(一块出问题了就维护一块)

利于分配工作(每人负责几块)

利于使用(想用哪块的话就拿来哪块)

1、前端模块化的前辈

js模块化有3个最基础的前辈

  1. 函数封装(代码还是一坨)
  2. 向其他语言学习,创建对象(面向对象编程,如果把函数比作能做的事,对象就是做事的人,想要做什么事,就找某个能做这个事的人,而不是去成千上万个事中找。还是一坨。)
  3. 通过<script>标签引入自调用函数包(已经变成块了,不过这个块只能连接一层,块之间的依赖只能手动增添,比如a.js依赖于b.js,然后手动添加script标签,然后b.js又需要依赖c/d/e/f/g/h.js等,也要手动写入,而且要注意写入的先后顺序,而且很多都需要导出一个函数或对象到全局作用域才能使用。)

前面的模块化也适应不了日益增长复杂的前端项目。 这时后端(也就是node.js)迈出了真正模块化的第一步——CommonJS标准

2、CommonJS(同步加载,同步执行)

参考原文中这么写——

因为在网页端没有模块化编程只是页面JavaScript逻辑复杂,但也可以工作下去,在服务器端却一定要有模块,所以虽然JavaScript在web端发展这么多年,第一个流行的模块化规范却由服务器端的JavaScript应用带来,CommonJS规范是由NodeJS发扬光大,这标志着JavaScript模块化编程正式登上舞台。

但是CommonJS的思想并不能用在浏览器上——

模块系统需要同步读取模块文件内容,并编译执行以得到模块接口。
这在服务器端实现很简单,也很自然,然而, 想在浏览器端实现问题却很多。
浏览器端,加载JavaScript最佳、最容易的方式是在document中插入script标签。但脚本标签天生异步,传统CommonJS模块在浏览器环境中无法正常加载。
解决思路之一是,开发一个服务器端组件,对模块代码作静态分析,将模块与它的依赖列表一起返回给浏览器端。 这很好使,但需要服务器安装额外的组件,并因此要调整一系列底层架构。

就是说CommonJS是同步加载的,<script>标签是异步加载的,所以CommonJS的同步加载思想不能用在浏览器端上。

那问题来了,原文有这一句话——但脚本标签天生异步,传统CommonJS模块在浏览器环境中无法正常加载。

<script>标签不是会同步加载和执行并阻塞dom树的构建吗,怎么在他那边就变成异步加载了。
首先我们要先看清楚加载这个词的意思,它在浏览器端指的是下载,在服务器端可以指读取硬盘资源。
同步加载是指在构建dom树时原本就有的一些<script>标签,在前一个标签内容加载和执行完之前,会阻塞dom树的构建,也就是不会开始解析下面<script>标签,也就不会加载此标签的内容了。

但模块化需要的是让每一个模块本身可以自动加载所需依赖。所以肯定得手动用代码去添加<script>标签来加载和执行其中所带的代码段。

但是手动添加的<script>标签它就是异步下载的,也就是我想同时想要下载a、b两个依赖,它们会同时下载,而不是和CommonJS中的那样先下载完a再下载完b,而且如果浏览器端同步加载的话,因为网络下载的延迟性,会阻塞浏览器页面的显示。所以说环境和框架不同使用的标准也要有变化。

于是requireJSseaJS两个模块化工具出现了,随之而来的是requireJSseaJS分别衍生出来的两个标准AMDCMD

3、AMD(异步加载,异步执行)

AMD为前端JS制定了规范,它使用异步的方式去加载模块,并且所有与模块相关的代码都写在回调函数当中,不影响其他代码的执行。
它主张依赖提前,就是在开始便异步加载所有依赖,哪个依赖先加载完就先执行。不过主逻辑还是同步的等所有依赖加载完后再执行。
相对于使用自调用函数封装并用<script>标签导入来说,它解决了:

  1. 异步加载,不会阻塞浏览器构造dom树
  2. 自动加载依赖,不再需要手动引入模块间的依赖关系
    目前requireJScurlJS为这个标准的主要实现

4、CMD(异步加载,同步执行)

CMD是淘宝团队开发的SeaJS在推广过程中的产出,弥补了AMD的一大缺点——异步执行,由于AMD是异步加载,并且加载完直接执行,所以执行的顺序与加载速度有关,并且不可控,这就非常不友好,于是CMD提倡的

这些关键字都是用于在 JavaScript 中导出模块的方式。 1. `module.exports` 是 CommonJS 规范中用于导出模块的方式。在 Node.js 中,`module.exports` 是一个指向当前模块导出对象的引用。可以通过给 `module.exports` 赋值来导出一个对象或函数。 例如: ``` // 导出一个函数 function add(a, b) { return a + b; } module.exports = add; ``` 2. `exports` 是 `module.exports` 的一个辅助对象,可以通过给 `exports` 对象的属性赋值来导出模块。 例如: ``` // 导出一个对象 exports.name = 'Tom'; exports.age = 18; ``` 这个模块实际上等价于: ``` module.exports = { name: 'Tom', age: 18 }; ``` 3. `export` 是 ES6 中用于导出模块的关键字。可以通过 `export` 关键字导出一个变量、函数或对象。 例如: ``` // 导出一个变量 export const name = 'Tom'; // 导出一个函数 export function add(a, b) { return a + b; } // 导出一个对象 export default { name: 'Tom', age: 18 }; ``` 4. `export default` 也是 ES6 中用于导出模块的关键字,但是它只能导出一个默认值。 例如: ``` // 导出一个默认值 export default function add(a, b) { return a + b; } // 导出一个默认对象 export default { name: 'Tom', age: 18 }; ``` 在导入模块时,可以使用 `import` 关键字来引入模块。例如: ``` // 导入 CommonJS 模块 const add = require('./add'); // 导入 ES6 模块 import { name, add } from './module'; import person from './person'; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值