高性能JavaScript-JS脚本加载与执行对性能的影响

在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化。本篇文章简单描述一下其中的优化准则。

1. 脚本加载优化

1.1 脚本位置对性能的影响

优化页面加载性能的原则之一是将script标签放在body底部,这跟浏览器的渲染原理有关:

  1. js脚本的下载和执行会阻塞浏览器的解析。在较早时期,浏览器不支持并行下载的时候,js脚本的下载执行按照在html文档中的位置依次进行,可以想象当页面有大量js脚本时页面的加载有多慢;
  2. js脚本的下载会阻塞其他资源的下载,比如图片、外链css等。虽然目前大多数浏览器支持并行下载,外链js文件可以并行下载,但是在下载js的过程中,其他资源的加载仍然会被阻塞。

综上所述,除非业务需求必须将js脚本放在指定位置,最佳的优化准则是将js放于body底部。

1.2 合并脚本文件

每个script标签都会阻塞页面的解析和其他资源的加载,可以通过合并js脚本文件进行优化。虽然目前大多数浏览器支持并行下载,但是随着web产品越来越庞大,浏览器支持的并行数量明显不能满足需求,随意js文件的合并打包是很有必要的。

目前较流行的grunt/gulp/webpack等编译工具都支持文件的打包合并,webpack甚至可以将css文件也一并打包到js文件里。我们先不去评价这种模式的好坏,单从减少文件数量这个角度来看,这是为了减少http请求数目、script标签数量以提高页面的加载性能。

其实这种理念很早就有,有后端开发经验的朋友可能接触过combo handler,这是Yahoo YUI团队开发的一个Apacha模块。combo handler可以支持浏览器使用一个url请求多个文件,比如我们页面中需要两个js文件,常规情况下使用2个script标签请求:

<script src='http://static.me.com/a.js'></script>
<script src='http://static.me.com/b.js'></script>

使用combo handler可以通过以下形式:

<script src='http://static.me.com?a.js&b.js'></script>

a.js和b.js在服务器是独立存在的,combo handler可以通过一个http请求将两者合并为一起返回,减少了http请求数目,提高了页面加载性能。

2. 无阻塞脚本

2.1 defer和async

defer和async都是针对外链的js脚本文件,如下:

<script src='http://static.me.com/a.js' defer></script>
<script src='http://static.me.com/a.js' async></script>

defer和async的作用都是令指定的js文件异步加载,不影响html文档其他内容的解析,也就是说带有defer和async的js文件和html文档的解析是并行的。但是两者的运行机制有稍许差别。

defer在IE4就引入了,目前几乎所有浏览器都支持。defer的js文件在并行下载结束后并不立即执行,其执行时机是在文档加载完毕后window.onload触发之前

async是HTML5引入的新规范,目前获得了大多数浏览器的支持。async的js文件在并行下载结束后立即执行

比较defer和async的区别可以得到以下结论:

  1. 两者都是并行下载,不影响html文档的解析;
  2. defer文件的执行时机是在window.onload之前,所以defer文件的位置任意;
  3. async文件下载结束后立即执行,是乱序的。所以并不适用于有依赖关系的js脚本;
  4. defer和async的脚本中应当避免使用document.wirte,否则会清空页面原有的内容。
2.2 动态脚本

所谓动态脚本的意思是使用JavaScript创建一个script,指定其src并将此script标签插入文档的head中。

之所以将动态script标签插入head而不是body中是因为在低版本IE中如果在html文档未解析完毕时,body中插入script标签会抛出“操作已终止”的错误信息。具体可参考The dreaded Operation Aborted error

动态脚本请求到的js脚本是立即执行的。

动态创建script标签时,某些业务场景下需要监听被请求的js脚本是否加载完毕。大多数浏览器都支持script.onload事件:

script.onload = function(){}

IE浏览器没有实现onload事件,而是会触发readystatechange事件。当readyState的状态为loadedcomplete时便可以认为js脚本文件已加载完毕。兼容所有浏览器的loadScript函数如下:

function loadScript(url,callbakc){
var script = document.createElement('script');
script.type = 'text/javascript'; //低版本IE下必须制定type,其他浏览器可以不写 if(script.readtState){ //IE
script.onreadystatechange = function(){
if(script.readyState === 'loaded' || script.readyState === 'complete'){
script.readyState === 'null';
callback();
}
};
}else{ //其他浏览器
script.onload = function(){
callbakc();
}
} script.src = url;
document.getElementByTagName('head')[0].appendChild(script);
}
2.3 使用XHR注入js脚本

