在B站上刷到一个讲webpack的课程视频,听课效果非常不错,打算做个听课笔记
视频链接在这里啦~「前端工程化」之 Webpack 原理与实践(彻底搞懂吃透 Webpack)汪磊原版_哔哩哔哩_bilibili
一、模块化演变过程
1. 文件划分方式
约定每个文件就是一个单独的模块,这种方式完全依靠约定。当项目有了庞大体量后,约定关系会变得非常复杂。
缺点:1.污染全局作用域 2.命名冲突问题 3.无法管理模块依赖关系
2. 命名空间方式
约定每个模块只暴露一个全局对象,模块中所有成员都挂载在这个对象身上。
这种方式可以减小命名空间冲突的可能性。缺点:模块仍然没有自己的私有空间,模块内部的成员仍然可以在外部被修改。另外模块之间的依赖关系也没有得到解决,还是得需要我们程序员自己去判断和约定
3. IIFE立即执行函数
可以利用立即执行函数提供私有空间,将模块中所有成员都放在一个函数提供的私有作用域中,对于需要暴露给外部的成员,通过挂载到全局对象上来实现。
此外,还可以利用自执行函数的参数作为依赖声明去使用,这样会使得每一个模块的依赖关系变得更加明显了。比如在模块中使用jQuery,可以通过立即执行函数去接收jquery 的参数,在立即调用时传递jQuery的参数。这样的话,在后期维护该模块时,可以很清楚地知道这个模块需要依赖jQuery
以上三种方式是早期没有工具和规划的情况下对模块化的落地方式,依旧还存在很多问题呀~
二、模块化规范的出现
为了统一不同的开发者习惯以及不同项目之间的差异,需要制定一个标准去规范模块化实现的方式。针对以往的模块加载方式,比如通过script标签进行引入,如果后期想要增加或者删除某些script文件时,需要开发人员手动进行引入和删除,比较麻烦。因此,需要一些公共的基础代码去实现自动通过代码来加载模块的功能。综上所述,我们需要制定一套【模块化规范】和一个可以用来自动加载模块的基本库,即【模块加载器】。
(1)CommonJS规范
首先介绍CommonJS规范,它是nodeJS提出来的一套标准,因此node中的JS代码必须要遵守CommonJS规范。该规范约定了以下内容:
- 一个文件就是一个模块
- 每个模块都有单独的作用域
- 通过【module.exports】导出成员
- 通过【require函数】载入模块
但是在浏览器端想要使用这套规范,就会出现问题。该规范约定以【同步】的方式去加载模块,node的执行机制是:在启动时去加载模块,执行过程中是不需要加载模块的,只会使用到已加载好的模块。所以同步方式在node中是没有问题的。但是在浏览器端,这种规范的使用会导致代码执行效率低下,因为每次页面的加载都会出现大量的同步模块请求。
(2)AMD(Asynchronous Module Definition)
专门为浏览器端制定了一套规范,即AMD,异步的模块定义规范。同期,还提出了一个非常有名的库,即Require.js,该库实现了AMD规范,另外它本身又是一个非常强大的模块加载器。
它约定每一个模块都必须要通过define函数去定义,如下图所示,函数可以传入两个参数,也可以传入三个参数。如果传递三个参数的话,第一个参数是模块的名字(后期加载该模块时可以使用);第二个参数是一个数组,用来声明模块的依赖项;第三个参数是一个函数,函数的参数和前面的依赖项是一一对应的,每一项为依赖项模块所导出的成员,该函数的作用可以理解为:为当前的模块提供一个私有的空间。如果需要导出一些成员,可以通过return的方式来实现。
除此之外,Require.js库中还存在一个require函数,用来帮助我们自动加载这个模块。其用法和define函数类似,区别在于:功能不同,一个是定义模块,一个是加载模块。一旦Require.js需要加载一些模块,内部会自动地创建script标签去发送对应的脚本文件请求,并且执行相应的模块代码。
目前,绝大多数的第三方库都支持AMD规范,说明该规范的生态性是不错的。但是,使用起来会比较复杂,编写过程中,不仅需要书写业务代码还需要使用define、require这种操作模块的代码,这些会导致代码的复杂度提高。此外,如果项目中的模块划分过于细致,那么在同一个页面中对JS文件的请求次数就会增多,从而导致效率低下。因此,可以将AMD规范视为前端模块化演变过程中的一步,是一种妥协的实现方式,并不是一个最终的解决方案。
(3)目前模块化标准规范
现如今的前端模块化规范,已经非常成熟了。而且目前大家针对前端模块化的最佳实践方式,也已经基本统一了看法。即在nodeJS环境中使用CommonJS规范,在浏览器端使用ES Module规范。
在nodeJS中使用CommonJS是无异议的,因为CommonJS是内置的模块系统,没有任何的环境问题。但是对于ES Module而言,情况会复杂一些。ES Module是由ES6标准所定义的最新的模块系统,由于ES6是前几年提出来的,所以可能会存在各种各样的环境问题。在ES6标准最早被推出的时候,所有的主流浏览器基本都是不支持的。但是后来随着webpack等一系列打包工具的流行,这一规范才逐渐开始普及。截止到目前,ES Module是最主流的前端模块化标准,相较于AMD这种社区所提出来的开发规范,ES Module在语言层面实现了模块化,因此更加完善。目前绝大多数浏览器已经开始支持ES Module这个特性了,原生的支持可以在之后的编程中直接去使用了,未来会有一个比较好的发展。
三、ES Module
对于ES Module的学习可以从两个维度入手:1.该规范约定了哪些特性和语法? 2.如何通过一些工具和方案去解决它在运行时可能会存在的兼容性问题
(1)ES Module的语法特性
目前绝大多数浏览器已经开始支持ES Module这个特性了,通过给script标签添加 type=module的属性,就可以以ES Module的标准执行其中的JS代码了,如下:
1. ESM自动采用严格模式,忽略 “ user strict ”。这时如果打印“this”,则为undefined
2. 每个ESM都是运行在单独的私有作用域中。
上述代码中,第一个打印结果是100,第二个则报错:foo is not defined
3. ESM是通过CORS的方式请求外部JS模块的。
(注意:如果请求一个外部地址,则服务器必须支持CORS才可以)
上述代码中,请求地址的服务器并不支持CORS,因此会报错
4. ESM中的script标签会延迟执行脚本。等同于向script标签中添加defer属性
(1)ES Module的导入与导出
在ESM中,导入和导出主要由“import”和“export”这两个关键词去构成的,export命令用于在模块内对外暴露接口,而import命令则是在模块内导入其他模块所提供的接口。
对于导出而言,首先