一. 浏览器内核
浏览器内核大致可以分为两部分:
渲染引擎和JS 引擎。由于JS 引擎越来越独立,所以现在说的浏览器内核就多指渲染引擎了。渲染引擎主要用来请求网络页面资源、对资源进行解析、排版后呈现给用户。
| 浏览器/RunTime | 内核(渲染引擎) | JavaScript 引擎 |
|---|---|---|
| Chrome | Blink(28~) Webkit(Chrome 27) | V8 |
| FireFox | Gecko | SpiderMonkey |
| Safari | Webkit | JavaScriptCore |
| Edge | EdgeHTML | Chakra(For JavaScript) |
| IE | Trident | Chakra(For JScript) |
| Opera | Presto->blink | Linear A(4.0-6.1)/ Linear B(7.0-9.2)/ Futhark(9.5-10.2)/ Carakan(10.5-) |
| Node.js | - | V8 |
二. 浏览器的主要组成部分
- 用户界面:包括地址栏,前进、后退、刷新、书签等按钮
- 浏览器引擎:在用户界面和呈现引擎之间传送指令
- 渲染引擎:用来绘制请求的内容
- 网络:用来完成网络调用,例如http请求,它具有平台无关的接口,可以在不同平台上工作
- JS 引擎:用来解析并执行 JS 代码
- 用户界面后端: 用于绘制基本的窗口小部件
- 数据储存: 属于持久层,浏览器在硬盘中保存类似cookie的各种数据,HTML5定义了web database技术,这是一种轻量级完整的客户端存储技术
三. 浏览器解析代码
(1) 渲染引擎解析 HTML
逐行解析,浏览器的渲染引擎会把 HTML 文档解析并转换成 DOM 节点。
- 将 HTML 解析成许多 Tokens
- 将 Tokens 解析成 object
- 将 object 组合成一个 DOM 树
(2) 解析 CSS
先生成 CSSOM 树,后 DOM 树与 CSSOM 树合并成 render 树,实际上是将 CSSOM 树附着到 DOM 树上。
注意:
渲染引擎从右往左解析选择器!
(3) 解析 JS
JS 引擎用来解析 JS 代码,基本步骤如下:
- 词法分析,先将代码分解成各种基础语法单元,如变量、标识符、关键字、操作符...构成标记流
- 语法分析,将标记流转化为一个抽象语法树 AST
- 代码优化,去除死代码、进行常量合并、函数内联...
- 字节码编译,将 AST 转化为字节码,字节码可以提高代码的执行效率
- 即时编译,为进一步提高代码的执行效率,一些浏览器会进行即时编译,即边编译边执行
三. JS 是单线程的!
Why?
主要与 JS 的用途有关,JS 最初是用来实现用户与浏览器进行交互、操作 DOM 。那基于以上,决定了 JS 得是单线程的!
譬如:
假设 JS 是多线程的,且其中一个线程想要修改一个 DOM 元素,而另一个线程又想要删除该 DOM 元素,那到底要听谁的。所以为了避免复杂性,JS 就被设计为单线程的!
但是:
如果完全是单线程,那效率就有点低下了。且也没有好好利用多核 CPU 的计算能力。于是HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程,但是子线程完全受主线程控制,且不得操作DOM。
四. CSS 加载会阻塞 DOM 么?
- CSS 不会阻塞 DOM 的解析,但会阻塞 DOM 的渲染!
- CSS 会阻塞 JS 执行,但不会阻塞 JS 文件的下载!
因为:
-
DOM树 和 CSSOM树 通常是并行构建的,所以
CSS 加载不会阻塞 DOM 的解析 -
render树是依赖 DOM树和 CSSOM树的,所以
CSS 加载会阻塞 DOM 的渲染。 -
如果 JS 脚本的内容是获取元素的样式,那它就必然依赖
CSS。因为浏览器无法感知 JS 内部到底想干什么,为避免样式获取,就只好等前面所有的样式下载完毕再执行 JS 。但JS文件与CSS文件下载是并行的,CSS文件会在后面的JS文件执行前先加载执行完毕,所以CSS会阻塞后面JS的执行 -
因为 JS 是可以操作 DOM 和 CSS 的,所以如果在修改这些属性的同时渲染界面【即 JS线程与 GUI渲染线程同时进行】就会出现问题。所以,浏览器设置JS线程与GUI渲染线程为互斥的关系!
为了避免白屏,需要提高 CSS的加载速度:
- 使用CDN(CDN会根据你的网络状况,挑选最近的一个具有缓存内容的节点为你提供资源,因此可以减少加载时间)
- 对CSS进行压缩
- 合理使用缓存
- 减少http请求数,合并CSS文件
五.JS 会阻塞 DOM 么?
- JS 会阻塞 DOM 的解析,所以就会阻塞页面的加载 ,所以我们通常要把 JS 文件放在最下面!
上面说过,JS线程 和 GUI渲染线程 是互斥的。所以,当 JS 引擎工作时,GUI 线程会被挂起,且会被保存在一个队列中,等到 JS 引擎执行完毕后,再执行!所以,如果 JS 执行时间过长,就会造成页面的渲染不连贯,导致页面渲染有被阻塞的感觉。