从JS的加载和执行谈性能优化

---高性能JS读后感(第一章)

从脚本的"霸道"说起,随着浏览器的进步,js越来越听话了,所以,我们先说说以前的浏览器是怎么加载js的,以及js如何个霸道法.

1.阻塞的脚本

在以前,js很霸道.

问:当我们在网页中使用<script></script>时,到底发生了什么?

答:无论是内嵌的<script>标签代码还是用<script>引入文件,当浏览器解析到<script>元素时,浏览器就会去加载js内容,这时,页面的其他资源的加载和渲染都会停止,可以说<script>元素是老大,当他说话(加载)时,其他人都不敢说(其他资源都会停止加载和渲染),这就是我们常说的js会阻塞资源加载和页面渲染.这是<script>的霸道(特点).单加载就阻塞了,执行就更不用说了.

所以,我们一般把js放在页面的底部,</body>之前,这样当页面加载到底部的js时,页面的结构和渲染都完成了,这个时候你阻塞也不影响我页面的加载.

后来浏览器开始进步

当浏览器解析到<script>元素时,依旧会阻塞页面其他资源的加载(比如图片)和渲染(css的渲染),但是如果页面有多个<script>,就是说如果我们引入两份js文件,那么这两份js文件是可以并行加载的,不用像之前一样,得第一份加载完再去加载第二份.

浏览器继续进步

2.无阻塞的脚本

无阻塞脚本的秘诀在于,在页面加载完成后才加载js代码.就是说在window对象的load事件触发后再下载脚本.

无阻塞脚本之延迟的脚本(这里的延迟指的是延迟执行,而不是延迟加载)

在现在的主流浏览器,js的加载已经不会阻塞页面了,他能够与其他资源和平共处,就是说他越来越乖巧了,当他在加载时,他允许图片等其他资源的加载,当然执行的话还是会阻塞的.

如下图,js与图片并行加载.(主流浏览器)

js的加载说完了,我们说说js的执行.

问:js执行为什么会阻塞页面的渲染.

答:js在执行时,一般都会操作DOM,操作DOM就会影响到DOM树的渲染,所以js的执行肯定会阻塞渲染.

注意:js是单线程,但浏览器是多线程,js在执行时,DOM树在构建,同时渲染树也在渲染,这个时候如果js构建和浏览器即将渲染的是同一个节点怎么办?所以,为免冲突,js在执行时,停止渲染.

现在js的加载和执行略懂略懂,那么问题来了.

问:js放置的最优位置的底部,我现在有一份js必须在head,等页面结构加载完(window.onload)就马上执行,就是说我要先加载后执行,那么我该怎么办?

答:用defer,在<script>加上defer属性

<script type="text/javascript" src="jquery-1.8.3.min.js" defer></script>

defer的功能:指明本元素所含的脚本不会修改DOM,(即只加载不执行),因此代码能安全的延迟执行.阻塞的脚本变成延迟的脚本了.

问:我现在有一份js必须在head,想让它加载完马上执行怎么办?

答:用async,在<script>加上async属性

<script type="text/javascript" src="jquery-1.8.3.min.js" async></script>

async的功能:异步并行加载js,加载完之后立即执行.defer是加载后等页面加载后再执行,两者的区别是执行方式的不同.

问:我们什么时候用defer,什么时候用async?

答:

1.当js并不需要立马加载的时候,我们把js放在页面底部就可以了,不需要用到这两个属性.

2.当某段js需要立马加载时,判断其是否需要立即执行,需要就用async,不需要立即执行就用defer.

当然,这里需要强调下,js立即执行,这个时候是会阻塞页面的渲染的.

也需要考虑到,这两个属性的浏览器支持情况,在不支持这两个属性的浏览器上,其实他们是会阻塞页面的渲染的(不支持的情况下,加载js不阻塞,但是加载完后的js会去立马执行,这个时候就会阻塞了).

3.DOM树创建完并不意味着页面资源加载完,所以在DOM树创建完,触发window.onload的时候,中间还是有一点时间的,这个时间就是加载其他资源的时间.(比如说加载js)

4.其实,在不考虑多个js文件有多个http请求数的情况下,当你把js放在页面底部的时候,其实已经是一种无阻塞的脚本了.

总结:(摘自书中内容)

减少js对性能的影响:

1.</body>闭合标签之前,将所有的<script>标签放到页面底部.这能确保在脚本执行前页面已经完成了渲染.

2.合并脚本.页面的中的<script>标签越少,加载也就越快,响应也更迅速.无论外链文件还是内嵌脚本都是如此.

3.有多种无阻塞下载js的方法:(可自行搜索用法)

  • 使用<script>标签的defer属性
  • 使用动态创建的<script>元素来下载并执行代码
  • 使用XHR对象下载js代码并注入页面中

个人觉得,在目前,js模块加载器,js模块打包器都有,解决文件的依赖关系并且打成一个包是比较容易的,再把打成的包放到</body>,可以减少http请求,也不会阻塞页面加载和渲染(可以说这是一个无阻塞的脚本),虽然这个文件可能很大,但相对于减少了http请求数来说,还是利大于弊的,因为我们一个页面不打包可能有很多个js,这样请求数会很多.

当然对于一些库,我们可以单独引入,这样可以缓存起来,缓存起来的文件加载就更快了.

有错请指出,欢迎交流~

JS的加载和执行的更多相关文章

  1. JS 动态加载脚本 执行回调

    JS 动态加载脚本  执行回调 关于在javascript里面加载其它的js文件的问题可能很多人都遇到过,但很多朋友可能并不知道怎么判断我们要加载的js文件是否加载完成,如果没有加载完成我们就调用文件 ...

  2. 性能优化-css,js的加载与执行

    前端性能优化 css,js的加载与执行 javascript是单线程的 一个网站在浏览器是如何进行渲染的呢? html页面加载渲染的过程 html渲染过程的一些特点 顺序执行,并发加载 词法分析 并发 ...

  3. JS 动态加载脚本 执行回调_转

    关于在javascript里面加载其它的js文件的问题可能很多人都遇到过,但很多朋友可能并不知道怎么判断我们要加载的js文件是否加载完成,如果没有加载完成我们就调用文件里面的函数是不会成功的.本文讲解 ...

  4. JS脚本加载与执行对性能的影响

    高性能JavaScript-JS脚本加载与执行对性能的影响 在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 ...

  5. 前端性能优化 css和js的加载与执行

    一个网站在浏览器端是如何进行渲染的? html本身首先会被渲染成 DOM 树,实际上 html 是最先通过网址请求过来的,请求过来之后,html 本身会由一个字节流转化成一个字符流,浏览器端拿的就是字 ...

  6. 高性能javascript学习笔记系列(1) -js的加载和执行

    这篇笔记的内容主要涉及js的脚本位置,如何加载js脚本和脚本文件执行的问题,按照自己的理解结合高性能JavaScript整理出来的 javascript是解释性代码,解释性代码需要经历转化成计算机指令 ...

  7. js并行加载,顺序执行

    js并行加载,顺序执行 <script>运行脚本或加载外部文件时,会阻塞页面渲染,阻塞其他资源的加载.如果页面中需要加载多个js文件,在古老浏览器中性能会比较糟糕. 因此有了最原始的优化原 ...

  8. jquery js 动态加载 js文件

    jquery方法 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://ww ...

  9. 使用getScript()方法异步加载并执行js文件

    使用getScript()方法异步加载并执行js文件 使用getScript()方法异步请求并执行服务器中的JavaScript格式的文件,它的调用格式如下所示: jQuery.getScript(u ...

随机推荐

  1. CSS中的一些伪类

    一.:nth-child 和 :nth-of-type (1):nth-child() :nth-child(n) 选择器选取某任意一父元素的第 n 个子元素( p:nth-child(n) 即选中任 ...

  2. vue开发微信公众号--地图

    在最近开发的微信公众号中,要实现一个打卡功能: 由于个人感觉微信SDK里面的地图不太好用,所以使用了腾讯地图. 在项目中引入腾讯地图 1,需要登录腾讯地图网站,注册一个账户,获得一个key. 2,然后 ...

  3. qt编程参考资料

    https://qtguide.ustclug.org/

  4. el-tree或者el-table里边的slot-scope传递变量但是没有元素对象解决方法

    传统的是点击一个元素可以通过e.target进行获取,但是对于这种的回调函数里边加个e再进行e.target就获取不到元素了,更获取不到点击的位置那么可以在触发的地方写成箭头函数并且传递一个变量.如下 ...

  5. nginx启动、关闭、重启及常用的命令

    nginx常用命令 启动:cd /usr/local/nginx/sbin./nginxnginx服务启动后默认的进程号会放在/usr/local/nginx/logs/nginx.pid文件cat ...

  6. linux设备驱动学习笔记--内核调试方法之printk

    1,printk类似于用户态的printf函数,但是比printf函数多了一个日志级别,内核中最常见的日志输出都是通过调用printk来实现的,其打印级别有8种可能的记录字串, 在头文件 <Li ...

  7. windows10 cortana 不能搜索解决办法

    不太确定是某次系统更新或安装VS软件之后, 发现windows10 cortana 搜索的结果是空白了, 搜索了相关帖子, 试遍所有方法都无效, 最后在联网的情况下, 只用了在powershell中重 ...

  8. java 字符串的操作方法

    方法 作用 范例   indexOf() 找到第一个字符出现的位置,()以下标来判断,返回的是字符所在的下标 int  num = String.indexOf("字符")   l ...

  9. delphi 在代码中 添加 TO-DO 并且 管理

    TO-DO List是一项非常好用的功能.采用她可以让我们很清楚的了解以前完成了那些任务,还有哪些任务需要做,由谁负责完成,是不是比较紧急的任务等.今天来不及完成的,明天上班就可以很快的找到任务所在的 ...

  10. git---从已有分支拉出新分支

    文章目录 开发中,经常需要从一个已有的分支拉出一个新分支,去这个新分支做一些开发改动,这里示例为: 从master分支,重新拉取出一个新的分支,名字为dev,具体命令如下: 切换到被copy的分支(m ...