这篇笔记的内容主要涉及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的加载和执行的更多相关文章

  1. 高性能javascript学习笔记系列(6) -ajax

    参考 高性能javascript javascript高级程序设计 ajax基础  ajax技术的核心是XMLHttpRequest对象(XHR),通过XHR我们就可以实现无需刷新页面就能从服务器端读 ...

  2. 高性能javascript学习笔记系列(5) -快速响应的用户界面和编程实践

    参考高性能javascript 理解浏览器UI线程  用于执行javascript和更新用户界面的进程通常被称为浏览器UI线程  UI线程的工作机制可以理解为一个简单的队列系统,队列中的任务按顺序执行 ...

  3. 高性能javascript学习笔记系列(4) -算法和流程控制

    参考高性能javascript for in 循环  使用它可以遍历对象的属性名,但是每次的操作都会搜索实例或者原型的属性 导致使用for in 进行遍历会产生更多的开销 书中提到不要使用for in ...

  4. 高性能javascript学习笔记系列(2)-数据存取

    参考 高性能javascript Tom大叔深入理解javascript系列 相关概念 1.执行上下文   当控制器转到ecmascript可执行代码的时候,就会进入一个执行上下文,执行上下文是以堆栈 ...

  5. 高性能javascript学习笔记系列(3) -DOM编程

    参考 高性能javascript 文档对象模型(DOM)是独立于语言的,用于操作XML和HTML文档的程序接口API,在浏览器中主要通过DOM提供的API与HTML进行交互,浏览器通常会把DOM和ja ...

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

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

  7. JS的加载和执行

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

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

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

  9. selenium学习笔记——driver.get(url) 页面加载时间太长

    # 两个同时设置才行 # 实现效果:加载状态停止,进行代码下一步操作 driver.set_page_load_timeout(10) driver.set_script_timeout(10) # ...

随机推荐

  1. struts2拦截器

    一.自定义拦截器 struts2拦截器类似于servlet过滤器 首先定义一个拦截器这个拦截器实现了Interceptor接口: package cn.orlion.interceptor; impo ...

  2. Zookeeper-Zookeeper leader选举

    在上一篇文章中我们大致浏览了zookeeper的启动过程,并且提到在Zookeeper的启动过程中leader选举是非常重要而且最复杂的一个环节.那么什么是leader选举呢?zookeeper为什么 ...

  3. MemCache分布式缓存的一个bug

    Memcached分布式缓存策略不是由服务器端至支持的,多台服务器之间并不知道彼此的存在.分布式的实现是由客户端代码(Memcached.ClientLibrary)通过缓存key-server映射来 ...

  4. bootstrap-datetimepicker.js学习

    之前项目运用到了这个时间控件,期间bug还是一些.抽个时间,简单地看一下. 先看一下datetimepicker.js的结构 var DateTimePicker = function(element ...

  5. [转载]TFS测试管理

    微软2010年发布的Visual Studio 2010或Visual Studio Test Professional 2010包含一个称为 Microsoft 测试管理器的新应用程序,用于帮助您使 ...

  6. js断点调试心得

    虽然网上已经有多的数不清的调试教程了,但仍然没有发现哪篇文章写的通俗易懂,索性自己尝试写写自己的一些使用习惯或者说是心得,希望对那些还不是很懂得使用断点调试的孩子有一些帮助(大神请无视~). 1.断点 ...

  7. 经验分享:CSS浮动(float,clear)通俗讲解

    很早以前就接触过CSS,但对于浮动始终非常迷惑,可能是自身理解能力差,也可能是没能遇到一篇通俗的教程. 前些天小菜终于搞懂了浮动的基本原理,迫不及待的分享给大家. 写在前面的话: 由于CSS内容比较多 ...

  8. PHP 中的Closure

    PHP 中的Closure Closure,匿名函数,又称为Anonymous functions,是php5.3的时候引入的.匿名函数就是没有定义名字的函数.这点牢牢记住就能理解匿名函数的定义了. ...

  9. HT for Web自定义3D模型的WebGL应用

    有不少朋友询问<HTML5 Web 客户端五种离线存储方式汇总>文章例子的3D表计模型是如何生成的,这个例子是通过导入3dmax设计好的表计模型,然后通过obj格式导入到HT for We ...

  10. Java魔法堂:解读基于Type Erasure的泛型

    一.前言 还记得JDK1.4时遍历列表的辛酸吗?我可是记忆犹新啊,那时因项目需求我从C#转身到Java的怀抱,然后因JDK1.4少了泛型这样语法糖(还有自动装箱.拆箱),让我受尽苦头啊,不过也反映自己 ...