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. MyBatis 简单入门

    添加maven 依赖 <dependencies> <dependency> <groupId>org.mybatis</groupId> <ar ...

  2. 【Linux开发】linux设备驱动归纳总结(十):1.udev&misc

    linux设备驱动归纳总结(十):1.udev&misc xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  3. 小米手机Toast带app名称

    如果用小米手机做测试,会发现,Toast弹窗有可能会在前面带app名称.这是因为你传入的context是activity,如果是Application的话,就不会显示app名称.但是,我做测试时,一般 ...

  4. Apache Zookeeper 集群环境搭建

    Zookeeper 是 一个分布式.开放源码的分布式应用程序协调服务,是Google Chubby的一个开源实现,大多数的分布式应用都需要Zookeeper的支持,这篇文章先简单的和大家分享如何搭建一 ...

  5. C++深拷贝和浅拷贝细节理解

    前提 在对象拷贝过程中,如果没有自定义拷贝构造函数,编译器会提供一个缺省的拷贝构造函数,缺省的拷贝构造函数对于基本类型的成员变量,按字节复制,对于类类型的成员变量则调用其相应的拷贝构造函数. 资料注解 ...

  6. java并发学习资料

    1.Java 并发编程知识梳理以及常见处理模式 https://github.com/Fadezed/concurrency 2.Java 高并发多线程编程系列 https://github.com/ ...

  7. [Python3] 035 函数式编程 高阶函数

    目录 函数式编程 之 高阶函数 1. 引子 2. 系统提供的高阶函数 3. functools 包提供的 reduce 4. 排序 函数式编程 之 高阶函数 把函数作为参数使用的函数,叫高阶函数 1. ...

  8. PostgreSQL查询数据库中包含某种类型的表有哪些

    and c.relnamespace = n.oid and nspname = 'public' and a.atttypid = t.oid and typname = 'TEXT' and c. ...

  9. MySQL中关于主从数据库同步延迟的问题解决

    MySQL的主从同步是一个很成熟的架构,优点为:①在从服务器可以执行查询工作(即我们常说的读功能),降低主服务器压力;②在从主服务器进行备份,避免备份期间影响主服务器服务;③当主服务器出现问题时,可以 ...

  10. CSP 画图(201512-3)

    问题描述 用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ASCII Art 的艺术.例如,下图是用 ASCII 字符画出来的 CSPRO 字样. ..____.____..____. ...