一、代码看:https://github.com/lizhongzhen11/myStudy/tree/master/js/AmdRequire
参考自:
https://github.com/youngwind/blog/issues/98
https://segmentfault.com/a/1190000006040968
http://www.cnblogs.com/yexiaochai/p/3632580.html
二、前言
先让我缓一缓,抚平一下激动地心灵!
好了,说话吧。
首先,为什么会有这篇文章?
因为我不想做一个coder~。
说人话!
哦,其实吧,我以前没用过require.js
,sea.js
,也不知道什么是Amd规范和CommonJs规范,因为我一接触前端就开始es6了,直接import
和export
别提多爽!但是,我并不知道它们的原理是什么。当然,如果仅仅会用这两个api也能干活,但是成长低,容易被替代。还有比较关键的是,前端发展很快,现在不少面试官估计都经历了很多前端技术变革,时不时的问一些以前的优秀技术来秀技术,要是回答不出来吧,估计面试官内心鄙视一波:你真菜。。。其实最重要的是:不要做一个只用api的coder,要学会去思考这个api或者框架的原理,思考的过程也是技术成长的过程!
三、let’s go!
先把参考链接看一下,尤其是第一个,作者讲了他的思想。当然,少不了看他的代码,我的实现里没有考虑循环依赖等问题,所以是最low级别的。
其实一开始我并没有定义defineOfLzz
这个变量,因为没用过require.js
,所以一开始我想的是直接实现一个类似于<script src="b.js" />
即加载js
文件的功能。所以第一个版本很简单,在index.html
里面只需要引入我的requireOfLzz.js
,然后设置一个主入口js文件–a.js,在a.js
里面可以直接调用requireOfLzz()
来加载所有其他的js
文件。一开始的想法真的很简单,不过由于js
基础还是不够,一些不怎么用的api以前没用过只能一个个去试。
这里我需求有了,接下来就要看实践了。第一个问题:如何定义主入口js?
我看require.js
是这样使用的:<script src="require.js" type="text/javascript" data-main="main.js"></script>
,那么这里的data-main
不就是主入口js吗?所以我依葫芦画瓢就好了,直接先这样写:<script data-main="a.js" src="requireOfLzz.js"></script>
。
那么问题又来了,我该怎么去加载这个主入口a.js呢?
根据需求,index.html
里只能引入一个requireOfLzz.js
,其他的js
看来只有靠requireOfLzz.js
内部代码去引入了。但是如何做?How to do ?
引入一个js
貌似一般就是用<script src="">
这种方式,但是,原谅我菜,我没想到怎么在requireOfLzz.js
里面实现。这时多亏了本文第一个参考链接里面的代码,我才恍然大悟,附上代码:
是的,直接通过DOM提供的api去创建一个新的script
标签,并指定src
以及type
,然后插入进DOM树中即可。原谅我之前脑子没转过弯来,因为下面有个问题真的是脑子没转过弯来。。。
好,此时已经知道如何去引入js
了,那么我需要src
的属性值吧。我得想办法把path
传过来。data-main
是<script>
标签里面的属性,我怎么得到呢?如果是正常的<dic>
我可以直接document.getElementByTagName('div')
,但是<script>
标签可以吗?还有,万一不会用的人在index.html
写了其他的<script>
标签怎么办呢?
得益于编辑器的提醒功能以及本文第一个参考链接的代码,我得知了
document.currentScript
方法,遂打印出来,发现通过document.currentScript.dataset.main
就能拿到<script>
标签里的data-main
属性值,very good!
接下来,我就可以把得到的data-main
属性值赋值给path
不就行了。其中,data-main
必须是相对于index.html
的路径。
- 中场休息
好,主入口a.js可以加载了,接下来只需要在主入口a.js里面去加载其他的js
就好了。那么,How to do ?
首先,还是利用创建一个新的script
标签然后插入body
的方法。那么我是不是可以把该方法写成一个函数方便去调用呢?of course!createScript()
函数应运而生~
另一个问题:如何在主入口a.js里面传递其他js
文件的地址方便我去调用createScript()
呢?
这时,查看源码以及参考链接都会发现,在(function(){})()
外面都定义了一些变量,包括require
和define
。那我继续也定义一个requireOfLzz
变量。然后怎么做呢?当然是继续参考人家的代码啦。。。
其实根据需求来说,我肯定是要把其他js
的路径传过来的,那么无疑通过函数传参这种方式了。简易版的:
这样只需要在主入口a.js里面requireOfLzz('b.js')
就能把b.js
路径传进来了,然后requireOfLzz
函数内部去调用createScript(path)
不就可以加载了吗?那我的最初版构思不就完成了吗?哈哈哈哈哈哈!当然,我这里都没做判断,代码不够健壮起码需要判断path
是不是String
吧。
但是仔细和他人代码对比发现,尼玛,相差十万八千里啊!他们的define
用来干嘛的?还有,好像还需要提供回调函数啊。。。
回调简单,加上不就好了,哈哈哈哈哈。(坑来了,可惜我不会打哭的表情。)
requireOfLzz
改写:
问题来了,怎么执行回调呢?成功时执行成功的回调,失败时执行失败的回调?这才像样嘛!这时候我也想骚一把,学前辈们的代码定义一个ModuleOfLzz
函数,然后在它原型上预定初始化init
和fetch
方法,同时,我如何去判断执行成功和失败呢?我需要预定一些状态常量,然后改写:
这样是不是好很多了呢?但是到这里我只实现了require
,全程不见export
的身影,那么做人有点追求嘛,哪怕再low,基础的export
也得实现啊。可惜,我百思不得其解,怎么去export
???
幸亏找到了参考第二个链接,虽然也看了半天,复制下来在本地跑起来发现没错,然后自己再一个个的对比着去写。我所纠结的地方在我的代码里也给出了,其实就是回调函数呢打印参数是undefined
!网上的案例却没有这个问题,经过反复思索与尝试,
发现是自己call
方法用错了,只传了this
却没有传其他参数,尴尬!而加上defineOfLzz
函数,我想可能是减轻requireOfLzz
的负担吧,毕竟不是所有的使用都需要执行回调函数的。