高性能javascript学习笔记系列(1) -js的加载和执行
这篇笔记的内容主要涉及js的脚本位置,如何加载js脚本和脚本文件执行的问题,按照自己的理解结合高性能JavaScript整理出来的
javascript是解释性代码,解释性代码需要经历转化成计算机指令的过程,这个过程就会带来一定的性能损耗,所以在js中做性能的优化是必须的
javascript的阻塞特性:浏览器在执行js代码的时候,不能做其他的任何事情,因为浏览器使用单一的进程来处理用户界面的刷新和javascript的脚本执行,也就是说什么时候执行js脚本影响着用户对页面的使用体验(之所以js会阻塞页面的解析和渲染,是因为无法预期js时候会对页面进行修改,所以会先执行完js代码在继续解析和渲染界面,无论是外链的js文件或者是内联的js文件的)
基于以上的原因,js文件位置决定这用户的体验并且通过js文件的优化能尽可能的提高页面的性能
<head>
<meta charset="UTF-8">
<title>Document</title>
<script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
上面这种模式我们将js文件放置在head中,这样的位置是存在问题的
(1)首先js的阻塞特性会导致必须等待这两个文件下载和执行,页面才会渲染,会出现空白,用户体验不好
(2)浏览器在解析body标签之前不会渲染页面的任何部分,也就是在a.js 和 b.js 在执行的过程中不存在页面的dom树,这个时候对页面进行操作就会出错
/*虽然现代的浏览器可以实现js的并行下载,但是js的下载过程中还是会阻塞其他资源的下载,例如图片等 外链的CSS文件本身已经是并行下载的*/
所以推荐的js脚本放置位置是下面这样的形式
<body> <script type="text/javascript" src="a.js"></script>
<script type="text/javascript" src="b.js"></script>
</body>
这样的放置模式在下载脚本文件和执行脚本文件的时候,页面的大部分内容已经显示给用户,也就是放置在body的最底部
组织脚本
script标签会阻塞页面的渲染,从提高页面的性能的角度考虑,就是如何尽可能的减少script标签去组织脚本,由于现在js文件的模块化和功能化越来越清晰,文件的数量有的时候很难去实质的减少,一个并不是特别好的方案,就是简单的合并两个js文件,因为减少了http请求这样的方式会比单纯的下载两个之前独立的js文件要快,但是这样也存在一定的问题
(1)破坏了js文件的模块性,两个不同功能的模块糅合在一起了
(2)在服务端我们需要增加更多的js文件,占据着服务器的资源
我们可以通过一些静态打包工具或者类似雅虎提供的合并处理器通过它们的CDN来实现一个url来加载两个js文件
/*不要将内联脚本放置在外链样式表的后面,这样会导致页面阻塞去等待样式表去下载(为了在js文件执行的时候获取到最精准的样式信息)*/
无阻塞脚本
通过上面合并url或者合并js文件并不能很好地提高的页面的性能,所以提出了无阻塞脚本,就是在页面加载完成后才加载js代码,也就是在相应window.onload事件触发后才去下载脚本(仔细理解无阻塞脚本就是这个js文件的下载不会阻塞页面其他元素的下载) 有几种方式可以实现上面的要求
(1)延时脚本defer
defer属性在js文件不会修改文档的时候可以使用,因为js文件不会修改文档,就不需要等待js的执行去停止页面的渲染 等待页面完成后执行(无位置需求)
ansyc 属性 js文件下载完成后自动执行(body底部 下载执行的时候需要页面的元素准备完毕)
可以通过上面这两种方式实现无阻塞脚本
举一个defer的例子
/*我测试了chrome下和IE下都支持了defer属性 但是当js文件是内联的时候 defer就会失效 */
<body>
<script type="text/javascript" src="a.js" defer></script>
<script type="text/javascript" defer>
console.log(1);
</script>
<input type="button" value="test" id="btn" /> </body>
a.js中的内容如下
var btn = document.getElementById("btn");
console.log(btn);
通过查看控制台我们发现a.js的确延时执行了,也就是在页面window.load 之前执行了a.js文件 但是内联的js的文件并没有延时
(2)动态脚本元素 script元素与其他元素一样可以动态的创建script元素,并且这种方式文件下载和执行的过程不会阻塞其他的进程,并且这种方式添加的脚本文件会在下载完成后立即执行,但是当你添加的这个脚本是一个提供接口的脚本的时候就需要获取一些信息来确认当前这个接口是否可以然后在进行后面的操作 IE下可以通过onreadystatechange事件 通过script的readyState状态来获取脚本完成时的状态 标准浏览器是通过onload事件来判断添加的js文件的状态
可以通过下面这个函数来实现动态的加载js文件
function loadScript(url,callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if(script.readyState) {
script.onreadystatechange = function(){
if(script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
}
} else {
window.onload = function() {
callback();
}
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script); /*添加到页面的时候开始下载,下载的过程执行都不会阻塞其他进程*/
console.log(1);/*1会在新添加的script执行前输出*/
}
如果需要脚本之间按照特定的顺序下载执行可以按照回调的方式一个一个的加载js文件
loadScript("a.js",function(){
loadScript("b.js",function(){});
});
(3)XMLHttpRequest脚本注入 也就是通过xhr对象去下载脚本文件 这种情况的优势是你可以下载到代码但不立即执行,等到你准备好的时候在去执行相应的代码
var xhr = new XMLHttpRequest();
xhr.open("get","a.js",true); /*true表示异步*/
xhr.onreadystatechange = function(){
if(xhr.readyState == 4) {
if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
var script = document.createElement("script");
script.type = "text/javascript";
script.text = xhr.responseText;
document.body.appendChild(script);
}
}
}
xhr.send(null);
推荐的方式: 通过阻塞的方式加载一小部分代码,例如loadSript 然后在去动态的加载剩余的代码
<script type="text/javascript" src="load.js"></script>
<script type="text/javascript">
loadScript("b.js",function(){
console.log("OK");
});
</script>
参考高性能javascript
高性能javascript学习笔记系列(1) -js的加载和执行的更多相关文章
- 高性能javascript学习笔记系列(6) -ajax
参考 高性能javascript javascript高级程序设计 ajax基础 ajax技术的核心是XMLHttpRequest对象(XHR),通过XHR我们就可以实现无需刷新页面就能从服务器端读 ...
- 高性能javascript学习笔记系列(5) -快速响应的用户界面和编程实践
参考高性能javascript 理解浏览器UI线程 用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程 UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 ...
- 高性能javascript学习笔记系列(4) -算法和流程控制
参考高性能javascript for in 循环 使用它可以遍历对象的属性名,但是每次的操作都会搜索实例或者原型的属性 导致使用for in 进行遍历会产生更多的开销 书中提到不要使用for in ...
- 高性能javascript学习笔记系列(2)-数据存取
参考 高性能javascript Tom大叔深入理解javascript系列 相关概念 1.执行上下文 当控制器转到ecmascript可执行代码的时候,就会进入一个执行上下文,执行上下文是以堆栈 ...
- 高性能javascript学习笔记系列(3) -DOM编程
参考 高性能javascript 文档对象模型(DOM)是独立于语言的,用于操作XML和HTML文档的程序接口API,在浏览器中主要通过DOM提供的API与HTML进行交互,浏览器通常会把DOM和ja ...
- JS 动态加载脚本 执行回调
JS 动态加载脚本 执行回调 关于在javascript里面加载其它的js文件的问题可能很多人都遇到过,但很多朋友可能并不知道怎么判断我们要加载的js文件是否加载完成,如果没有加载完成我们就调用文件 ...
- JS的加载和执行
从JS的加载和执行谈性能优化 ---高性能JS读后感(第一章) 从脚本的"霸道"说起,随着浏览器的进步,js越来越听话了,所以,我们先说说以前的浏览器是怎么加载js的,以及js如何 ...
- 性能优化-css,js的加载与执行
前端性能优化 css,js的加载与执行 javascript是单线程的 一个网站在浏览器是如何进行渲染的呢? html页面加载渲染的过程 html渲染过程的一些特点 顺序执行,并发加载 词法分析 并发 ...
- selenium学习笔记——driver.get(url) 页面加载时间太长
# 两个同时设置才行 # 实现效果:加载状态停止,进行代码下一步操作 driver.set_page_load_timeout(10) driver.set_script_timeout(10) # ...
随机推荐
- Java多线程系列--“JUC线程池”04之 线程池原理(三)
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3509960.html 本章介绍线程池的生命周期.在"Java多线程系列--“基础篇”01之 基 ...
- Hadoop官方文档翻译——MapReduce Tutorial
MapReduce Tutorial(个人指导) Purpose(目的) Prerequisites(必备条件) Overview(综述) Inputs and Outputs(输入输出) MapRe ...
- 一道 google曾出过的笔试题:编程实现对数学一元多项式的相加和相乘操作(1)
数学中一元n次多项式可表示成如下的形式: Pn(x)=p0+p1x+p2x^2+…+pnx^n (最多有 n+1 项,n +1 个系数唯一确定她) (1)请设计一套接口用以表示和操 ...
- Android Studio项目转Eclipse项目
Android Studio项目的目录结构和Eclipse项目不同.如何转换? 以FloatingAction 项目为例:实现向上滑动隐藏悬浮按钮,向上滑动显示悬浮按钮. GitHub 地址:http ...
- Oracle数据库常见的误操作恢复方法(上)
实验环境:Linux6.4 + Oracle 11g 面向读者:Oracle开发维护人员 概要: 1.误操作drop了emp表 2.误操作delete了emp表 3.误操作delete了emp表的部分 ...
- 报告一个IE很奇葩的滚动条问题——百分比计算宽度为浮点数时的滚动条显示异常
起因: 做项目的时候做了一个表格内容超过DIV容器自动横向滚动处理.别的浏览器都正常:但是在IE下面明明表格table和容器DIV宽度一致但是却出现了滚动条.如图 然后本人做实验找了半天原因终于是找到 ...
- [译]学习IPython进行交互式计算和数据可视化(四)
第三章 使用Python进行数字计算 尽管IPython强大的shell和扩展后的控制台能被任何Python程序员使用,但是这个工具最初是科学奖为科学家设计的.它的主要设计目标就是为使用Python进 ...
- CSS常用选择器名
一.页面结构划分 box 盒子wrap 包裹container 容器 header 头部main 主要区域footer 底部 content 内容区域banner 横幅广告区域menu 菜单 二.模块 ...
- 选择排序---直接选择排序算法(Javascript版)
在要排序的一组数列中,选出最小(或者最大)的一个数与第1个位置的数交换:然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个 ...
- 实现了IEnumerable接口的GetEnumerator 即可使用 Foreach遍历,返回一个IEnumerator对象
#region 程序集 mscorlib.dll, v4.0.0.0 // C:\Program Files (x86)\Reference Assemblies\Microsoft\Framewor ...