本节书摘来自异步社区《javascript框架设计》一书中的第2章,第2.3节,作者:司徒正美著,更多章节内容可以访问云栖社区“异步社区”公众号查看
require方法的作用是当依赖列表都加载完毕,执行用户回调。因此这里有个加载的过程,整个加载过程分为以下几步。
(1)取得依赖列表的第一个id,转换为url。无论是通过basepath+id+".js",还是以映射的方式直接得到。
(2)检测此模块有没有加载过,或正在被加载。因此我们需要一个对象来保持所有模块的加载情况。当用户从来没有加载过此节点时,就进入加载流程。
(3)创建script节点,绑定onerror、onload、onreadychange等事件判定加载成功与否,然后添加href并插入dom树,开始加载。
(4)将模块的url,依赖列表等构建成一个对象,放到检测队列中,在上面的事件触发时进行检测。
在mass的加载器,它支持允许第一个参数为字符串,然后内部按空格或逗号切分为id数组,以及做去重处理,其他都一样。
mass在这基础上做了扩展。
(1)模块id本来就是url的简体,因此可以包含斜线(/),并以/划分为多项。
(2)模块id应该是以符合变量的规则的字符串组成,第一个项可以是“.”或“..”。这些都是url的基本的规则,表示当前目录与父目录。
(3)模块id的未尾可以包含“.js”,但如果它是指向一个css文件,那么必须以css结尾。
(4)如果以“/”或“./”开头,表示它与加载它的模块在同一目录。
(5)如果以“..”开头,表示它在加载它的模块的上一级目录,如果存在多个“..”,就要向上找。
(6)如果模块id是“mass”,不做转换与加载,这表示mass框架的种子模块,也就是加载器的所在模块。
(7)如果模块id是“ready”,不做转换与加载,这用于延迟用户回调到dom树建完后执行。避免出现domready的回调函数与模块的回调函数出现套嵌。
(8)如果情况就直接在前面按上basepath——加载器所在的目录!
除了这些情况外,我们通常还用到映射,就是允许用户在事前用一个方法,把id与完整的url对应好,这样就直接拿。mass称之为别名机制。id只是给用户用的,框架还是url做加载或其他检测。此外,amd还发展一种shim技术,shim就是垫片的意思,目的是让不符合amd定义的javascript文件也能无缝切入我们的加载器系统。
如这个是普通的别名机制:
而对于jquery或其插件,我们需要shim机制:
下面是require的源码:
每require一次,相当于把当前的用户回调当成一个不用加载的匿名模块,id是随机生成,回调是否执行,要待到deps对象里面所有值都为2。
require里有三个重要方法:loadjscss,它用于转换id为url,后面再调用loadjs, loadcss,或再调用require方法;firefactory,就是执行用户回调,我们的最终目的;checkdeps,检测依赖是否都安装好,安装好就执行firefactory。
注意,上面的modules[src]是以完整路径做id的,它对应的对象没有state属性,表示其正在加载中。
loadjs与loadcss方法就比较纯粹了,不过loadjs会做个死链检测checkfail。
checkfail方法主要是用于开发调试。有3个参数。node——script节点,onerror——是否为onerror触发,fuckie——对应旧版本ie的hack。思路是,javascript文件从加载到解析到执行需要一个过程,在interact阶段,我们的javascript代码已经有些部分可以执行了,这时我们将模块对象的state改为1,如果还是undefined,我们就可识别它为死链。不过,此hack对不是以amd定义的javascript模块无效,因为将state改1的逻辑是由define方法执行的。如果判定是死链,我们就把此节点移除。
checkdeps方法会在用户加载模块之前及script.onload后各执行一次,检测模块的依赖情况,如果模块没有任何依赖或state都为2了,我们调用firefactory方法。
历经千辛万苦,我们终于到达firefactory方法。它的工作是从modules中收集各模块的返回值,执行factory, 完成模块的安装。