前端优化有一点就是优化js的执行时机,一般做法是将script放置于body的结束标签,以避免加载执行js 文件导致页面渲染阻塞的问题
这种做法确实能防止页面阻塞,但是在页面渲染完成之后才去加载js文件,有时候会显得js文件加载时间过长。
于是我们可以合理的使用script的属性defer,async

(一)分析defer,async的作用

(1)在不加defer,async的情况下
页面会预先渲染,如果遇到script 标签,他就会停止页面的继续渲染,去加载js文件,带文件加载完并执行结束之后,才会继续刚才暂停的页面渲染,
这意味着在head中加载的js文件如果有操作dom,或者获取dom节点信息的操作,可能会出错,因为DOMContentLoaded还没执行。
这也就是为什么需要将script文件放置在body结束标签之前的原因

(2)async
使用async之后会使得浏览器异步加载js文件,异步加载的意思是在加载js文件的过程中并不会阻塞页面的渲染,页面的渲染和js文件的加载是同时进行的
但是在js文件加载完之后,就会停止页面的渲染立即执行js文件,这个也是async与defer的区别。
如果在文件里面有相关dom操作可能会报错,因为html可能只是解析了一部分
因为带async的脚本一定会在load事件之前执行,可能会在DOMContentLoaded之前或之后执行,这个与加载脚本的时间长短有关系。

case 01

case 02

(3)defer
页面的渲染和js文件的加载是同时进行的,但是js文件加载完之后并不会立即执行而是需要在页面渲染完之后才去执行,但是带有defer的脚本(defer-script)是在DOMContentLoaded之前执行的

参考:https://html.spec.whatwg.org/multipage/scripting.html#ready-to-be-parser-executed

case 01

case 02

(二)区分load与DOMContentLoaded

DOMContentLoaded:
  当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。
  https://developer.mozilla.org/zh-CN/docs/Web/Events/DOMContentLoaded
load:
  当一个资源及其依赖资源已完成加载时,将触发load事件。
 
(三)HTML解析过程与DOMContentLoaded触发时机

1.在既没有CSS也没有JS的情况下,HTML文档的解析过程为

DOMContentLoaded事件的触发时机为:HTML解析为DOM之后。

2.有CSS无JS的情况下,HTML文档解析过程为:

这里与1.不同的地方在于,渲染树的生成是基于DOM和CSSOM的。但是触发DOMContentLoaded的时间依然是在HTML解析为DOM后,无论此时CSS解析为CSSOM的过程是否完成。

3.当有JS时,HTML文档解析过程为:

 

(四)JS单线程,浏览器多线程的问题

浏览器是多线程:
1.js引擎线程
  作用:JS内核,主要负责处理Javascript脚本程序,例如V8引擎。Javascript引擎线程理所当然是负责解析Javascript脚本,运行代码。
  js 是单线程(js引擎线程):
  js运作在浏览器中,是单线程的,js代码始终在一个线程上执行,因此任务分为同步(立即执行)异步任务(事件队列),
  web worker也只是允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。

2.GUI渲染线程
  作用:渲染页面(js可以操作dom,影响渲染,所以js引擎线程和UI线程是互斥的。js执行时会阻塞页面的渲染)
  当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行
  GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行
  这也是为什么<script>标签要放到body标签的最底部。

3.浏览器事件触发线程
  作用:控制交互,响应用户
  当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理

4.http请求线程
  作用:ajax请求等
  在XMLHttpRequest在连接后是通过浏览器新开一个线程请求, 将检测到状态变更时,如果设置有回调函数,异步线程就产生状
  态变更事件放到 JavaScript引擎的处理队列中等待处理。在发起了一个异步请求时,http请求线程则负责去请求服务器,有了响应以后,
  事件触发线程再把回到函数放到事件队列当中。

5.定时触发器线程
  作用:setTimeout和setInteval

6.事件轮询处理线程
  作用:轮询消息队列,event loop

主线程执行的说明: 【js的运行机制】
(1)所有同步任务都在主线程上执行,形成一个执行栈。
(2)主线程之外,还存在一个”任务队列”。只要异步任务有了运行结果,就在”任务队列”之中放置一个事件。
(3)一旦”执行栈”中的所有同步任务执行完毕,系统就会读取”任务队列”,看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

