【序】今天看到一篇文章讲javascript启动性能瓶颈分析的公众文章,觉得不错,在此做一下总结
【疑惑】优化该如何入手?
【解惑】第一步,就是要了解当浏览器获取我们的代码后是如何处理的?
此处以chrome为例,我们知道chrome的js解析部分其实就是使用的V8引擎,因此我们分析V8引擎的运行方式
- 获取js代码
- 解析js代码
- 生成抽象语法树
- 生成字节码
- 动态生成对应平台机器码
- 运行到各个架构端
jscode → AST → ByteCode → 动态生成对应平台机器码
【疑惑】 是什么拖慢了应用启动时间?
【解惑】
通过分析来看绝大部分时间都被浏览器中语法解析与编译时间占用,在mobile开发中更长
【引申】 Chrome Canary是什么?
金丝雀(Canary)版baiChrome是Google推出的一个特du别版Chrome。可以同时安装/运行两个zhiChrome版本,比如,安装Chrome 稳定版/Beta测试版/开发者版本,同时安装使用Chrome金丝雀版本(目前仅支持Windows XP、Vista、Windows 7平台)。
【疑惑】那我们可以通过什么手段去优化代码的解析与编译时间?
【解惑】:
- 加载更小的js包
当我们应用包体已经非常巨大的时候,使用一些现代的打包技巧,譬如代码分割,TreeShaking,Service Workder 缓存等等 - 多使用async/defer,异步加载资源这个技术会允许 HTML 解析器将相应的脚本加载任务分配给专门的 script streaming 线程,从而避免阻塞文档解析。在实践中,我们建议将 <scriptdefer>添加到 <head>块内,这样浏览器引擎就能够尽早地发现需要解析的脚本,然后将其分配给后台线程进行处理。
- 较大的模块放到最上面
V8 推荐尽早加载较大的模块,毕竟我们只有一个 streamer 线程 - 在功能相同的情况下选择代码快最小,性能最优的替代品
例如:我们应该尽可能地选择具有相同功能但是加载地更快的依赖,譬如使用 Preact 或者 Inferno 来代替 React,二者相较于 React 体积更小具有更少的语法解析与编译时间。 - 在引用时尽量使用按需引入
- 如果你的 JavaScript 框架支持 AOT(ahead-of-time)编译模式,那么能够有效地减少解析与编译的时间。Angular 应用就受益于这种模式
- Optimize JS 优化
类似于 V8 这样的 JavaScript 引擎在进行完整的解析之前会对脚本中的大部分函数进行预解析,这主要是考虑到大部分页面中包含的 JavaScript 函数并不会立刻被执行。
预编译能够通过只处理那些浏览器运行所需要的最小函数集合来提升启动时间,不过这种机制在 IIFE 面前却反而降低了效率。尽管引擎希望避免对这些函数进行预处理,但是远不如optimize-js这样的库有作用。optimize-js 会在引擎之前对于脚本进行处理,对于那些立即执行的函数插入圆括号从而保证更快速地执行。这种预处理对于 Browserify, Webpack 生成包体这样包含了大量即刻执行的小模块起到了非常不错的优化效果。尽管这种小技巧并非 V8 所希望使用的,但是在当前阶段不得不引入相应的优化机制。
【引申】:(AOT && JIT)
JIT和AOT
目前,程序主要有两种运行方式:静态编译与动态解释。
静态编译的程序在执行前全部被翻译为机器码,通常将这种类型称为AOT (Ahead of time)即 “提前编译”;
而解释执行的则是一句一句边翻译边运行,通常将这种类型称为JIT(Just-in-time)即“即时编译”。
AOT程序的典型代表是用C/C++开发的应用,它们必须在执行前编译成机器码,而JIT的代表则非常多,如JavaScript、python等。
【引申】:crome的性能调试技巧
打开 Timeline( Performance panel ) > Bottom-Up/Call Tree/Event Log 就会显示出当前网站在语法解析/编译上的时间占比。如果你希望得到更完整的信息,那么可以打开 V8 的 Runtime Call Stats。在 Canary 中,其位于 Timeline 的 Experims > V8 Runtime Call Stats 下 image.png
网友评论