Script tag

When we want to insert a script into a web page, the standard way is to use the script tag(i.e. <script>). The first impression that comes to people's mind about script tag is - BLOCKING .
The book High Performance Web Sites Rule 6 suggests to put scripts at the bottom of html body. The article will examine how putting scripts at varying positions affects performance and how async and defer attributes work for script tag.

First thing first, when a script is referenced in a html page, the browser does two things for you:

  • Retrieve/Load the script content, this is NON-BLOCKING!
  • Run the script content, this is BLOCKING !

Assume we have two scripts on the page:

//script1.js
let t1 = +new Date;
console.log('script1 is running at', t1); console.log('script1 element', document.getElementById('load-experiment'));
while((+new Date) - t1 < 1000) {
// delay 1 seconds
}
console.log('script1 finishes running at', +new Date);
//script2.js
let t2 = +new Date;
console.log('script2 is running at', t2); console.log('script2 element', document.getElementById('load-experiment'));
while((+new Date) - t2 < 2000) {
// delay 2 seconds
}
console.log('script2 finishes running at', +new Date);

Put script tags in head

<!--all-in-head.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
<script src='./script1.js'></script>
<script src='./script2.js'></script>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
</body>
</html>

The console output:

script1 is running at 1496747869008
script1.js:4 script1 element null
script1.js:8 script1 finishes running at 1496747870008
script2.js:2 script2 is running at 1496747870009
script2.js:4 script2 element null
script2.js:8 script2 finishes running at 1496747872009

Conclusion:

  • When we open the html in the browser, we can notice the delay of page load. The page goes blank before it renders correctly. This is due to the fact that the running of the two scripts blocks the DOM rendering.
  • When scripts are running, they are not able to fetch the DOM element (i.e. document.getElementById('load-experiment') being null). This is because scripts are run before DOM is rendered.
  • Scripts themselves are blocking each other. They are run in the order specified in the html. Script2 is run after script1 finishes running.

Put script tags at the bottom of body

This is the suggestion from the Rule 6 of the book High Performance Web Sites.

<!--all-in-body.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script src='./script1.js'></script>
<script src='./script2.js'></script>
</body>
</html>

The console output:

script1 is running at 1496751597679
script1.js:4 script1 element <h1 id=​"load-experiment">​ hello world ​</h1>​
script1.js:8 script1 finishes running at 1496751598679
script2.js:2 script2 is running at 1496751598680
script2.js:4 script2 element <h1 id=​"load-experiment">​ hello world ​</h1>​
script2.js:8 script2 finishes running at 1496751600680

Conclusion:

  • No page delay and blank page when opening the page in browser, as scripts are put/run after the DOM is ready.
  • Scripts can correctly fetch DOM elements.
  • Scripts themselves are blocking each other, as the same as the first example.

However, puting all scripts at the bottom of body sometimes doesn't fit in some specific real life cases. For example, if we aim to calculate the ATF[https://en.wikipedia.org/wiki...] of a page, we can't simple wait for the DOM to render. We have to load and run some scripts beforehand and then fire the ATF marker when the ATF is ready, which is definitely before DOM is ready for web pages with scrolling content. Therefore, it seems reasonable to come out with the next solution.

Put scripts seperately in head and body based on requirements.

Put scripts that needs pre-running at head and others at the bottom of body. E.g.

<html>
<head>
<script src="headScripts.js"></scripts>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script src="bodyScripts.js"></script>
</body>
</html>

defer!

The main disadvantage of putting scripts at the bottom of body is that the scripts will only be retrieved/loaded after the DOM is rendered. As we said, retrieving/loading the script content is NON-BLOCKING while running the script content is the BLOCKING part. We can improve the web performance if we can retrieve/load the scripts while the DOM is rendering, rather than wait for the DOM to complete rendering. This works pretty well especially when the scripts are large. This is why defer is introduced. defer loads the scripts simultaneously but only runs the scripts after the DOM is ready.

<!--defer.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
<script defer src='./script1.js'></script>
<script defer src='./script2.js'></script>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
</body>
</html>

The console output:

script1 is running at 1496760312686
script1.js:4 script1 element <h1 id=​"load-experiment">​ hello world ​</h1>​
script1.js:8 script1 finishes running at 1496760313686
script2.js:2 script2 is running at 1496760313686
script2.js:4 script2 element <h1 id=​"load-experiment">​ hello world ​</h1>​
script2.js:8 script2 finishes running at 1496760315686

Conclusion:

  • The result is the same as putting scripts at the bottom of body. We can conclude from the result that the scripts are run after the DOM is ready as we can indeed fetch the DOM elements.
  • Even the defered scripts follow the order rule specified in html, script1 is run after script2.

async!

Web pages often contain some add-on features which are strictly independently and NOT must-to-have, such as the comment and chat functionalities on some pages. As the features are independent, they don't have the running order restriction. In this case, we can use async

<!--async.html-->
<html>
<head>
<title> test js tag async and defer attributes</title>
</head>
<body>
<h1 id='load-experiment'> hello world </h1>
<script async src='./script1.js'></script>
<script async src='./script2.js'></script>
</body>
</html>

We can observe different console outputs when we refresh the page:

  • The running order of script1 and script2 varies
  • The result of fetching DOM element is inconsistent

As async scripts don't guarantee the running order, this is often the source of potential hidden bugs. Have a second thought before using async and mare sure these scripts are strictly independent.

Conclusion

