前端性能优化一:性能指标javascript
现代前端程序中,前端资源文件中占绝大部分byte的是javascript。一个现代前端应用程序,javascript包中可能包含如下几种类型的js文件html
你发送到浏览器的js文件越大,你的加载时间就会越慢前端
他们可能用了相同的时间下载,可是处理这两种文件的过程会带来很是不一样的性能消耗vue
一个jpeg图片文件下载之后须要被解码,栅格图像,而后渲染到屏幕上。而一个js文件可能花费了相同的时间下载了一个相同大小的js文件,接下来交由js引擎处理咱们的js。java
从拿到你写的js文件开始。js引擎解析(parse)你的源代码将他转换成抽象语法树(AST)。基于这个AST,解析器开始生成字节码(bytecode)。ok到这里引擎已经能够开始执行你的代码了。react
可是当你的其中一块代码被执行的次数愈来愈多,js引擎会将这一块代码标记成热代码(hot),为了让这一块代码执行的更快,以前生成的字节码(bytecode)会传入优化器,优化器会根据以前运行的代码状况,作一些假设(好比根据以前的运行状况猜想变量的类型都是Number类型),而后根据这些假设进行优化。若是由于以前优化时候作出的假设错误,性能优化器会将代码去优化,从新执行以前未优化的bytecode。webpack
这个是一个大体的流程每一个浏览器的实现方式都相似可是会有一些区别,在V8中解析器叫作(Ignition),优化器叫作(TurboFan),Ignition生成完bytecode以后,在执行过程当中收集执行数据(profiling data),用于在代码变hot以后,交由TurboFan,根据执行数据作出假设进行优化.ios
SpiderMonkey用于Firefox的内核执行方式有一些不一样,他有两个优化器,一个Baseline优化器生成轻度的优化代码,IonMonkey优化器生成高度优化的代码.若是优化器作的假设失败,去优化为Baseline轻度优化代码。 Chakra用于Edge的内核他也是两个优化器,分别为SimpleJIT生成轻度优化代码,FullJIT生成高度优化代码。JavaScriptCore用于Safari和ReactNative的内核有三个优化器。web
解析器能够快速的生成字节码(bytecode),可是bytecode的执行效率却不是很高,另一方面优化器能够生成用于高效执行的机器码,可是生成优化代码所须要的时间也会高一些。这是一个快速获得执行代码(bytecode)和获得可以快速执行的代码(机器码)的一个取舍,另外机器码所占用的内存也会高一些。因此优化器在选择是否要优化一块代码的时候是很是复杂的意见事情,举个例子vuex
fun()
复制代码
假设咱们的前端程序只有这一行代码,那么可想而知,咱们快速的获得能够执行的bytecode比获得高度优化的机器码所花费的时间要有意义的多。
for(let i = 0;i< 100000;i++){
fun()
}
复制代码
一样的一个方法被放在循环里面被重复执行10万次,那么虽然花费了多一点的时间获得机器码,可是由于要执行不少次,因此获得高度优化的机器码所花费的时间也是划算的。
由于以前提到太高度优化的代码所占用的内存也会高一些,那么考虑是否优化某块代码的时候还要考虑运行终端的内存状况,好比在内存很是小的手机上,优化器选择是否优化某块代码就要谨慎的多。
那么若是跑在服务器端Node.js的环境下,由于程序可能只启动一次,可是须要运行屡次,那么第一次启动的时间对程序来讲意义不是很大,相反的可以快速执行的机器码就要有意义的多。
因此一些引擎选择多个优化器,能够更好的控制这个复杂度。
可是总的来讲,这些引擎执行js代码的顺序是同样的,架构也是同样的(SoureCode->AST->Bytecode->MachineCode).
说了这么多就是想说js是如今浏览器中最消耗性能的资源,不一样设备和网络环境所带来的性能表现差别也比较大,终端性能越差,固然花的时间对应的也越多,因此发送到浏览器的js文件越小越好。
利用code split将文件拆分,给他们排出优先级,只加载首屏用户须要的js文件,将其余的js文件懒加载,可以较大程度的减小js文件的大小,可以显著的加快loading时间和TTI的时间。
开发者若是没有这个意识,有很大的可能性将一整个工具库打包到一个bundles里面,可是其实你只用了其中一个方法。
tree sharking如今应该不是什么新鲜的单词了,最先由Rollup提出,后来由于webpack的影响力,发扬光大。
code split和tree sharking这里就不展开介绍了,网上已经有不少好的文章。
这一条主要针对vue或者react的universal程序,也就是说你的程序同时在服务器端和客户端依赖了vue或者react框架,先利用服务器端渲染进行html渲染,而后在转为前端路由。这的确很酷,可是你有没有想过,当你已经在服务器端利用vue或者react的服务器端渲染将html生成了,在一些简单的页面其实你是不须要将vue或者react发送到客户端的。Netfix:是一个美国的视频网站,他们依赖了react,他们的开发者尝试在注册页去掉了前端的react,只在服务器端渲染的时候依赖react渲染html,也就是说将react也按需加载了,将注册页的js大小减小了70%,大大的提升了性能。
利用tree sharking和code split将你的js文件减小了之后,咱们始终仍是有js文件的。那咱们还能作什么呢?
当浏览器得到服务器发送的HTML标签,开始从上到下进行解析。碰到js标签,就会发送js请求,而后进行解析,编译,执行。在进行这一系列动做的时候主线程都是堵塞的。可是如今咱们有了更多的选择来引入一个js文件。
<link rel="preload" href="one.js" as="script">
复制代码
新版本的浏览器会在接收到HTML之后,先快速的检查一遍全部的html标签,若是碰到了preload,他会直接去请求这个文件,而后根据as
后面的文件类型,对文件进行处理,若是是js就会解析,编译。那么何时preload的js文件会执行呢?
<script src="one.js" ></script>
复制代码
当你碰到正常的一个script标签或者你能够onload的时候直接让他执行
<link rel="preload" as="script" href="one.js" onload="var script = document.createElement('script'); script.src = this.href; document.body.appendChild(script);">
复制代码
不论哪一种方式,浏览器都能更早的请求js文件,而后执行的时候不须要等待本来都是堵塞主线程的下载,解析,编译。 preload不止能够提早loadjs还能够用于其余类型的文件,能够得到所有的list
<script src="one.js" async></script>
复制代码
若是你须要引入一些第三方的js文件,好比baidu统计之类的第三方库。你可让这个标签打上async属性,当浏览器看到这个属性的时候,这个js就不会堵塞线程。而且在新版本的chrome浏览器里,async标签他的解析,编译都会在子线程里进行,这个对性能是很划算的。
不一样的浏览器不一样版本可以同时发起平行请求的数量不一样,若是http2的标准那么能同时发送的请求就更多了。具体的数量咱们这里就不讨论了。能同时发送的请求越多,意味着咱们能够尽量多的在一个页面不一样组件中,将能够共用的代码的打包到一块儿,也不用花两个阶段去请求js。在打包的时候,尽量的把不怎么改变的文件和可能会发生改变的文件分开来打包,这样就能够充分利用缓存。对于特别大的js打包文件,尽可能能够在同时请求数容许的范围里将文件打包成大于等于50kb一个。
新版本的chrome已经开始支持js文件stream解析,也就是说当你的js文件还没彻底下载完毕的时候,js引擎就能够分块的将js stream放在工做子线程进行解析。可是单个文件必须大于50kb才会使用stream parse,因此将一个大的js包,分开打包为多个50kb的js包,平行请求,chrome会使用stream parse,而且将解析,编译的工做放到子线程进行,这样主线程就能够响应交互请求或者解析HTML。 例如facebook,经过大约292个请求加载了大约6M的压缩过的js包,他利用async和stream parse将子线程的利用率大大的提升。
NOTE:inline Script和缓存中加载的js文件没办法使用stream parse。
js长任务在以前已经介绍过,一直堵塞主线程,致使主线程没法处理用户响应和其余任务的超过50ms的任务,咱们认为是长任务。
那么怎么能够缩短长任务呢?基本的思路就是把任务切片,设置优先级将长任务分块的去执行。 若是你使用的是vue或者react这种响应式的框架。多个组件同时须要渲染,会引起多个组件相继出发init->render->patch,颇有可能出现一个超过50ms的长任务。那你要分析有些组件是否是能够放到后面去渲染(好比不是首屏须要的组件),或者两个组件分开时间去渲染。
在现代浏览器中js文件仍然是加载时最消耗性能的文件。因此利用code split和tree sharking让js文件尽量的小,只加载首屏须要的文件是有效提升性能的办法。另外使用preload和async来得到js。学会分析js包,利用缓存和stream parse也能够有效的提升性能。下一章咱们讨论js另外的一些提升性能的办法。