-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
如何实现一个异步模块加载器--以requireJS为例 #98
Labels
Comments
mark |
在 |
a模块加载完, 所有依赖a的小伙伴们的depCount--; 当depCount为0的时候, 就执行这个回调。 |
不错 |
文中,“比如Task没有网络请求,所以不需要fetch这样的方法“,请教下这里,感觉这里说的不太准确,例如在模块a中通过require['c'], 这样的方式(虽然建议在依赖中加入,但是不能排除这种使用)加载模块c,因为之前没有对c的依赖,所以会new Module, 并且执行fetch. 只有在循环依赖模块中的时候,文中所描述的才是对的。这是我的理解。。。不知道有问题木有。。。 |
nice |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
为什么
最近参考requirejs的API,自己动手实现了一个简单的异步模块加载器fake-requirejs。
为什么要做这样一个东西呢?
原因是:我一直觉得自己对模块化这方面的理解不够深入,即便用了很长时间的webpack,看了很多模块化相关的资料,比如模块化的发展历史,比如amd,commonjs和cmd规范之争等等。然而,我依然觉得自己的理解流于表面,所以决定自己动手实现一个。
目标的选择:本来一开始的目标是webpack的,但是后来考虑到webpack是建立在模块化基础上的一个构建工具,且webpack的实现也相当的复杂,而我希望能够刻意区分开模块化和构建这两个概念。因为这有助于我集中有限的精力研究模块化这一个概念,所以后来决定实现requirejs,这是一个相对来说比较简单的异步模块加载器。虽然现在使用它的人已经越来越少了,但是正因为其简单和纯粹,倒是非常适合现在的我。
注:请确保掌握了requirejs的基本用法再往下阅读。
Module原型的设计
刚开始敲代码的时候,我就在想如何实现
require
函数和define
函数,但是后来我发现我错了,因为这陷入了面向过程编程
的误区,正确的方式应该是面向对象编程
。所以,我重新进行了思考。问题:这里都有哪些类型的对象呢?
答案:至少有模块(Module)这一类对象
那模块类对象有哪些数据呢?
又有哪些对应的操作这些数据的方法呢?
依赖分析与处理
顺着上面的思路一步步写,我碰到了一个难点:如何分析和处理模块的依赖?
举个例子:
我们的目标是:当模块
a
和b
都准备好之后,再执行成功回调函数;一旦a
或b
有任意一个失败,都执行失败回调函数。这个跟使用
Promise.all
和Promise.race
很像,但这一次我们是要实现它们。怎么办呢?我想了一个方法:记数法。分两步走。
Module.depCount
属性,初始值为该模块依赖模块数组的长度。depCount===0
,说明该模块依赖的模块都已经运算好了,通过setter触发执行该模块。Module.STATUS===5
,通过setter
触发下一步。mapDepToModule
,查找到依赖与该模块的所有模块,那么让那些模块都执行depCount--
。注:对象
mapDepToModule
的作用是映射被依赖模块到依赖模块之间的关系。结构如下图所示。举个例子:当模块
a
准备好之后,我们就遍历mapDepToModule['a']
对应的数组,里面的每一项都执行depCount--
。下面是一些关键的代码:
循环依赖
虽然我们都说循环依赖是一种不好的现象,应该在设计之初尽量避免。但是,随着项目越滚越大,谁又能保证一定不会出现?所以,**作为一个合格的模块加载器,必须解决循环依赖的问题。**那么,让我们先来看看别人是怎么处理的吧。
Commonjs和ES6的循环依赖
seajs的循环依赖
requirejs的循环依赖
这里我们不讨论各种处理方式孰优孰劣,我们只关注:
如何实现requireJS API文档中那样的功能?
仔细观察下面的例子:
a
与b
出现循环依赖我们能看到:模块
b
的回调函数中,并不能直接引用到a
,需要使用require
方法包住。那么问题来了:**在原先的设计中, 每一个
define
是跟一个模块一一对应的,require
只能用一次,用于主入口模块(如:main.js)的加载。但是,现在在模块
b
的回调函数中,又出现require(['a'])
,这显然是乱套了。至此,我发现
require
不应该仅仅是用于主入口模块的加载,require
应该对应更高层次的抽象概念:我将它命名为:任务(Task)
,这是一个有别于Module的新的类。每一次调用
require
,相当于新建一个Task(任务)。这个任务的功能是:当任务的所有依赖都准备好之后,执行该任务的成功回调函数。有没有发现这个
Task
原型与Module
很像?它们都有依赖、回调、状态,都需要分析依赖、执行回调函数等方法。但是又有些不同,比如Task
没有网络请求,所以不需要fetch
这样的方法。所以,我让**
Task
继承了Module
**,然后重写某些方法。关键代码如下:
至此,我们就完成了一个简单的异步模块加载器。
参考资料
------完-----------
The text was updated successfully, but these errors were encountered: