浏览器
浏览器解析HTML过程
DOMContentLoaded
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载
load
当整个页面及所有依赖资源如样式表和图片都已完成加载时,将触发load事件
case1:无JS,无CSS
case2:无JS,有CSS
在这个情况的时候,CSS不会阻碍DOM的解析,但是会阻碍DOM的渲染
case3:有JS,有CSS
- JS脚本会阻碍DOM解析
- CSS会阻碍JS脚本执行,因此也会间接阻碍DOM解析
- CSS必定会阻碍DOM渲染
async defer 区别
DOMContentLoaded事件不一定就是DOM解析完成的事件
async
是异步执行,此处的DOMcontentLoaded
事件触发只关注DOM
是否被解析完,与async
无关。异步下载完毕后就马上执行,因此可能会阻塞DOM
解析,浏览器并不能保证多个async
脚本按顺序执行。总的来说执行时机一定在load
事件之前,可能在DOMcontentLoaded
事件之前或者之后defer
是延迟执行,此处的DOMcontentLoaded
事件触发是要defer
脚本执行完毕后才会触发,defer
的执行时机是在DOMcontentLoaded
事件之前,换个一个角度说就是defer
会等待真正的DOM
解析完之后执行,所以它不会阻碍DOM
解析,并且多个defer
脚本执行可以按顺序执行- 区别就在于DOMContentLoaded事件的触发时间点
cookie sessionStorage localStorage 区别
共同点是都存储在客户端,且都是同源的
- 生命周期不同:
cookie
一般由服务器生成,可以设置失效时间,如果是在自己浏览器生成的话,默认是关闭后会失效;sessionStorage
关闭标签页或者浏览器就会失效;localStorage
除非被清除,否则一直是有效的 - 作用域不同:
cookie
和localStorage
在所有的同源窗口都是共享的,而sessionStorage
不能共享 - 大小不同:
cookie
大小一般不超过4k,sessionStorage
和localStorage
可以到5M - 是否与服务端通信:
cookie
数据始终在同源的请求中携带,再浏览器和服务器之间来回传递;而sessionStorage
和localStorage
不会自动把数据发给浏览器,仅在本地保存
cookie session 区别
- 存储位置不同:
cookie
存储在客户端,session
存储在服务端,因此session
也会更安全一些 - 存储大小不同:
cookie
大小一般不超过4k,而session
要大得多,但是session
太多太大的话会造成服务器的压力 - 有效期不同:
cookie
一般有效期较长,session
有效期一般较短
session token 区别
- 服务端是有记录状态:
session
记录了客户端和服务器的会话状态,而token
是令牌,是一种访问资源的一种凭证,它可以使得服务器无状态化,不需要存储会话信息,它用计算来代替了储存 token
的安全性更好一些,因为每个token
还有签名,可以防止监听,而session
需要依靠链路层来保证安全性了
重排和重绘
解释
重排和重绘其实是发生在浏览器渲染路径上的两个节点。浏览器最关键的渲染路径就是
DOM
和CSSOM
生成渲染树(render tree
),然后渲染树render tree
通过布局(layout
)来确定页面所有的内容的大小与位置,确定布局后,就进行paint
操作,也就是把像素绘制到屏幕上其中重排就是当元素的位置和大小发生变动的时候,浏览器就要重新执行布局这个步骤,来重新确定页面所有内容的大小和位置,然后再重新绘制到屏幕上,所以重排一定会导致重绘
其中重绘就是当元素的位置和大小没有发生变动,而仅仅是样式发生变动的时候,浏览器就会跳过布局(
layout
)步骤而直接执行绘制(paint
)这个步骤,所以重绘不一定会导致重排
如何减少重排和重绘?
- 集中式改变样式,不要频繁的进行样式的改变
- 分离读和写操作,读操作的时候可以用变量进行缓存
- 尽量少使用
dispaly:none
,可以使用visibility:hidden
代替,dispaly:none
会造成重排和重绘,visibility:hidden
只会造成重绘。 - 不要使用
Table
布局,因为一个小小的操作,可能就会造成整个表格的重排或者重绘 - 批量修改元素时,可以先让元素脱离文档流,等修改完毕后,再放入文档流
白屏与首屏
白屏
白屏时间是指浏览器开始显示内容的时间。一般认为是解析完<head>
的时刻为白屏时间
计算:
在 <head>
标签前的<script>
标签内加入代码:
console.log(new Date().getTime() - performance.timing.navigationStart)
首屏
首屏时间是指用户打开网站开始,到浏览器首屏内容渲染完成的时间
计算:
再<body>
下方的<script>
标签加入代码:
console.log(new Date().getTime() - performance.timing.navigationStart)
优化
- 减少HTTP请求次数,减少HTTP请求大小
- 合并压缩文件(gzip)
- 采用svg图片或者字体图标
- 尽量将 CSS 放文件头部,JS 文件放在底部,也可以是用defer加载JS
- 采用服务端渲染
- CDN
- 资源缓存
- 图片懒加载
客户端渲染(CSR)与服务端渲染(SSR)
客户端渲染(Client-side rendering)
从服务端获取HTML
文件,服务端不会对文件进行任何的处理,客户端需要自行下载和执行javascript
,生成DOM
然后再渲染
优点
前后端分离,减少服务端压力,局部刷新
缺点
不利于SEO,首屏渲染慢
服务端渲染(Server-side rendering)
可能实际上很多网站是处于经济效益的考虑,主要是SEO和首屏
服务端返回渲染好的HTML
文件,客户端不需要做任何操作,直接呈现就可以
优点
利于SEO,首屏渲染速度快
缺点
服务端压力大,前后端不分离不容易维护,前端页面更改,后端也要更改
浏览器有哪些进程?
- 浏览器进程(Browser Process),这个是浏览器的主进程,主要负责包括地址栏、前进后退按钮、文件存取等
- 渲染进程(Renderer Process),主要负责将 HTML, CSS, JavaScript 转换为用户可交互的网页,排版引擎 Blink 和 JavaScript 引擎 V8 就运行在渲染进程
- 网络进程(NetWork Process),主要负责页面的网络资源加载
- 插件进程(Plugin Process),主要负责网站使用的所有插件,每个插件一个进程,单独隔离出是为了防止插件挂了影响用户
- GPU(GPU Process),主要负责 UI 渲染
js在V8中的执行过程
总流程
![](http://songnian.gitee.io/imgs/imgs/js%E5%9C%A8v8%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B.png)
JS TO AST
AST Parser参考网站,有Token也有AST
词法分析
所谓词法分析就是将JS
代码中的每个字符串,也就是将每个词解析出特定的意义,每个Token
主要由一对对的type
和value
组成。例如var name = 'songnian'
生成的Token
如下:
[
// type表示属性/类型 value表示值
{
"type": "Keyword",
"value": "var"
},
{
"type": "Identifier",
"value": "name"
},
{
"type": "Punctuator",
"value": "="
},
{
"type": "Identifier",
"value": "songnian"
},
{
"type": "Punctuator",
"value": ";"
}
]
语法分析
AST:Abstract Syntax Tree 即抽象语法树
语法分析就是将生成好的Token
转化成AST
树,如果源码符合语法规则,这一步就会顺利完成。但如果源码存在语法错误,这一步就会终止,并抛出一个“语法错误”。最著名的是bable
,它可以将ES6
原码转化成AST
,然后再将这个ES6 AST
转成ES5 AST
,最后利用ES5 AST
转成js
执行代码。例如var name = 'songnian'
生成的AST
如下:
{
"type": "Program",
"body": [
{
"type": "VariableDeclaration",
"declarations": [
{
"type": "VariableDeclarator",
"id": {
"type": "Identifier",
"name": "name"
},
"init": {
"type": "Identifier",
"name": "songnian"
}
}
],
"kind": "var"
}
],
"sourceType": "script"
}
生成字节码
字节码由解释器
Ignition
生成,它是介于AST
和机器码之间的一种代码,它所占用的空间比机器码小很多
缓存机器码
在以前的v8
引擎中,都是直接将AST
转化成机器码然后给CPU
执行(因为cpu
只认识机器码),但是这样会带来一个问题,就是时间问题,因为每次执行都需要重新编译一次。基于此,Google
团队提出了缓存机器码的解决方案,其实就是空间换时间,浏览器运行的时候存放在内存,浏览器关闭后存放在磁盘,但是这样又有一个问题,浏览器运行的时候机器码占用内存很大,非常考验电脑的性能,要是换一个内存小一点的电脑可能就无法支撑了
惰性编译
为了解决上一个问题,Google
团队又提出了惰性编译:V8
启动的时候只编译和缓存全局作用域的代码,而函数作用域中的代码,会在调用的时候去编译,同样函数内部编译后的代码一样不会被缓存下来。一开始还行,但是后来又发现了一个问题,对于以前的jQuery
这一些,他们的插件都是通函数去封装的,那么如果一个插件过大就会引起编译的时间很慢,这个惰性编译也就相当于把路给封死了
引入字节码
从全流程上来看,虽然多了一个生成字节码的环节,但是占用空间却能大大减小
编译器
热代码
热代码就是被重复执行多次的代码,在V8
中会有专门的监控模块,来监控同一代码是否多次被调用,如果被多次调用,那么就会被标记为热代码
优化编译器TurboFan
接着热代码继续说,当存在热代码的时候,V8
会借着TurboFan
将为热代码的字节码转为机器码并缓存下来,这样一来,当再次调用热代码时,就不在需要将字节码转机器码,当然热代码相对来说还是少部分的,所以缓存也并不会占用太大内存,并且提升了执行效率,同样此处也是牺牲空间换时间
反优化
当热代码在某次执行的时候,突然某一个属性被修改了,则此时优化编译器TurboFan
会执行反优化,其实就是将热代码退回到AST
那一步,这个时候解释器Ignition
会重新解释执行被修改的代码