The general rule to import script is:

<html>
<head>
<!--headScripts.js is the script that has to be loaded and run before DOM is ready-->
<script src="headScripts.js"></scripts>
<!--bodyScripts.js loads first and runs after DOM is ready-->
<script defer src="bodyScripts.js"></script>
</head>
<body>
<!--body content-->
<h1 id='load-experiment'> hello world </h1>
<!--independent scripts,nice-to-have -->
<script async src="forumWidget.js"></script>
<script async src="chatWidget.js"></script>
</body>
</html>

Code Sample

Reference

Notice

  • If you benefit from this Repo,Please「Star 」to Support.
  • If you want to follow the latest news/articles for the series of reading notes, Please 「Watch」to Subscribe.

Five minutes to understand async and defer的更多相关文章

  1. script标签不带属性与带async、defer的区别

    <script> 当页面解析到script标签时,会停止解析并下载对应的脚本,并马上执行,执行完毕后再继续解析页面 <script async> async 在下载脚本的同时不 ...

  2. js之script属性async与defer

    概念 默认情况下js的脚本执行是同步和阻塞的,但是 <script> 标签有 defer 和 async 属性, 这可以改变脚本的执行方式,这些都是布尔类型了,没有值,只需要出现在 < ...

  3. 转:script中的async和defer

    script中的async和defer defer: This Boolean attribute is set to indicate to a browser that the script is ...

  4. 浅谈script标签中的async和defer

    script标签用于加载脚本与执行脚本,在前端开发中可以说是非常重要的标签了.直接使用script脚本的话,html会按照顺序来加载并执行脚本,在脚本加载&执行的过程中,会阻塞后续的DOM渲染 ...

  5. async与defer

    <script>元素的几种常见属性: async  异步加载,立即下载,不应妨碍页面其他操作,标记为 async 的异步脚本并不保证按照指定的先后顺序执行,因此异步脚本不应该在加载期间修改 ...

  6. JavaScript异步加载的三种方式——async和defer、动态创建script

    一.script标签的位置 传统的做法是:所有script元素都放在head元素中,必须等到全部js代码都被下载.解析.执行完毕后,才能开始呈现网页的内容(浏览器在遇到<body>标签时才 ...

  7. JS—ajax及async和defer的区别

    ###1.ajax  “Asynchronous Javascript And XML”(异步 JavaScript 和 XML) 使用: 如不考虑旧版本浏览器兼容性, // 第一步创建xhr对象 v ...

  8. script 标签里的 async 和 defer

    无 async 和 defer 浏览器立即加载并执行指定脚本(读到即加载并执行),阻塞文档解析 async 脚本的加载执行和文档的加载渲染 并行. defer 脚本的加载和文档的加载渲染并行,但脚本的 ...

  9. script async和defer

    1.没有async和defer,html解析时遇到script标签,会先去下载js文件,文件加载完成立即执行,执行完了再开始解析后面的html,是一个顺序流的过程 2.async,加载和渲染后续文档元 ...

随机推荐

  1. python-Web-项目-svn和git

    SVN概念: >>>本地服务端 >>>服务端: 安装:windows傻瓜式安装 使用: 1 在弹出的右键菜单中选择Create New Repository或者新建 ...

  2. php配置php-fpm启动参数及配置详

    php-fpm 启动参数及重要配置详解 约定几个目录 /usr/local/php/sbin/php-fpm/usr/local/php/etc/php-fpm.conf/usr/local/php/ ...

  3. 338.比特位计数( Counting Bits)leetcode

    附上:题目地址:https://leetcode-cn.com/problems/counting-bits/submissions/ 1:题目: 给定一个非负整数 num.对于 0 ≤ i ≤ nu ...

  4. python基础之元祖tuple

    元祖是只读列表,不可哈希,可循环查询,可切片*儿子不能改,孙子可更改--元祖里面单个元素不能更改---元祖内列表可更改增:tu1+tu2查:tu1[index] tu1[start_index:end ...

  5. linux centos 安装输入法

    终端输入命令: yum install ibus-libpinyin.x86_64

  6. 多线程--原子操作 Interlocked系列函数

    [转]原文地址:http://blog.csdn.net/morewindows/article/details/7429155 线程同步与互斥: 互斥主要指多个线程不能同时访问一个资源,如打印机就是 ...

  7. Luogu P5354 [Ynoi2017]由乃的OJ

    题目 这题以前叫睡觉困难综合征. 首先我们需要知道起床困难综合征怎么做. 大概就是先用一个全\(0\)和全\(1\)的变量跑一遍处理出每一位\(1\)和\(0\)最后会变成什么. 然后高位贪心:如果当 ...

  8. thinkphp6下无法获取header头中的Authorization(apache版)

    今天遇到在thinkphp框架中获取不到header头里边的 Authorization ,后来在.htaccess里面加多一项解决,记录下: <IfModule mod_rewrite.c&g ...

  9. Springboot实现上传文件接口,使用python的requests进行组装报文上传文件的方法

    记录瞬间 近段时间使用Springboot实现了文件的上传服务,但是在使用python的requests进行post上传时,总是报错. 比如: 1.Current request is not a m ...

  10. 04 Websocket和Websocketed

    一.web socket事件和方法 有了HTTP协议为什么还需要Websocket这种协议呢?因为HTTP协议发起的通信只能通过客户端发起,然后服务端才可以将消息回应到客户端.因此HTTP协议做不到服 ...