How loading JavaScript can slow websites down (even if it’s asynchronous)

Where would websites be without JavaScript? The preeminent programming language for the web has helped turn the Internet from a sea of static documentation into a rich, interactive experience.
But have we come to rely on it too much?
Blocking scripts
It’s not uncommon for a modern web page to contain references to a dozen or more JavaScript files, all adding features and functionality designed to enhance the user experience.
However, some years ago we started to realize that scripts could also be a menace. Browsers would wait for external JavaScript to load and execute before continuing to build the web page. And if a script were delayed for some reason, visitors could be left staring at a blank page while they waited.
The simplest way around this was to put scripts at the bottom of the page. This meant that a slow-loading script wouldn’t block the rendering of content above it in the HTML. Another alternative was to use inline JavaScript to add a reference to an external JavaScript file. This allowed the external file to load without blocking the page’s construction.
Later, we got the async and defer attributes. Async means that the page can continue to be built while the script loads. Once the script has loaded, it is executed immediately (意味着页面上不同脚本的执行顺序不能被保证). Defer works in much the same way, except that execution waits for the DOMContentLoaded event, and any dependencies based on how the scripts are ordered in the document are preserved.
This hasn’t eliminated the issue of blocking scripts. If JavaScript is used for feature detection, for example, or to add content dynamically, it may still have to be loaded synchronously, at the top of the page.
But what about all our asynchronous and deferred scripts? Surely, they can’t detract from the user experience?
为什么异步脚本仍然会阻塞?
When someone visits a web page, they’re usually interested in the content. Although JavaScript gives us the opportunity to deliver a whole range of enhancements, people are still often primarily interested in consuming information.
Unfortunately, browsers don’t know this. 当涉及到对象的加载顺序时,浏览器倾向于给脚本和图片同样较高的优先级 (scripts might, after all, be required for the page to work).
But what if you have a large number of scripts and images? The scripts might be important because they add a number of enhancements to the page. But maybe the images matter more to you – and, more importantly, to the people who come to your website.
Despite this, the browser may stubbornly stick to loading the scripts early, even if you’ve put them at the bottom of the page, after all your images.
The result can be that images are effectively delayed by scripts. We’re back to blocking JavaScript.
Here’s an example. This test page contained a lot of images, referenced towards the top of the document. It also contained a lot of scripts, referenced at the bottom. We tested it in Chrome, using WebPagetest.
Half the images loaded ahead of the scripts. However, the other half, which included some above-the-fold pictures, loaded afterwards.


It’s also helpful to look at the connection view waterfall, which shows script files tying up connections ahead of images:

Is there anything we can do about this?
Domain sharding
One partial solution is to avoid loading large numbers of scripts and images from the same domain. Delivering them from separate domains will at least mean that images won’t have to ‘queue’ behind scripts on the same connection.
However, even if you move all your scripts to a different domain, there’s still a limit to the total number of connections a browser will open. And there’s no guarantee that the next set of connections will be given over to your images. Instead, they could easily be occupied by third-party scripts, for example.
Adding scripts using inline JavaScript
Of course, we don’t have to include script references directly in the HTML. We can use some inline JavaScript to add <script> elements to the page. If we do this after we’ve referenced our images, those images should start loading before the scripts.
The disadvantage of this is that scripts loaded in this way can’t be discovered by the browser preloader. So, yes, they’ll be loaded after the images, but they might just be loaded too late for our liking. The same applies to another possible alternative – loading scripts on the onload event.
预加载图像
What we really need to do is change the priority the browser gives to images, so that it leapfrogs the priority level given to scripts.
Fortunately, we now have a way to do this, using preloading. Preloading lets us tell the browser to load certain resources that are going to be needed on the page. It is particularly useful for the early loading of objects that tend to be discovered late, such as fonts and background images referenced in CSS.
However, it also allows us to alter the priority given to those objects.
Preload is used within a <link> element, as follows:
<link rel="preload" href="images/mypic.jpg" as="image">
The as attribute indicates the kind of object the browser should expect. However, it’s not very helpful for our purposes. This is because telling the browser to expect an image will lead to it having the same low priority as it did before.
Instead, we can simply omit the as attribute:
<link rel="preload" href="images/mypic.jpg">
This doesn’t give the image especially high priority, but it does elevate it above the default priority for scripts.
Here are the waterfall charts for our test page when we tried this:

And the connection view:

There are two points to bear in mind about using preload in this way.
The first is that support, at the time of writing, is limited to Chrome, Opera and Android Browser (source:caniuse.com).
The second is that it’s quite a blunt instrument. We’re taking advantage of the fact that the default priority for preloaded objects is higher than it is for scripts.
Instead, it would be useful to have finer control over the priority given to each object on the page, rather than leaving it to the browser to guess at what’s most important. We started to see something like this with the now-abandoned Resource Priorities specification. What could be helpful is something like the pr attribute in the Resource Hints specification, which is used to indicate the likelihood that a given resource will be used.
HTTP/2
With HTTP/2 some of these problems go away, since requests and responses are multiplexed in streams over a single connection. However, the concept of resource priorities remains, with bandwidth apportioned accordingly, and taking various dependencies into account.
Another important concept in HTTP/2 is server push. This involves a server sending a resource to the client before the client has asked for it, making it especially useful for delivering the CSS required to start rendering a page.
Preloading and server push can be used together in HTTP/2 by adding a response header to the root object:
Link: </ images/mypic.jpg>; rel=preload;
Add nopush if you want to use preloading but not server push:
Link: </ images/mypic.jpg>; rel=preload; nopush
Server push is probably more relevant to critical CSS files than it is to imagery, but it’s worth mentioning here because it is possible to adjust the priority given to pushed resources – see Apache’s H2PushPriority directive.
The point to take away
When building any web page, it’s important to consider what visitors are most likely to want to see and do when they land on it. Non-essential scripts can get in the way of important content, and loading them asynchronously doesn’t guarantee that they won’t negatively impact the user experience.
Published date: 14 September 2016
Written by: Alex Painter

defer 属性(只有 Internet Explorer 支持 defer 属性)规定是否对脚本执行进行延迟,直到页面加载为止。

如果您的脚本不会改变文档的内容,可将 defer 属性加入到 <script> 标签中,以便加快处理文档的速度。因为浏览器知道它将能够安全地读取文档的剩余部分而不用执行脚本,它将推迟对脚本的解释,直到文档已经显示给用户为止。

语法:<script defer="defer">

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容