这篇文章花了我两天时间,一开始觉得自己会模块化了,写着写着感觉好多地方都有点模糊,然后开始不断的翻笔记,搜资料。
写完之后,感觉还是美好的,学到东西了,略微打通了一些经脉。
正文开始
什么是模块
- 将一个复杂的程序按照一定规则封装成几个块,并进行组合在一起;
- 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信;
为什么要使用模块化
- 私有的作用域,避免命名冲突,减少命名空间污染
- 更好的分离, 按需加载
- 更高复用性
- 高可维护性
模块化的发展历史
函数时期
function fn() { }
命名空间时期:模块化的思路雏形初显
var student = {
name: 'luyi',
getScore: function() {
}
}
student.name
student.getScore
闭包时期
// 递增的小栗子
function test() {
let count = 1
return function(){
count++
console.log(count)
}
}
var a = test()
a()
解决依赖问题 : 此处已有现代模块化(vue)的感觉了,模块各自独立,模块之间有依赖(global, $),逻辑层操作视图层
(function(global, $) {
var name = 'Chenluo Notes';
let data = 'www.baidu.com'
// 操作数据的函数
function doSth() {
//用于暴露有函数
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}
function getName() {
return name
};
global.student = { name, doSth, getName };
})(window, jquery);
console.log(window.student)
CJS规范(模块化的一种规范)
此时的模块化已经具备了两个必要条件
- 隔离变量:每个模块有其独立作用域
- 相互通信:模块之间通过modules,modules.exports暴露了入口,可以互相通信
var module = {
exports: {
}
}
(function(modules, exports, require) {
const $ = require('../juery.min.js')
var name = 'Chenluo Notes';
function getName() {};
modules.exports = { name, getScore };
exports.name = name;
// exports = {name, getScore}
})(modules, modules.exports, require);
// require 函数,以一个路径作为参数,函数的功能是,读取路径指向的 js 文件的地址,
// 然后把文件读出来,以 js 的形式解析。
时代的车轮滚滚向前~~~
函数 => 命名空间 => 闭包 => 解决依赖 => cjs模块化规范
可以看出,技术的发展和迭代都是为了解决人们的一些痛点,然后不断进行创新迭代优化。就如最近大火的ChartGPT, 不也是为了解放劳动力,提高生产力。让资源更集中,查询更便捷,想法落地更快速。
当然,也引发了一些其他的问题~~~ 毕竟,未知的总是让人焦虑。
貌似有点跑偏,让我们接着奏乐接着舞(学模块化)!!!
什么是模块化规范
模块化规范是为了解决隔离变量
和相互通信
的问题,上文有提到
比如以下场景:
场景一,传统用script标签引入js文件的一些痛点~~~
// 传统引入js模块的方法
<script src="./A.js"></script>
<script src="./B.js"></script>
场景二,没有依赖关系
- A和B两个js我想B先执行完再执行A
- 我要拿到B返回的数据,放到A里去执行
场景三,不能按需解析
- 我要用到A时,A才会被解析(函数调用,接口请求)
基于以上场景
,再实际开发和维护过程中,就比较痛苦
所以模块化规范就诞生啦!!!
utils工具库想必有开发经验的小伙伴肯定不陌生,这算是最常见的模块化。
每个项目大家都约定熟成的封装一些公共的工具函数在utils里,方便在各个页面使用,提高开发效率。俗称造轮子
。
utils在vue项目中一般使用ESM规范导入,为了更好的进行模块之间的依赖,和按需解析。下文中引入的是一个文件,如果utils文件夹下面有多个文件,彼此之间通过ESM进行依赖,做一些逻辑处理,效果会更明显点。
// 引用方式: esm规范
import { isURL } from '@/utils'
console.log(isURL('https://juejin.cn'))
// 引用方式: cjs规范
let {timestampToHMS} = require("../../utils/index.js");
console.log(timestampToHMS(211312))
模块化规范有哪些
AMD, CMD, CJS(common js), ESM(es模块), UMD,一般共五种模块化规范
AMD 是一个已经过时的规范,和CMD一样都是异步的
一,AMD模块化规范, RequireJS
define('a', function () {
console.log('a load')
return {
run: function () { console.log('a run') }
}
})
define('b', function () {
console.log('b load')
return {
run: function () { console.log('b run') }
}
})
require(['a', 'b'], function (a, b) {
console.log('main run') // 🔥
a.run()
b.run()
})
// 依赖前置,引用的时候就执行方法
// a load
// b load
// main run
// a run
// b run
二,CMD模块化规范, sea.js
// CMD sea.js
define('a', function (require, exports, module) {
console.log('a load')
exports.run = function () { console.log('a run') }
})
define('b', function (require, exports, module) {
console.log('b load')
exports.run = function () { console.log('b run') }
})
define('main', function (require, exports, module) {
console.log('main run')
var a = require('a')
a.run()
var b = require('b')
b.run()
})
seajs.use('main')
// 依赖后置
// main run
// a load
// a run
// b load
// b run
三,CJS模块化规范,主要用于服务端编程, nodejs 最早实现了它,是同步的
(function(modules, exports, require) {
// require 函数,以一个路径作为参数,函数的功能是,读取路径指向的 js 文件的地址,
// 然后把文件读出来,以 js 的形式解析。
const $ = require('../juery.min.js')
var name = '张三';
function getName() {};
modules.exports = { name, getScore };
exports.name = name;
// exports = {name, getScore} 不能这样用,会使引用断了
})(modules, modules.exports, require);
四,ESM模块化规范,现代浏览器规范
import XXX from 'xxx';
export const xxxx;
export default xxxxxxx;
// 或者
// 浏览器中实现了它,ES6规范是浏览器的JS引擎识别JS代码的一种规则
<script type='module'>
import XXX from 'xxx';
</script>
五,UMD模块化规范, 通用模块定义,配置了多个模块,兼容AMD CMD CJS script浏览器标签
延伸一点点
- 当一个UMD模块在ESM环境中使用时,可以把它当作普通的JS库来使用,通过script标签引入即可。
- 而当一个ESM模块在UMD环境中使用时,则需要使用一些兼容策略,例如将ESM模块编译成UMD规范,并将其作为JS库来使用。
模块化和工程化的关系
提到工程化,不得不提webpack 和 rollup。
他们两个都是为了把项目中的高阶语法(ES6写法)打包成浏览器可以识别的低级语法(IIFE),也就是小伙伴们vue项目中dist目录下的文件。
上面的五种模块化规范,webpack 和 rollup都可以进行配置打包。
下面是小白会遇到的三个疑问(不要问我为什么知道)
一,打包后,通过配置生成的这五种规范,浏览器可以解析吗?如果不可以,为什么webpack/rollup中可以这样配置?
ESM规范是浏览器的亲儿子,也是我们上文一直提到的。ESM规范是可以被浏览器解析的。
- 可以直接在html文件中script type="module"使用improt export
- 也可以直接在服务器运行的浏览器环境中直接使用es6语法,但是不同的浏览器对模块化规范的支持程度有所不同。
那其他几种规范存在的意义是什么呢
- CJS是给服务器使用的,主要用于服务端编程,因为同步意味着阻塞可能。如果非要在浏览器中解析,也可以通过Browserify来把CJS的代码转成普通js(不带require)
- AMD和CMD是为了解决浏览器端异步加载的问题,需要通过模块加载器来让浏览器解析。AMD(requireJS), CMD(seajs)
- UMD综合了 CJS 和 AMD 规范的优点,兼容各种运行环境,可以在浏览器和 Node.js 环境中运行。UMD 规范支持多种模块加载器,可以满足不同项目的需求,比较灵活。
二,开发过程中,webpack可以解析这五种规范吗?
- 当然可以,项目中不论是import(ESM),require(CJS)等写法,webpack都可以解析,并且可以打包成你指定的模块化规范。
三,webpack 和 rollup有啥区别呢?
-
Rollup是提供一个充分利用ESM各项特性的高效打包器,更加专注于打包 JavaScript 库和组件。主要优势在于可以生成非常小的包,因为它采用 ES6 模块语法并利用 Tree shaking 技术进行优化。这使得与其生成的包一起使用的代码量最小化。
-
Webpack 的设计目标是支持复杂的应用程序打包。它可以将许多不同类型的代码(包括 JavaScript、CSS、图片等)打包为一个或多个文件,同时还支持功能强大的开发工作流,如热替换、代码分割和动态导入。
完结
这篇文章我尽力把我的笔记和想法放到这了,希望对小伙伴有帮助。
欢迎转载,但请注明来源。
最后,希望小伙伴们给我个免费的点赞,祝大家心想事成,平安喜乐。