参考链接:https://blog.csdn.net/github_34514750/article/details/76577663

script 标签的defer,async的作用,及拓展浏览器多线程,DOMContentLoaded的更多相关文章

  1. HTML <script> 标签的 defer 和 async 属性

    HTMKL <script>标签中有defer和async属性,简单介绍一下两者的区别吧.   普通的script标签会让浏览器立即下载并执行完毕,执行也是按照先后顺序,再进行后面的解析. ...

  2. script标签的defer、async属性

    之前一直对script标签的defer.async属性一知半解,直到看到了论坛上某大神发的图片,茅塞顿开!!!!!

  3. 怎样理解script标签的defer属性和async属性

    如果script标签是引用的外部js文件, 那就会有一个下载js文件这一过程, 为了不因为这个下载过程而阻塞页面解析与渲染, 我们需要一种机制来解决这一问题, 方法之一就是使用 defer和async ...

  4. script标签中defer和async属性的区别

    这篇文章来源于JS高级程序设计第三版中关于script标签的介绍,结合查阅的资料写下的学习笔记. 向html页面中插入javascript代码的主要方法就是通过script标签.其中包括两种形式,第一 ...

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

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

  6. script标签中的async、defer属性

    Script标签是我们常用的引用js脚本的一种方式. 撸代码的时候,我们常常只写src属性,直接忽略其他属性. 最近发现了2个可以利用的属性:async.defer. 顾名思义async就是异步,在不 ...

  7. script标签中defer和async的区别(稀土掘金学习)

    如果没有defer或async属性,浏览器会立即加载并执行相应的脚本.它不会等待后续加载的文档元素,读取到就会开始加载和执行,这样就阻塞了后续文档的加载. 下图可以直观的看出三者之间的区别: 其中蓝色 ...

  8. script 标签里的 async 和 defer

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

  9. <script>标签里的defer和async属性 区别(待补充)

    defer与async的区别(表格显示): table th:first-of-type { width: 150px; } table th:nth-of-type(2) { } 区别 defer ...

随机推荐

  1. Harbor和YUM部署for CentOS 7

    Harbor部署for CentOS 7 下载 wget https://storage.googleapis.com/harbor-releases/release-1.7.0/harbor-off ...

  2. iOS UI-创建空项目

    一.创建工程 二.删除ViewController 三.在Supporting Files/Info.plist文件中清空值 四.创建新的控制器 五.写代码 1.在AppDelegate.h文件中 # ...

  3. angular2使用ng g component navbar创建组件报错

    Error: ELOOP: too many symbolic links encountered, stat 'C:\Users\inn\angulardemo\node_modules\@angu ...

  4. c#输出指定信息到文本文件中(追加方式)

    /// <summary> /// 输出指定信息到文本文件 /// </summary> /// <param name="msg">输出信息& ...

  5. jenkins的应用与搭建

    实验条件2个服务器, git服务器 ip地址:192.168.200.151 Jenkins服务器 ip地址:192.168.200.132 操作系统:centos7.5 [root@git ~]# ...

  6. 模拟QQ分组

    package com.lixu.fenzu; import java.util.ArrayList; import java.util.HashMap; import android.app.Lis ...

  7. sql server 的变量

    对于占用关键字等不符合规则的命名,可使用中括号[ ]括起来. 局部变量: 局部变量名必须以@开头,作用范围仅为程序内部. 常用用途: 1.作为计数器计算循环执行的次数或控制循环执行的次数 2.保存数据 ...

  8. focusSNS学习笔记

    FocusSNS是一个社交类型的网站架构 系统的加载过程 所有的分发都从RouteController开始 @RequestMapping(value={"/", "/h ...

  9. 【LeetCode 235_二叉搜索树】Lowest Common Ancestor of a Binary Search Tree

    解法一:递归 TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { if (root == NULL | ...

  10. ZedGraph实时曲线实例

    2010-10-17 11:23:58| 分类: ASP.NET |举报|字号 订阅public partial class FrmMain : Form { // 起始时间以毫秒为单位 int ti ...