使用XHR注入脚本是比较偏门并且应用面很小的一门技术,原理就是用Ajax去get请求一个js文件,监听xhr.status,获取到的响应信息是js文件的代码。然后动态创建一个script标签,将获取到的js代码注入script标签内,最后将script标签插入文档中。

这种方式注入的js脚步并不会立即执行。缺点是无法跨域,因此很少得到应用。

JS脚本加载与执行对性能的影响的更多相关文章

  1. 高性能JavaScript-JS脚本加载与执行对性能的影响

    在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 脚本位置对性能的影响 优化页面加载性能的原则之一是将scri ...

  2. JS的加载和执行

    从JS的加载和执行谈性能优化 ---高性能JS读后感(第一章) 从脚本的"霸道"说起,随着浏览器的进步,js越来越听话了,所以,我们先说说以前的浏览器是怎么加载js的,以及js如何 ...

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

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

  4. 浏览器环境下JavaScript脚本加载与执行探析之defer与async特性

    defer和async特性相信是很多JavaScript开发者"熟悉而又不熟悉"的两个特性,从字面上来看,二者的功能很好理解,分别是"延迟脚本"和"异 ...

  5. 浏览器环境下Javascript脚本加载与执行探析之DOMContentLoaded

    在”浏览器环境下Javascript脚本加载与执行探析“系列文章的前几篇,分别针对浏览器环境下JavaScript加载与执行相关的知识点或者属性进行了探究,感兴趣的同学可以先行阅读前几篇文章,了解相关 ...

  6. 浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入

    在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及 ...

  7. CSS样式表、JS脚本加载顺序与SpringMVC在URL路径中传参数与SpringMVC 拦截器

    CSS样式表和JS脚本加载顺序 Css样式表文件要在<head>中先加载,这样网页显示时可以第一次就渲染出正确的布局和样式,网页就不会闪烁,或跳变 JS脚本尽可能放在<body> ...

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

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

  9. 脚本加载后执行JS回调函数的方法

    动态脚本简单示例 // IE下: var HEAD = document.getElementsByTagName('head')[0] || document.documentElement var ...

随机推荐

  1. Delphi代码中嵌入ASM代码(简单明了)

    前言 Delphi作为一个快速高效的开发平台,使用的人越来越多,但熟悉在Delphi代码中嵌入ASM代码的程序员我想不多,因为这方面的资料太少了,另一方面,它还需要有基本的汇编语言知识,关於汇编语言的 ...

  2. 14.2.5.2 Clustered and Secondary Indexes

    14.2.5.2 Clustered and Secondary Indexes : 每个InnoDB 表 有一个特别的索引称为clustered index 行数据存储的地方. 典型的,cluste ...

  3. java(样品集成框架spring、spring mvc、spring data jpa、hibernate)

    这是你自己的参考springside集成框架的开源项目.主要的整合spring.spring mvc.spring data jpa.hibernate几个框架,对于这些框架中仍然感觉更舒适sprin ...

  4. JS Bin Tips and Bits • About

    JS Bin Tips and Bits • About   About Who built this? JS Bin was built by Remy Sharp and is completel ...

  5. Erlang cowboy 处理不规范的client

    Erlang cowboy 处理不规范的client Cowboy 1.0 參考 本章: Dealing with broken clients 存在很多HTTP协议的实现版本号. 很多广泛使用的cl ...

  6. BUG系列:转让startActivityForResult()&amp;onActivityResult()没有反应

    前天遇到了一个麻烦,还真是麻烦啊. 我使用startActivityForResult()&onActivityResult().由Activity-A 跳转到Activity-B 页面,然后 ...

  7. Types of Windows

    The Window's Real Estate   Application's Instance   A window is referred to as parent when it can be ...

  8. 设置Mysql的连接超时参数

     在Mysql的默认设置中,如果一个数据库连接超过8小时没有使用(闲置8小时,即   28800s),mysql server将主动断开这条连接,后续在该连接上进行的查询操作都将失败,将   出现:e ...

  9. g++优化选项

    g++优化选项 g++优化选项 对于下面的这段代码: 1 2 3 4 5 6 7 8 9 10 11 12 #include <iostream> using namespace std; ...

  10. Spring中继承配置的注入方法

    (1)两个java类.一个父类一个字类 package com.lc.inherit; /* * 这里是父类 */ public class Student { protected String na ...