参考
作者:木头房子 链接:https://juejin.cn/post/6844903817272623117
作者:浪里行舟 链接:https://juejin.cn/post/6844903744518389768
ps:我只是通过自己学习这几篇文章进行了一个总结,另外根据自己的理解进行了拓展,仅仅是一个学习笔记,感谢大佬们写的优质文章,建议去阅读原文
前言
模块化概述?
-
什么是模块?
* 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起; * 块的内部数据/实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信; * 一个模块由内部的数据和方法组成 -
非模块化会造成的问题
1. 难以维护 2. 依赖模糊 3. 请求过多 4. 命名冲突 5. 污染全局变量 -
模块化的好处
* 更好地分离:避免一个页面中放置多个script标签,而只需加载一个需要的整体模块即可,这样对于HTML和JavaScript分离很有好处; * 更好的代码组织方式:有利于后期更好的维护代码; * 按需加载:提高使用性能,和下载速度,按需求加载需要的模块 * 避免命名冲突:JavaScript本身是没有命名空间,经常会有命名冲突,模块化就能使模块内的任何形式的命名都不会再和其他模块有冲突。 * 更好的依赖处理:使用模块化,只需要在模块内部申明好依赖的就行,增加删除都直接修改模块即可,在调用的时候也不用管该模块依赖了哪些其他模块。
模块化的进化史
全局function模式
直接将不同的功能封装成不同的全局函数
1 | function m1(){ |
问题:
污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系namespace模式/对象封装模式
将方法和数据封装到一个对象中成为模块,减少了全局变量,解决了命名冲突
1 | let myModule = { |
问题:
数据不安全(外部可以直接修改模块内部的数据)LIFE模式/立即执行函数
将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口,数据是私有的,解决了数据不安全的问题,只能通过外部暴露的方法进行操作
问题:
无法处理依赖关系1 | (function(window) { |
LIFE模式增强:引入依赖
如果当前的模块依赖jquery这个模块怎么办呢?可以用以下的写法。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显,这是现代模块化的基石。
1 | (function(window, $) { |
CommonJS规范
概述
node.js是CommonJS的主要实践者。Node由应用模块组成,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理(在引入之前已经完成了处理,运行的是打包生成的js文件)
特点
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
- CommonJS 模块输出的是一个值的拷贝。
基本语法
module,exports(暴露接口),require(加载模块),global
1 | // 定义模块math.js |
规范概述
- Node内部提供一个Module构建函数。所有模块都是Module的实例。
- 每个模块内部,都有一个module对象,代表当前模块。
- module.exports属性表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量。
- Node为每个模块提供一个exports变量,指向module.exports。
- 如果一个模块的对外接口,就是一个单一的值,不能使用exports输出,只能使用module.exports输出。
更多的内容在学习node的时候在进一步了解。
AMD规范
- CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。
- AMD规范则是非同步加载模块,允许指定回调函数,可以实现异步加载依赖模块,并且会提前加载;
- 由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。
- 如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。
AMD规范需要搭配require.js使用
1 | define(['module1', 'module2'], function(m1, m2){ |
CMD规范
CMD规范专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。在 Sea.js 中,所有 JavaScript 模块都遵循 CMD模块定义规范。搭配sea.js使用。
1 | define(function (require, exports, module) { |
ES6规范
概述
- 模块化的规范:CommonJS和AMD两种。前者用于服务器,后者用于浏览器。
- 而ES6 中提供了简单的模块系统,完全可以取代现有的CommonJS和AMD规范,成为浏览器和服务器通用的模块解决方案。
- ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。
基本语法
export
export用于规定模块对外的接口,export不止可以导出函数,还可以导出,对象、类、字符串等等;
1 | //test.js文件 |
import
1 | //导入一个模块 |
ES6 模块与 CommonJS 模块的差异
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
- CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- ES6 模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
1 |
|
总结
- CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案。
- AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅。
- CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行。不过,依赖SPM 打包,模块的加载逻辑偏重
- ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
如果这篇文章对你有帮助,可以bilibili关注一波 ~ !此外,如果你觉得本人的文章侵犯了你的著作权,请联系我删除~谢谢!