前端模块化

《源码学习之前端模块化》

1. 什么是模块化

2. 为什么需要模块化

3. 源码中的模块化

3.1 AMD

3.2 Commonjs

3.3 Vue源码解析之模块化

1. 什么是模块化

模块化并不是前端独有的思想。模块化是一种自顶向下的过程,通过把一个大的系统,逐步划分为一个个小的模块,这些模块内部封装了一些特定的功能,通过约定的接口对外暴露。各个模块之间互不干扰,易于插拔。 模块化可以解耦代码,更好地进行复用,每个模块之间互不影响,不用担心变量污染、命名冲突等问题,同时也有利于并行开发,提升效率。

js最初是没有模块化的。随着前端应用越来越复杂,比如,当一个html页面要请求多个js文件时,如何保证这些js文件之间的变量互不干扰?为了解决诸如变量污染、命名冲突的问题,js开始出现了一些模块化的方案,从2003年提出的闭包模块化,再到2009年的CommonJS和AMD,以及如今的ES模块化。

2. 为什么需要模块化

模块化的好处可以总结为以下几点:

拥有独立作用域,避免变量污染、命名冲突

降低项目复杂度,提高开发效率

提升代码可复用性和可维护性

3. 源码中的模块化

在上述提到的AMD和Commonjs规范,对于模块化的定义不尽相同,在进行模块化时需要对不同环境进行兼容处理。

3.1 AMD

AMD(Asynchronous Module Definition),异步模块定义。在加载模块以及模块所依赖的其它模块时,AMD都采用异步加载的方式,避免模块加载阻塞网页渲染。

AMD作为一个规范,只需定义其语法API,而不关心其实现。AMD规范简单到只有一个API,即模块定义define函数:

define(name?, [dependencies]?, factory)

name是模块的标识,如果未提供则以文件名为标识;

dependencies表示所依赖的模块;

factory是模块初始化要执行的函数或对象,如果是函数,只执行一次,如果是对象,即为模块输出值。

AMD规范主要是针对前端浏览器的规范。

3.2 Commonjs

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

CommonJS规范加载模块是同步加载的,也就是说,只有加载完成,才能执行后面的操作。

模块定义语法: module.exports=factory()

CommonJS主要是针对浏览器之外的js环境的规范。

3.3 Vue源码解析之模块化

以vue2的源码为例,将源码折叠后你会看到如下代码: 


这是一个匿名自执行函数,这个匿名函数拥有独立的作用域,既避免污染外界代码,也避免被外界代码污染。

如果需要用到外界的全局变量,可以通过参数传入,如图中的this,这个this实际上是Window对象,通过传入 this,使得Window由全局变量变为局部变量,当在后面那个代码块中访问 this时,不需要将作用域链回退到顶层作用域,这样可以更快的访问Window对象;将 Window作为参数传入,也可以在压缩代码时进行优化。如:(function(a,b){})(window); // window 被优化为 a。

global即自执行传进来的this参数,也就是Window对象。

factory即传进来的第二个参数,也就是后面那个匿名函数。

展开第一个function可以看到如下代码:

typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :

    typeof define === 'function' && define.amd ? define(factory) :

      (global = global || self, global.Vue = factory());

这段代码的作用是检测当前的运行环境支持哪种模块化规范,然后将Vue模块化,其中exports是Commonjs规范,define是AMD的规范。

在源码中可以看到,factory()最后return的是一个Vue对象。

当检测出为Commonjs规范时,导出Vue对象的写法为:

module.exports = factory();

当检测出为AMD规范时,写法为:

define(factory)。

为了更直观,改写一下这段代码:

if(typeof exports === 'object' && typeof module !== 'undefined'){

    // Commonjs规范

    module.exports = factory();

}else if(typeof define === 'function' && define.amd){

    // AMD规范

    define(factory);

}else{

    // 其他情况下,将Vue挂载到全局对象中

    // 在浏览器里就是给Window对象添加Vue属性,属性值为factory()返回的Vue对象

    global = global || self,

    global.Vue = factory();

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容