egg.js curl
In the world of JavaScript, John Hann is one B.A.M.F. His usual handle is unscriptable, but that should be the last thing he should be called. John has created and contributed to many incredible JavaScript tools -- simply check out his GitHub page. This blog uses John's curljs, an incredibly efficient, flexible JavaScript loader. I wanted to interview John about creating a loader: the pitfalls, the browser differences, and what's in store for the future.
在JavaScript的世界里,约翰·汉恩是一个BAMF他一贯的手柄unscriptable ,但应该是他应该叫过去的事情。 John创建并贡献了许多令人难以置信JavaScript工具-只需查看他的GitHub页面即可 。 该博客使用John的curljs,这是一种高效,灵活JavaScript加载器。 我想就创建装载程序采访John:陷阱,浏览器差异以及将来的存储空间。
你好约翰! 对于那些不认识您的人,请给我们快速介绍一下,然后让我们知道您在做什么。 (Hello John! For those who don't know you, give us a quick introduction and let us know what you're working on.)
Hi, I'm John. John Hann. "@unscriptable" on most of the interwebs. I've been writing Javascript since 1995. Like many, I wasn't enthused, at first. In 2005, I was comfortable enough with it to start appreciating it's good parts and started coding in it exclusively.
嗨,我是约翰 约翰·汉恩 大多数互连网上都显示“ @unscriptable”。 自1995年以来,我一直在编写Javascript。像许多人一样,起初我并不热衷。 在2005年,我对它足够满意,开始欣赏它的精髓,并开始专门对其进行编码。
Ha! There's a good story around that. I'll make it quick. At that time, I was running a boutique software development company. We were 12 employees at the peak in 2001, but had dwindled to 5 by 2005. Internet bubble: you know the story. Anyway, I announced to my staff that Javascript was the way of the future.
哈! 周围有个好故事。 我会尽快。 当时,我正在经营一家精品软件开发公司。 我们在2001年达到顶峰时只有12名员工,但到2005年已减少到5名。互联网泡沫:您知道这个故事。 无论如何,我向我的员工宣布了Java语言是未来的方式。
Hmmm. let me back up for a sec. I should mention that I often prognosticated about software development trends and was usually right. For instance, the day I heard about C#, I predicted that it would eclipse all other Microsoft languages and told all my employees that they needed to learn it *now*. They all complied and we were in high demand for a long time.
嗯 让我备份一秒钟。 我应该提到的是,我经常对软件开发趋势进行预测,并且通常是正确的。 例如,当我听说C#的那天,我预言它将使所有其他Microsoft语言黯然失色,并告诉所有员工*他们现在需要学习它。 他们都遵守了,很长一段时间以来我们的需求量很大。
However, when I predicted that Javascript was the next big thing, they all -- every last one of them -- shrugged and disagreed. I sold off the company and never looked back.
但是,当我预测Java语言是下一件大事时,它们所有人-他们中的每一个都耸了耸肩,不同意。 我卖掉了公司,再也没有回头。
Anyways, by 2008, I had written three decent Javascript frameworks from scratch for various private projects and was irritated that the most of the industry was still doing things that I considered archaic. Finally, in 2010, I decided to go open source. That's when cujo.js was conceived.
无论如何,到2008年,我从头开始为各种私人项目编写了三个不错的Javascript框架,这使整个行业的大多数人仍在做我认为过时的事情而感到恼火。 最终,在2010年,我决定使用开源软件。 那时是cujo.js的构思。
I started out by structuring cujo.js as an application framework on top of dojo. That seemed like the best way to get started: stand on the shoulders of giants. At the same time, I felt like I wasn't targeting the right community. After all, it was the jQuery-centric folks that needed the most guidance.
我首先将cujo.js构建为dojo之上的应用程序框架。 这似乎是最好的入门方法:站在巨人的肩膀上。 同时,我觉得我没有针对正确的社区。 毕竟,需要大多数指导的是以jQuery为中心的人。
By happenstance, I found out one of the colleagues I admired the most was also toying with similar ideas. It was from discussions with Brian Cavalier later in 2010 that we discovered that we didn't want to create yet another framework at all. We wanted to build an "architectural framework" -- a collection of architectural tools that can work together or as individual libraries. More importantly, these tools must work with other popular libraries as well.
偶然地,我发现我最敬佩的一位同事也在玩类似的想法。 正是从2010年下半年与Brian Cavalier的讨论中,我们发现我们根本不想创建另一个框架。 我们想建立一个“架构框架”-一组可以一起使用或作为单个库使用的架构工具。 更重要的是,这些工具也必须与其他流行的库一起使用。
cujo.js, as we know it today, came to life in 2011. That's what I do now. I work with Brian and some other part-timers to make cujo.js more awesome every day. Increasingly, that's what I do at my day job at SpringSource, too. They're the best company I've worked with so far.
我们今天所知道的cujo.js于2011年投入使用。这就是我现在要做的。 我与Brian和其他兼职人员合作,使cujo.js每天都变得更加出色。 越来越多地,这也是我在SpringSource的日常工作中所做的事情。 他们是迄今为止我合作过的最好的公司。
On weekends, I like to build things with my kids and post pictures of the results on Flickr.
在周末,我喜欢和孩子们一起做东西,并在Flickr上发布结果的图片。
您是AMD格式的知名支持者。 是什么导致了您对AMD的热爱? 为什么AMD是编写JavaScript的最佳格式? (You're a well known supporter of the AMD format. What led to your love of AMD? Why is AMD the best format for writing JavaScript?)
I came to love AMD when I realized it was the first Javascript module format that wasn't tied to a specific library or company. Open source FTW!
当我意识到这是第一个与特定库或公司无关的Javascript模块格式时,我就爱上了AMD。 开源FTW!
Seriously. I had grown fond of dojo.require(), but was really wishing my code wasn't entangled with dojo. dojo was -- and still is -- one of the most awesome Javascript frameworks, but it just didn't feel right that my code was inextricably tied to it. AMD was the first module format -- and the only module format at that time -- that didn't entangle my code with a framework.
说真的 我已经很喜欢dojo.require(),但确实希望我的代码不会与dojo纠缠在一起。 dojo曾经是-至今仍然是-最令人敬畏的Javascript框架之一,但是我的代码与它紧密地联系在一起是不对的。 AMD是第一个模块格式(当时也是唯一的模块格式),它没有使我的代码与框架纠缠在一起。
I'm going to go out on a tangent here, but I think it's important to mention: Javascript is a community of fanboys. There aren't many standards, no universal best practices, no de facto high-level frameworks like Java or C#. We've got no other choice but to rally around our favorite library or framework.
我将在这里进行切线讨论,但我认为必须提一下:Javascript是一个迷信者社区。 没有太多的标准,没有通用的最佳实践,也没有像Java或C#这样的事实上的高级框架。 我们别无选择,只能团结我们最喜欢的库或框架。
Furthermore, we're not overly educated. Many of us don't have computer science degrees. We don't even have engineering backgrounds. We're just hacks who love what we do. So when something powerful, yet simple comes along and suddenly blows our minds, WE JUST LOVE IT TO DEATH.
此外,我们没有受过良好的教育。 我们中许多人没有计算机科学学位。 我们甚至没有工程背景。 我们只是热爱我们工作的黑客。 因此,当强大而简单的事物出现并突然使我们震惊时,我们就爱死了。
AMD did that for me. The idea that I could write modular code that was totally independent of any framework made me an instant fanboy.
AMD为我做到了。 我可以编写完全独立于任何框架的模块化代码的想法使我立即成为狂热分子。
So, why is AMD the best format for Javascript? Hmm... I guess it comes down to this: it's the simplest format I've seen that doesn't require a build step. It was designed for browsers. You can get started with AMD by just downloading an AMD loader and writing some code. Hit F5 or Cmd-R and see your first module load.
那么,为什么AMD是Javascript的最佳格式? 嗯...我想这可以归结为:这是我所见过的最简单的格式,不需要构建步骤。 它是为浏览器设计的。 您可以通过下载AMD加载程序并编写一些代码来开始使用AMD。 点击F5或Cmd-R并查看您的第一个模块加载。
创建curl.js时,还有其他可用的加载程序。 创建curl.js的灵感是什么? (When you created curl.js, there were other loaders available. What was your inspiration behind creating curl.js?)
Heh. Ok, so I'm a little bit competitive. Not overtly, but definitely competitive. When I first tried RequireJS, I thought it was really cool, but why was it so big???? At the time, RequireJS was pretty unstable, too. I'd rather rely on my own buggy code than somebody else's since I understand how to fix my own code.
嘿。 好吧,所以我有点竞争力。 并不公开,但绝对具有竞争力。 当我第一次尝试RequireJS时,我认为它真的很酷,但是为什么这么大? 当时,RequireJS也相当不稳定。 我宁愿依赖我自己的错误代码,也不愿依赖别人的代码,因为我了解如何修复自己的代码。
I thought I could create an AMD loader that was smaller and faster. Turns out I was right.
我以为我可以创建更小,更快的AMD加载器。 原来我是对的。
To be fair, RequireJS has some legacy code in it. That adds some bloat. I was able to start from scratch. The first version of curl.js was about 3.5 KB gzipped when RequireJS was about 6 KB. Of course, RequireJS had way, way more features.
公平地说,RequireJS中包含一些旧代码。 这增加了一些膨胀。 我能够从头开始。 当RequireJS约为6 KB时,curl.js的第一个版本压缩了约3.5 KB。 当然,RequireJS有更多功能。
But the tiny size of curl.js motivated me. I obsessed on it. I vowed to never let it grow larger. Today, it's still around 3.5 KB and has a similar feature set as RequireJS.
但是curl.js的微小尺寸激励了我。 我迷上了它。 我发誓永远不要让它变大。 今天,它仍然约为3.5 KB,并具有与RequireJS类似的功能集。
Again, to be fair, RequireJS seems very stable now and has an amazing test suite.
再次,公平地说,RequireJS现在看起来非常稳定,并且具有出色的测试套件。
It also occurred to me that there needed to be multiple implementations of a standard in order to really be considered a standard. I felt that AMD needed to be larger than just RequireJS in order to be taken seriously.
在我看来,要真正被视为一个标准,就需要有多个标准的实现。 我认为,AMD不仅需要比RequireJS大,而且要被重视。
在curl.js上开始开发时遇到了什么挑战? 您没有预料到什么问题,如何解决它们? (What challenges did you encounter when starting development on curl.js? What problems did you not anticipate and how did you solve them?)
CommonJS. I totally didn't know what the heck I was doing and didn't know *anything* about CommonJS modules -- or packages -- until more recently. Also: configuration. I still can't believe how many bytes of curl.js are used up trying to handle all the ways that users can configure curl.js. Now I know why people write unfriendly, unconfigurable APIs!
CommonJS。 我完全不知道自己在做什么,直到最近才对CommonJS模块或软件包一无所知。 另:配置。 我仍然不敢相信curl.js会用完多少字节,以尝试处理用户可以配置curl.js的所有方式。 现在,我知道了为什么人们编写不友好,无法配置的API!
Oh. I guess what you're probably inquiring about is what browser-ish roadblocks I encountered? Whew! Lots. Browsers really didn't standardize script loading behavior until this year.
哦。 我想您可能要问的是我遇到了哪些类似于浏览器的障碍? ew! 很多。 直到今年,浏览器才真正实现脚本加载行为的标准化。
Luckily, browsers pretty much fall into two camps: IE and everything else. Wait, but then there's Opera which is somewhere in between, but not really even.
幸运的是,浏览器几乎可以分为两个阵营:IE和其他所有东西。 等等,但是歌剧介于两者之间,但实际上并非均匀。
The trick to script loading is knowing the precise moment when a script has executed. In IE, you can detect the currently executing script by looping through all of the recently created script elements and sniffing for which of them has a readyState of "interactive". Of course, Opera misleads us and says some seemingly random script is "interactive", so we have to account for that.
脚本加载的技巧是知道脚本执行的确切时间。 在IE中,您可以通过遍历所有最近创建的脚本元素并嗅探它们的readyState为“ interactive”来检测当前正在执行的脚本。 当然,Opera误导了我们,并说某些看似随机的脚本是“交互式的”,因此我们必须考虑到这一点。
The standards-compliant browsers work a bit differently. They queue up the executing scripts and fire each script's onload event immediately *after* it executes. This requires an entirely different algorithm than IE, of course.
符合标准的浏览器的工作方式略有不同。 他们将正在执行的脚本排入队列,并在执行之后*立即*触发每个脚本的onload事件。 当然,这需要与IE完全不同的算法。
Error handling is a different matter. IE and Opera still don't fire an onerror event if a script element 404's. Luckily, we can detect if an AMD `define()` hasn't been called and throw a meaningful error anyways.
错误处理是另一回事。 如果脚本元素是404,IE和Opera仍然不会触发onerror事件。 幸运的是,我们可以检测是否未调用AMD`define()`并抛出有意义的错误。
CSS loading is a serious can of worms. curl.js treats CSS just like it does Javascript. You can load (and wait) for it just like Javascript. The problem is that even browsers like Chrome and Firefox haven't had adequate support for onload and onerror handlers on link elements until very recently. The CSS-handling code is just atrocious. But it works.
CSS加载是严重的蠕虫病毒。 curl.js就像对待Javascript一样对待CSS。 您可以像Javascript一样加载(并等待)它。 问题在于,直到最近,像Chrome和Firefox这样的浏览器都没有对链接元素上的onload和onerror处理程序提供足够的支持。 CSS处理代码是残酷的。 但这有效。
创建JavaScript加载器的哪一部分比人们想象的容易? (What part of creating a JavaScript loader is easier than people may think?)
It's not easy. None of it. As soon as you create anything that handles real-world code, that thing becomes complex. Every web developer on this planet wants to do things *their way*. Nobody ever does things quite like the next person.
这并不容易。 都没有 一旦创建处理现实世界代码的任何东西,那事情就会变得复杂。 这个星球上的每个Web开发人员都想*以自己的方式做事。 没有人会像下一个人那样做事情。
Hmm.... there must be something in curl that's not overly complex. Thinking... thinking... Nope. Forget it. There's not a single line of code that hasn't cost me hours of testing, nail biting, or frustration. Seriously.
嗯....一定有一些不太复杂的东西。 思考...思考...不。 算了吧。 没有哪一行代码不会花费我数小时的测试,咬指甲或感到沮丧。 说真的
创建加载程序时,现代浏览器有多大的因素? 哪种浏览器最简单,哪种浏览器最难安装? (How big of a factor is the modern browser when creating a loader? Which browser was easiest, and which was the most difficult to accommodate?)
Modern browsers are much better, of course. Firefox has been the easiest, by far. Chrome and Safari are next. IE and Opera still don't support basic error handling. In fact, they still falsely declare success if a script 404's. Brilliant.
当然,现代浏览器要好得多。 到目前为止,Firefox是最简单的。 接下来是Chrome和Safari。 IE和Opera仍然不支持基本的错误处理。 实际上,如果脚本404仍然成功,它们仍然错误地声明成功。 辉煌。
Firefox always seemed to be diligent about script loading, even before Kyle Simpson -- the godfather of script loading -- joined Mozilla. Oh... link loading, too. They were the first to implement functional onload and onerror handlers for script *and* link elements. They were the first to support the async attribute on script elements, too. They also seemed to know that the sequencing of script evaluation and onload events needed to be predictable long before other browsers, if I recall correctly.
即使在脚本加载的教父Kyle Simpson加入Mozilla之前,Firefox似乎始终对脚本加载保持勤奋。 哦,链接加载也是如此。 他们是第一个为脚本*和*链接元素实现功能性onload和onerror处理程序的人。 他们也是第一个在脚本元素上支持async属性的人。 他们似乎也知道,如果我没有记错的话,脚本评估和加载事件的顺序需要比其他浏览器早很多可预测。
curl.js even works in Netscape 7 because of that. Hm... I haven't tested in Netscape 7 lately. YMMV.
因此,curl.js甚至可以在Netscape 7中使用。 嗯...我最近没有在Netscape 7中测试过。 YMMV。
性能是任何软件组件的重要组成部分。 您采取了哪些步骤来使curl.js高效紧凑? (Performance is an important part of any software component. What steps have you taken to make curl.js efficient and compact?)
As I mentioned earlier, I've obsessed about code size since day one. That said, I think curl.js is in need of a diet. As soon as the next big features are released, I'll give it a look-over to see what I can trim.
如前所述,自第一天起,我就一直痴迷于代码大小。 就是说,我认为curl.js需要节食。 下一个主要功能发布后,我将对其进行一次查看,以了解可以进行哪些调整。
Size isn't the only concern. I'm also obsessed with http performance. Maybe not so obsessed as John-David Dalton (he's nuts), but obsessed enough to not accept compromise.
尺寸不是唯一的问题。 我也迷恋http性能。 也许不像约翰·戴维·道尔顿(John-David Dalton)(他的疯子)那么痴迷,但痴迷于不接受妥协。
One of the differences between curl.js and other loaders, say RequireJS, is that curl.js resolves it's dependencies synchronously. In production, if you've properly concatenated your modules, sync resolution doesn't make a huge difference. However, during development -- when concatenation is burdensome and totally unnecessary -- the average 12ms delay caused by async resolution can make a huge difference. We were once working on a project that had 300+ modules. That's 300 http requests! We were waiting forever -- like over 30 seconds -- for the app to load in IE6. It was actually faster to run a build script to concatenate the modules and then load the single file into IE.
curl.js和其他加载程序(例如RequireJS)之间的区别之一是curl.js可以同步解决其依赖项。 在生产中,如果您已正确串联模块,那么同步分辨率不会有太大的不同。 但是,在开发过程中(当连接是繁重且完全不必要的时候),异步解析所导致的平均12ms延迟会产生巨大的变化。 我们曾经在一个拥有300多个模块的项目中工作。 那是300个http请求! 我们一直在等待-超过30秒-等待该应用程序加载到IE6中。 运行构建脚本来连接模块,然后将单个文件加载到IE中,实际上速度更快。
Ahhhh! I just remembered. That was another one of the reasons I wrote curl.js. RequireJS was timing out and giving up. Even when we set the timeout to 60 seconds, it still would puke. I was sure we could write a loader that didn't waste 12ms per module just sitting around. Little did I know that async module resolution was way easier than sync module resolution.
啊! 我刚想起来。 那是我写curl.js的另一个原因。 RequireJS超时并放弃了。 即使我们将超时设置为60秒,它仍然会呕吐。 我确信我们可以编写一个不会浪费每个模块12ms的装载程序。 我几乎不知道异步模块解析比同步模块解析容易得多。
Timeouts are problematic, anyways. It's impossible to set a timeout that works across all browsers and for every connection speed. curl.js doesn't use one. curl.js doesn't need one.
无论如何,超时是有问题的。 不可能设置一个适用于所有浏览器和每种连接速度的超时。 curl.js不使用一个。 curl.js不需要一个。
Also: slow IE6 is slow no matter what you throw at it. We cut the un-concatenated load time in half with curl.js, but it was still 6 times slower than Firefox and Chrome.
另外:慢速IE6不管您扔什么都慢。 我们使用curl.js将未连接的加载时间缩短了一半,但仍然比Firefox和Chrome慢6倍。
为curl.js实现promise API有多困难? (How difficult was implementing the promise API for curl.js?)
Well. Once I implemented promise-like behavior inside curl, it wasn't hard to implement it in the API. To be fair, curl.js doesn't implement the full CommonJS Promises/A standard. It's just promise-like. We've got another library, when.js, that is fully compliant and blazingly fast, too.
好。 一旦我在curl中实现了类似于承诺的行为,就很难在API中实现它。 公平地说,curl.js没有实现完整的CommonJS Promises / A标准。 就像诺言一样。 我们还有另一个库when.js,它完全兼容并且运行速度非常快。
具有设置别名,程序包和外部模块URL的能力,在创建加载程序时解析路径有多困难? (With the ability to set aliases, packages, and external module URLs, how difficult is path resolving when creating a loader?)
Wow. Loaded question. Where to start. I've been meaning to write more documentation about this. I guess I'll first mention that AMD loader authors have concluded that it's important to think about two different stages in url resolution. First, you have to normalize the module's id. Then, you can resolve a url.
哇。 加载的问题。 从哪儿开始。 我一直想写更多有关此的文档。 我想我首先要提到AMD加载器作者得出的结论是,考虑URL解析的两个不同阶段非常重要。 首先,您必须规范化模块的ID。 然后,您可以解析一个URL。
Id resolution requires a few steps. First, you have to reduce leading dots. For instance, if you're requiring a module that's two folders up from the current (parent) module, you've got two levels of double-dots to fold into the parent module's id. At this point, you've hopefully got no more leading dots. If you do have leading dots, then the module id is really a url path, and that's problematic, but I'll just skip over that for now.
ID解析需要几个步骤。 首先,您必须减少前导点。 例如,如果您需要一个比当前(父)模块高两个文件夹的模块,则可以将两个双点水平折叠到父模块的ID中。 在这一点上,您希望不再有领先点。 如果您确实有前导点,那么模块ID确实是一个url路径,这是有问题的,但是我现在暂时跳过它。
Once you've removed all of the leading dots, you can perform id transforms. curl.js currently has two module id transforms: 1) a plugin id transform and 2) a package "main" module transform. Both of these types of ids have shortcut notation. curl checks to see if the module you're requesting is a shortcut for a plugin or a main module and expands them into their long forms.
删除所有前导点后,即可执行id转换。 curl.js当前有两个模块ID转换:1)插件ID转换和2)包“ main”模块转换。 这两种类型的ID都有捷径符号。 curl检查您请求的模块是插件还是主模块的快捷方式,并将其扩展为长格式。
Ok, so once you have a normalized id, you can look up the url path. curl.js uses a very fast, regex-driven algorithm that allows the dev to create increasingly specific url transforms. Basically, curl sorts the url transforms by the number of slashes in it. The more slashes, the higher priority. curl.js uses this algorithm to search through the paths configuration to determine where you've put the module. Finally, curl appends the path to the base url and uses that to fetch the module.
好的,一旦有了标准化的ID,就可以查找url路径。 curl.js使用一种非常快速的,由正则表达式驱动的算法,该算法允许开发人员创建越来越具体的url转换。 基本上,curl按其中的斜杠数量对url转换进行排序。 斜杠越多,优先级越高。 curl.js使用此算法搜索路径配置,以确定将模块放置在何处。 最后,curl将路径附加到基本URL并使用该路径来获取模块。
curl.js与许多插件捆绑在一起,允许基本的XHR请求,CSS文件加载,domReady回调执行等。 本质上,您可以例如在模块依赖项数组中加载完整的UI小部件。 集成插件有多难?您打算将来增加其他插件吗? (curl.js comes bundled with many plugins, allowing basic XHR requesting, CSS file loading, domReady callback execution, and more. Essentially you can load a complete UI widget, for example, within your module dependency array. How difficult was it to integrate the plugins, and do you have additional plugins you plan to include in the future?)
James Burke designed a very simple plugin API consisting of one function. With a little help from Rawld Gill of dojo fame and myself, we finalized a complete, yet still simple, run-time plugin API that consists of only two functions and a property. James and Rawld have extended that API a bit to suit certain requirements. However, I've been able to do everything with the original API.
James Burke设计了一个非常简单的由一个函数组成的插件API。 在dojo fame和我本人的Rawld Gill的帮助下,我们完成了一个完整但仍简单的运行时插件API,该API仅包含两个函数和一个属性。 James和Rawld对该API进行了扩展,以适应某些要求。 但是,我已经能够使用原始API进行所有操作。
The major use cases for plugins are loading HTML templates with the text plugin and loading localization files with the i18n plugin. curl.js has two flavors of CSS plugin, too. Other people have created Coffeescript plugins, CommonJS plugins, and plugins for other compile-to-Javascript languages.
插件的主要用例是使用文本插件加载HTML模板和使用i18n插件加载本地化文件。 curl.js也有两种CSS插件。 其他人已经创建了Coffeescript插件,CommonJS插件以及用于其他Java编译语言的插件。
Our favorite pattern is -- like you said -- to create an entire UI component in a module. Javascript, CSS, HTML, localization files, etc. all in one folder. Lots of folks offer widgets, but the way you handle the Javascript and the CSS is so disjointed. When you can co-locate the Javascript and CSS together, you've got a truly portable widget. curl.js does that so well.
就像您所说的,我们最喜欢的模式是在模块中创建整个UI组件。 Javascript,CSS,HTML,本地化文件等都在一个文件夹中。 许多人都提供小部件,但是处理Javascript和CSS的方式却是如此。 当您可以将Javascript和CSS并置在一起时,便有了一个真正可移植的小部件。 curl.js做得很好。
We've already got a good set of plugins. I think where we'll concentrate going forward is on transpilers. As of curl 0.8, we'll have full support for transpilers using the same old plugin API that regular plugins use. We call this concept "Compile to AMD" and it's pretty powerful. You just find or write a plugin that transpiles your preferred language -- Coffeescript, Haskell, Sybilant, TypeScript, whatever -- and tell curl.js that you want to use it to convert a set of modules to AMD. Other modules in your project don't need to know what language any others were written in. They're all converted to AMD either at run time or at build time, but you probably don't want to convert them at build time for production code.
我们已经有了一套不错的插件。 我认为我们将重点放在转译器上。 从curl 0.8开始,我们将使用与常规插件相同的旧插件API完全支持编译器。 我们将此概念称为“编译为AMD”,它的功能非常强大。 您只需找到或编写一个可以转换您喜欢的语言的插件(Coffeescript,Haskell,Sybilant,TypeScript等),然后告诉curl.js您想使用它来将一组模块转换为AMD。 项目中的其他模块不需要知道其他任何语言。在运行时或构建时它们都已转换为AMD,但是您可能不想在构建时将它们转换为生产码。
This sure feels like the future!
这肯定感觉像未来!
从代码和逻辑的角度来看,考虑在同一模块中同时加载异步文件和同步文件会带来哪些挑战? (What challenges are presented, from a code and logic standpoint, when accounting for loading both async and sync files within the same module?)
Well, curl doesn't load files sync. I should say that *AMD* doesn't load files sync. You can write code that assumes a file will be loaded sync, but the AMD loader will detect that and pre-load the file asynchronously.
好吧,curl不会加载文件同步。 我应该说* AMD *不会加载文件同步。 您可以编写假定文件将同步加载的代码,但是AMD加载器将检测到该文件并异步预加载文件。
Since AMD was written for browsers, the AMD format just let's you write your code as if the dependencies are available synchronously. If you want to write in the CommonJS Modules style, there's a specific way to wrap your modules for this to work. I think James Burke calls it "Simplified CommonJS-wrapped modules". Just google that and you'll find some decent docs on it.
由于AMD是为浏览器编写的,因此AMD格式仅让您编写代码,就好像这些依赖项是同步可用的一样。 如果要使用CommonJS Modules样式编写代码,则有一种特殊的方法可以包装模块以使其正常工作。 我认为James Burke称其为“简化的CommonJS包装的模块”。 只是谷歌,你会发现一些体面的文档。
curl.js actually has a way to load CommonJS modules without wrapping. It's an "experimental" feature that previews the "Compile to AMD" features coming in 0.8. It's awesome because you get the best of both worlds. I call it "experimental", but it works great today. It's just that the configuration settings will change.
curl.js实际上有一种无需包装即可加载CommonJS模块的方法。 这是一项“实验性”功能,可以预览0.8版中的“编译为AMD”功能。 很棒,因为您可以两全其美。 我称其为“实验性”,但今天效果很好。 只是配置设置会更改。
添加jQuery支持带来了哪些挑战? (What challenges did adding jQuery support present?)
Well, James did all the leg work by getting the jQuery folks to support AMD, but the way they implemented it required a loader that resolves modules asynchronously. curl.js resolves modules sync, as I mentioned earlier. The first jQuery release with AMD support, 1.7, didn't account for sync resolution. Version 1.7.2 did. All subsequent versions work great with curl.
好吧,James通过让jQuery人员支持AMD来完成所有工作,但是他们实现它的方式需要一个能够异步解析模块的加载器。 curl.js解决了模块同步问题,正如我之前提到的。 具有AMD支持的第一个jQuery版本1.7没有考虑同步分辨率。 版本1.7.2做到了。 所有后续版本均适用于curl。
jQuery does something else that requires special note, though. They *name* their module. jQuery's define statement has a hard-coded module id in it. This makes it possible to use non-AMD build tools in conjunction with an AMD loader. I don't think anybody in the real world is actually doing this, but, oh well, we can deal with it.
jQuery做了其他需要特别注意的事情。 他们*命名*他们的模块。 jQuery的define语句中包含一个硬编码的模块ID。 这样就可以将非AMD构建工具与AMD加载程序结合使用。 我认为现实世界中没有人真正在这样做,但是,哦,我们可以处理。
The only way to handle named modules is to specify a path config for the module. In short, you absolutely have to specify a path mapping for jQuery in your AMD config. This isn't a big deal in my opinion since I think the developer should specify a path mapping to every package or library in their app, anyways. It can just trip up newbs.
处理命名模块的唯一方法是为模块指定路径配置。 简而言之,您绝对必须在AMD配置中为jQuery指定路径映射。 我认为这没什么大不了的,因为我认为开发人员无论如何都应该指定一个路径映射到其应用程序中的每个包或库。 它可能会使新手绊倒。
您是否要共享curl.js中的任何小巧但有用的代码片段? (即是否存在某些人不知道的边缘特征检测摘要或“黑客”?) (Do you have any small but useful code snippets from the curl.js you'd like to share? (i.e. are there any edge feature detection snippets or "hacks" that some people wouldn't know?))
Oh gawd. The css plugin is chock full of hacks and edge cases. I think the best one is the method we're using to avoid the 31-stylesheet limit in IE6-9. This method also provides onerror support since IE's link elements don't normally call onerror when a url 404's. Here's how it works:
哎呀 css插件充满了骇客和边缘案例。 我认为最好的方法是我们用来避免IE6-9中的31个样式表限制的方法。 此方法还提供了onerror支持,因为IE的链接元素通常在url 404时不会调用onerror。 运作方式如下:
First, a "collector" sheet is created. This stylesheet will be used to collect the first 31 stylesheets. We add an onload and an onerror handler to the collector sheet and insert the first requested stylesheet as an @import. The collector sheet will fire either the onload or the onerror handler when the imported sheet loads or fails. For some reason at this point, the onerror handler becomes non-functional, so we have to replace it -- and the onload handler -- before we try to load the next stylesheet.
首先,创建一个“收集器”工作表。 该样式表将用于收集前31个样式表。 我们将一个onload和一个onerror处理程序添加到收集器工作表,并将第一个请求的样式表作为@import插入。 当导入的工作表加载或失败时,收集器工作表将触发onload或onerror处理程序。 由于此时的某种原因,onerror处理程序将无法运行,因此在尝试加载下一个样式表之前,我们必须替换它-和onload处理程序。
We keep replacing handlers and inserting @imports until we reach the 31-sheet limit. At 31 sheets, we create a new collector sheet and start counting to 31 all over again.
我们会不断替换处理程序并插入@imports,直到达到31页的限制。 在31张纸上,我们创建了一个新的收集器纸,然后从31开始重新计数。
The problem with this algorithm is that it can only load one sheet at a time. To get around this limitation, we create up to 12 simultaneous collector sheets. The css plugin uses a "round robin" technique so that up to 12 sheets may be loaded simultaneously. Since IE9's HTTP request limit is 12, this works out nicely.
该算法的问题在于它一次只能加载一张纸。 为了解决此限制,我们最多创建12个同时收集器页。 css插件使用“循环”技术,因此最多可同时加载12张纸。 由于IE9的HTTP请求限制为12,因此效果很好。
If you're well-versed in CSS semantics, red lights are flashing and sirens are ringing in your head right now. A round robin rotation algorithm like this would surely screw up the CSS cascade. You'd be right -- if you were thinking of the behavior of *normal browsers*. IE is not a normal browser. Unlike all other browsers, the IE team interpreted the cascade differently. They decided that the *temporal* order decides the cascade preference. All other browsers decide cascade preference by the *DOM* order. When you put static link elements in your html page, temporal order and DOM order are the same so you've probably never noticed the difference.
如果您精通CSS语义,红色指示灯会闪烁,并且警报器会立即响起。 这样的轮循旋转算法肯定会破坏CSS级联。 如果您想到的是*正常浏览器*的行为,那您将是对的。 IE不是正常的浏览器。 与所有其他浏览器不同,IE团队对级联的解释不同。 他们决定由*时间*顺序决定级联优先级。 所有其他浏览器均按* DOM *顺序确定级联首选项。 当您在html页面中放置静态链接元素时,时间顺序和DOM顺序是相同的,因此您可能从未注意到过这种差异。
In short, since we ensure that the CSS stylesheets are handled in their proper temporal sequence, it all works out. Legacy IE can load up to a total of 372 stylesheets using this algorithm, and it's pretty darn fast.
简而言之,由于我们确保按正确的时间顺序处理CSS样式表,因此一切都可以解决。 使用此算法,旧版IE最多可以加载372个样式表,而且速度非常快。
您预计在不久的将来会添加到curl.js中的哪些功能? (What features do you anticipate adding to curl.js in the near future?)
Well, I mentioned the "Compile to AMD" feature. That's going to be hot.
好吧,我提到了“编译为AMD”功能。 那会很热。
The other major feature is the "Portable AMD Bundle" feature. curl.js's sister project, cram.js, will be able to concatenate modules into larger files. This isn't anything earth shattering if you're already familiar with RequireJS's build tool, r.js. However, there are a few twists. First, CSS may also be bundled into the file. Second, there will be a sensible way to break the files into logical chunks that we call "bundles". Lastly, the files should be loadable by even the dumbest of AMD loaders since they'll be compiled down to the least common denominator.
另一个主要功能是“便携式AMD套件”功能。 curl.js的姊妹项目cram.js将能够将模块串联成更大的文件。 如果您已经熟悉RequireJS的构建工具r.js,那么这并不是什么破天荒的事情。 但是,有一些曲折。 首先,CSS也可以捆绑到文件中。 其次,将有一种明智的方式将文件分成逻辑块,我们称之为“捆绑”。 最后,即使是最笨拙的AMD加载程序也应该可以加载这些文件,因为它们将被编译为最小公分母。
You could take these bundles and host them on a CDN somewhere, publish them on github, or just use them within your own organization. It won't matter that you used some of curl.js's super cool features to create the bundle, it should work just about anywhere.
您可以使用这些捆绑包,并将其托管在某个地方的CDN上,将其发布在github上,或者仅在您自己的组织内使用它们。 没关系,您可以使用curl.js的一些超酷功能来创建捆绑包,它几乎可以在任何地方工作。
您可以提供一些技巧来简化AMD模块的调试吗? (Are there any tips you can provide for easier debugging with AMD modules?)
Good point. Debugging async *anything* is hard. curl's debug module is useful for logging each module as it gets processed. But it's almost as easy to just watch the console and the network activity. Here are some things to watch out for:
好点子。 调试异步“任何东西”都很困难。 curl的调试模块对于在处理每个模块时记录日志非常有用。 但是,只需观看控制台和网络活动几乎一样容易。 这里有一些注意事项:
If a module 404'ed, take a look at the url the browser used. Did you use too many double-dot parent path navigations? Does it look like curl didn't apply a path mapping? Try fetching the module in the console by typing `curl([ ], console.log.bind(console));` and see what happens.
如果是模块404,请查看使用的浏览器的网址。 您是否使用了太多的双点父路径导航? 看起来curl没有应用路径映射吗? 尝试在控制台中输入`curl([ ],console.log.bind(console));`看看会发生什么。
- If curl just fails silently and you're loading non-AMD javascript using the js plugin, try using the `exports=` feature of the js plugin. That feature provides explicit error feedback in all browsers. 如果curl只是静默失败,而您正在使用js插件加载非AMD javascript,请尝试使用js插件的`exports =`功能。 该功能可在所有浏览器中提供明确的错误反馈。
- Create a testing harness and limit the problem scope. Tracking dozens of async things is crazy hard. Keep cutting the problem scope until you've got a handle on what's happening. 创建测试工具并限制问题范围。 跟踪数十个异步事物非常困难。 不断缩小问题范围,直到掌握了所发生的一切。
Other gotchas:
其他陷阱:
Be careful not to try to use a global
require()
by accident. Unlike CommonJS environments, AMD environments don't automatically provide a context-sensitiverequire()
(aka a "local require"). A global require can't figure out how to find relative dependencies, which can lead to serious WTF moments. By default, curl.js fails early and loudly if you've referenced the global require by accident since it doesn't declare a global `require()` at all (unless you tell it to). Be sure to always request a local require in your modules and don't declare a global require unless you are certain your project is in the 0.00001% of use cases that actually need a global require.注意不要试图偶然使用全局的
require()
。 与CommonJS环境不同,AMD环境不会自动提供上下文相关的require()
(也称为“本地需求”)。 全局需求无法弄清楚如何找到相对依赖关系,这可能导致严重的WTF时刻。 默认情况下,如果您偶然引用了全局需求,curl.js会尽早失败,因为它根本没有声明全局`require()`(除非您告诉了它)。 确保始终在模块中请求本地需求,并且不要声明全局需求,除非您确定您的项目在实际需要全局需求的用例的0.00001%中。- Don't let urls creep into your module ids. As soon as you have urls in your module ids, your options for moving files becomes limited. The situation gets worse when you're concatenating your files into bundles. 不要让网址爬入您的模块ID。 在模块ID中包含网址后,用于移动文件的选项就会受到限制。 当您将文件串联在一起时,情况变得更糟。
There are two ways that urls creep into module ids. I mentioned the first one already. It happens when you try to navigate up too many levels.
网址可以通过两种方式进入模块ID。 我已经提到了第一个。 当您尝试向上导航太多级别时,就会发生这种情况。
define(["../../util/foo"], function (foo) { /* create something epic */ });
In general, using double dots in your application code is a code smell. The only time you should ever use double dots is to reference a related module within the same package. Highly modular third-party packages like dojo, wire.js, poly.js, etc. use double dots a lot. If you find you're using them in your web app, you should consider breaking your app into packages. You don't need to make them legitimate packages with a package.json; you just need to configure the loader to recognize that there's a logical organization of modules.
通常,在应用程序代码中使用双点是代码的味道。 您唯一应该使用双点的方法是引用同一程序包中的相关模块。 高度模块化的第三方程序包(如dojo,wire.js,poly.js等)经常使用双点。 如果发现自己在Web应用程序中使用它们,则应考虑将应用程序打包。 您无需使用package.json使其成为合法的软件包; 您只需要配置加载程序以识别模块的逻辑组织即可。
Actually, I think urls in general are problematic. Module ids are more flexible and are more in line with CommonJS and node.js patterns. I guess the take-away should be that you should use your AMD loader's path mapping and package mapping features. If your module ids look any more sophisticated than "myFoo" or "myPackage/foo" -- in other words, if they have a lot of slashes or double-dots -- you're probably playing with a footgun.
实际上,我认为网址通常是有问题的。 模块ID更灵活,并且更符合CommonJS和node.js模式。 我想得出的结论应该是您应该使用AMD加载程序的路径映射和包映射功能。 如果您的模块ID看起来比“ myFoo”或“ myPackage / foo”更复杂-换句话说,如果它们有很多斜线或双点-您可能正在使用脚枪。
egg.js curl