我一向认为seajs不应该用在前端,主要是因为同步加载依赖的做法极大影响用户体验。虽然我不会在自己的项目中使用它,但是对同步加载的实现我还是有点兴趣的。
seajs的源码风格并不是很杂乱,基本一眼扫过就可以看明白在做什么。鉴于源代码太长,我使用猜测+搜索的方式来查看。因为我所知道的加载js方法只有createElement('script')
和ajax+eval
两种,所以对于加载依赖的方法我首先尝试搜索createElement
这个关键字,在第285行找到了:
但是这些代码并不是我要,因为从这些代码只能看出是把依赖文件用非阻塞方式来加载,与同步加载毫无关联。难道seajs用的是ajax+eval么?我虽然不相信但还是尝试搜索了XMLHttpRequest关键字,结果没找到。
两条常规道路都堵死了,那么扩展思维,也许require的方法做了一些奇怪的事情。所以我接着搜索了require
关键字,我看到416行的正则和后面的替换操作:
到这里终于解开了同步加载的原理,其实是用了javascript-function的toString特性。parseDependencies方法接受参数为function字符串(请看代码第714行),调用之后首先使用正则表达式提取function里的所有依赖文件,在它们全部加载完之后执行function,所以实现了同步依赖。 虽然想法看似可行,但是从实现的角度上看seajs并没做好。比如说下面这样的代码均完全符合js语法,也没有违背seajs的规范,但实际上这些代码在seajs中都不会正常工作:
define(function(require){
// 第一种,连接字符串
require('jque'+'ry');
// 第二种,紧跟在多行注释之后
/*jquery*/require("jquery");
// 第三种,转义字符形式
require("jqu\u0065ry");
// 第四中,转义字符反斜杠解析丢失,资源地址后v参数是空的
require("jquery.js?v=\\\\");
});
也许我所列举的调用方式比较奇葩,但是生产环境和业务流程不会总是循规蹈矩,所以没人能保证不会发生这种情况。 除此之外,最根本的问题是seajs的同步加载依赖设计思想完全不符合前端环境。把js模块化的意义,在于降低耦合,提高内聚,不同功能之间不会互相影响。而seajs的加载方式直接反其道而行,下面代码很清楚的反应这个问题:
define(function(require){
require('a').run();
require('b').run();
});
不考虑a,b的内部依赖,仅看上面的两行代码中a和b并无直接关联,因此它们的执行顺序应该和写法无关,任何一个加载完成都应该立即执行,而不需要等待别的模块加载完成。而seajs永远会保证a,b都加载完成再一起执行,如果业务需求必须要b在加载完成之后就执行,那就只能把 require('b').run()
拆到另一个文件中了。
其实requirejs的异步模块加载方式是目前看来最符合前端设计要求的,很多人认为它不好的原因基本是因为不清楚它的工作方式,或者无法理解异步编程的逻辑罢了。
相关文档
暂无
随便看看