一个页面从服务器访问回来后,浏览器拿到页面源代码,之后做的一些事情(1-5是交给GUI渲染线程完成的)
- 生成 DOM TREE =>对HTML的处理
- 基于HTTP获取的是流文件(进制编码)
- 把进制编码编译为具体的字符
- 词法解析
- 生层具体的节点(元素节点/文本节点...)
- 按照相互的嵌套关系生成一个DOM树(节点树)
- 生成 DOM TREE =>对HTML的处理
- 生成 CSSOM TREE =>对CSS的处理
- 生成 RENDER TREE (渲染树)
- 布局/回流/重排 Layout
- 按照渲染树计算出每一个元素在视口中的位置和大小
- 布局/回流/重排 Layout
- 分层
- 按照计算出来的样式进行分层
- 单独计算每一层的绘制列表(具体怎么绘制)
- 分层
- 6.绘制/重绘 Painting
- 把生成的绘制列表提交给“合成线程”
- “合成线程”进行我们最后的绘制,呈现在浏览器的页面上
真正的同时做多件事情必须依赖多线程(浏览器是多线程的)
- JS本身是单线程的 (因为浏览器本身只分配一个线程 “GUI渲染线程” 运行JS代码),从本质来讲是不能同时做多件事情的,但有同步跟异步
- 同步:一件事情完成,再去做下一件事情
- 异步:上一件事情没有完成(做一些特殊处理[async await等]),下一件事情继续执行 (但不是JS可以同时处理两个事情)
- 浏览器生层DOM TREE/CSSOM TREE...的过程也是单线程的“GUI渲染线程”(配合浏览器的多线程去完成一些事情,例如:资源请求就是利用的就是浏览器的HTTP网络线程去做的)
浏览器具体的解析过程 “GUI渲染线程”:
- 自上而下解析完所有的HTML标签/各种节点后,DOM TREE就生成了
- 但是过程中还会遇到一些比较特殊的
- link href='xxx.css' 外链式- >浏览器会分配一个新的HTTP网络线程去加载资源文件,不会阻碍DOM树的渲染
-
<style> ... </style> 内嵌式- >不用去请求新的资源文件了,但是此时样式还没有处理,浏览器会做一个记录,它会等待所有的CSS资源加载回来之后,按照先后顺序依次渲染CSS,从而生成CSSOM树
-** @import 'xxx.css'** 导入式 - >也是分配网络HTTP线程去加载资源文件,但是此时GUI渲染线程会被阻塞掉[阻碍DOM树渲染](只有等资源加载回来,才会继续渲染DOM) - 遇到script 内嵌js代码的 - >立即执行JS(阻碍DOM TREE的渲染)
- 遇到script 外链js代码的(src)- > 阻碍DOM TREE的渲染,同时分配一个HTTP线程去加载资源文件,加载回来后立即执行JS,如果JS中没有采用异步,直接获取DOM元素,而DOM元素此时还没有渲染,JS是获取不到的
- 遇到img - >某些老旧版本浏览器会阻碍DOM渲染,新版浏览器虽然不会阻碍DOM渲染,但是图片资源的请求会占用HTTP线程(浏览器同时只能开6~7个HTTP线程,这样图片/音视频资源加载本来就会慢一些,会影响其他资源link/script等的加载)
可做的性能优化
- link放到HEAD中(尽可能提前去加载资源文件,这样等DOM树渲染完,资源可能也加载回来了); 当代浏览器的机制越发完善,Webkit浏览器预测解析:chrome的预加载扫描器html-preload-scanner通过扫描节点中的 “src” , “link”等属性,找到外部连接资源后进行预加载,避免了资源加载的等待时间,同样实现了提前加载以及加载和执行分离
- 把script放到页面的底部(先渲染DOM TREE,再执行JS,也可以获取到DOM元素了),也可以基于事件DOMContentLoaded/load等到结构加载完再去获取DOM元素
- 当代前端开发,开始只把首屏的结构/内容写出来,渲染只是首屏的,当首屏加载完,页面滚动的时候,再基于JS创建其他屏幕的结构和内容 => 骨架屏/SSR =>客户端骨架屏,开始首屏结构都没有,只有一个loading或者占位图而已...