这篇笔记的内容主要涉及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. 深入理解CSS定位中的偏移

    × 目录 [1]定位 [2]包含块 [3]偏移属性[4]绝对定位[5]格式化 [6]auto 前面的话 CSS有三种基本的定位机制:普通流.浮动和绝对定位.利用定位,可以准确地定义元素框相对于其正常位 ...

  2. [Qt5] How to connect c++ with QML

    Qt5处于过度阶段,架构繁琐,学习成本不低.尤其是UI代码竟然被重写,变了天. Qt中的c++可能是连接OPENCV与QML的一个不错的桥梁,在此学习这部分实用的技术. Reference: http ...

  3. java io系列16之 PrintStream(打印输出流)详解

    本章介绍PrintStream以及 它与DataOutputStream的区别.我们先对PrintStream有个大致认识,然后再深入学习它的源码,最后通过示例加深对它的了解. 转载请注明出处:htt ...

  4. WPF The Hard Way

    WPF The Hard Way Windows Presentation Foundation (WPF) 是微软下一代显示系统,用于生成能带给用户震撼视觉体验的 Windows 客户端应用程序.  ...

  5. $("#id").val()取值textarea是""

    今天取值的时候,判断是null可以通过,证明不是null,明明是空的. 判断是''通过,证明取出来的是''空字符串.

  6. Java魔法堂:finalize函数

    一.finalize与GC 在GC第一次进行可达性分析时会将不可达而且该对象所属类重写finalize方法和finalize方法重未被执行过的对象追加到F-Queue当中,然后JVM会自动开启一个低优 ...

  7. 实现iOS图片等资源文件的热更新化(四): 一个最小化的补丁更新逻辑

    简介 以前写过一个补丁更新的文章,此处会做一个更精简的最小化实现,以便于集成.为了使逻辑具有通用性,将剥离对AFNetworking和ReativeCocoa的依赖.原来的文章,可以先看这里: htt ...

  8. ADB am 命令详细参数

    usage: am [subcommand] [options] usage: am start [-D] [-W] [-P <FILE>] [--start-profiler <F ...

  9. iOS——使用StroryBoard页面跳转及传值

    之前在网上搜iOS的页面跳转大多都是按回以前的那种xib的形式,但鄙人是使用storyboard的.这篇就只介绍利用storyboard进行页面跳转与传值. 新建页面 iOS的程序也是使用了MVC的思 ...

  10. iOS 阶段学习第七天笔记(函数、递归)

     iOS学习(C语言)知识点整理笔记 一.函数 1)概念:具有特定功能的代码块的封装 2)函数的定义: 函数类型+函数名(形参列表) 函数类型 函数名(形参类型1  形参名1,形参类型2   形参名2 ...