编写一个页面所需要的技术栈:
- HTML&CSS 「less/sass/stylus...」
- 按照W3C规范去编写代码,浏览器本身也是按照W3C的规范起编译解析我们的代码,最后渲染出我们想要的效果!
- javaspcript 「es6_/XMLHttpRequest/veu/react...」 开发者按照ECMAScript-262规范去开发代码,浏览器也是按照这个规范来解析
浏览器内核:
谷歌:blink(webkit内核的分支)
Safari:webkit
火狐:Gecko
欧朋:Presto(后期版本也改为webkit内核)
IE:Trident
Edge:chromemui
国产浏览器: 之前用的Trident内核,现在用webkit内核 ...
手机端浏览器基本上都是webkit内核的
浏览器基于自己的内核(渲染引擎 webkit->V8引擎),自上而下,自左而右(有特殊情况)渲染和解析代码,最后在浏览器中绘制出对应的页面和效果!!
html5 文档声明方式 :
渲染过程中「基于GUI线程渲染」:
- 遇到,浏览器后单独分配一个线程「HTTP网路线程」去服务器获取资源文件信息,此时GUI继续向下渲染-->异步操作
注:但是不同浏览器可以允许同时并发的HTTP请求次数,一般是5-7次
-
遇到style标签,无需分配线程去获取资源文件,只需要等待DOM TREE 生层后,其他样式资源也获取后,按照顺序依次渲染即可!!
-
遇到style标签@import"xxx.css";阻碍GUI的渲染,需要等待服务器返回对应样式资源后,GUI才可以继续向下渲染!! (一般在less中用)
-
遇到script src="xxx.js",也会阻碍GUI的渲染,虽然分配了一个新的HTTP线程去服务器获取JS文件,但是文件没有获取之前,GUI是不进行渲染的!!只有获取到JS代码,并且把它执行完,CUI才会继续渲染---->同步操作
所以把script放在 head中,经常是获取不到DOM元素的 ** 解决办法:**
@1 把script放在body最底部,这样是等到DOM TREE生成之后才执行的,此时可以获取DOM元素「最推荐的方法」
@2 也可以基于window.onload(所有资源加载完成) 或者 DOMContentLoaded(DOM TREE生成之后就可以)事件去监听
@3 如果是导入外部的JS资源,还可以基于async 或者 defer属性做延迟异步处理 !!
async:分配新的线程去获取JS,此时GUI继续向下渲染「改为异步操作」;当JS资源获取后,立即停止GUI渲染,先把获取的JS执行,执行完成后,再继续GUI渲染!!
defer:分配新的线程去获取JS,此时GUI继续向下渲染;等待GUI渲染完以后,而且所有设置defer的资源都获取到了,按照之前导入的前后顺序依次执行代码(和linkl类似)
如果导入的JS资源不存在依赖关系,可以使用async;但凡需要依赖关系,则使用defer;
- 遇到img src="xxx.png"图片标签也是分配新的线程去获取图片文件,GUI继续渲染=》异步操作
过程
1.生成DOM TREE
GUI渲染中,遇到link标签,img标签...等,都分配HTTP线程去获取资源,GUI继续渲染;所以在资源获取到之前,首先会吧当前页面中的HTML结构规划好,规划好的HTML结构就是“DOM TREE”!!
2.生成 CSSOM TREE
DOM TREE生成后,等待请求的样式资源获取到,GUI渲染线程开始渲染个解析CSS代码「重点:等待所有样式资源都获取到之后,按照之前的导入顺序依次渲染」
3.生成RENDER TREE
把DOM TREE 和 CSSOM TREE 合并在一起, 生成RENDER TREE「渲染树中包含了每个节点的样式及节点之间的嵌套关系」
元素节点是1
4.浏览器按照RENDER TREE 进行渲染
4.1 layout 布局排列
根据当前浏览器视口的大小,计算出每一个节点在视口中的位置等
4.2 分层
把节点根据对应的样式,放在指定的文档流中「创造新文档流的样式:float,position,transfrom...」
并且规划出每一层具体的绘制步骤和要绘制的样式
4.3 Painting绘制
按照之前规划好的方式一层层进行绘制,最后呈现出页面的样式和效果
根据浏览器渲染的机制,我们在每一个核心步骤中去做一些优化,以此来加快页面渲染的速度,缩短页面首次打开的时间「或者是减少页面白屏等待时间」=>CRP(思想)「关键渲染路径」优化方案
------优化首次渲染时间
@1 CSS代码较少的情况下,使用“内嵌式”「尤其式移动端」,代码较多的使用“外链式”「尽可能把样式文件合并压缩为一个」;非必要,绝对不使用“@import导入式”样式,因为它会阻碍GUI渲染!!;
@2 JS代码都放在页面的底部「可以设置async|defer」;
@3图片一定要做“懒加载”:第一次渲染页面不去获取真实的图片资源,加快页面的渲染,第一次渲染完,再把可视窗口中出现的图片做懒加载!! 目的:让GUI不阻碍页面的加载
@4 减少HTML的层级嵌套,一定要是用语义化标签....这样可以加快DOM TREE的构建!!
@5 CSS选择器结构不要太深(前缀不要太多)「扩展:雅虎CSS36条优化准则」...加快CSSOM TREE的构建!!
@6 前端骨架屏:
- 在页面真实的内容渲染之前,先给一个Loading的效果(一般都用灰白框框站位)!!「提高人性化的体验」
- 开始最好只渲染首屏内容「ps:可以交给服务器来渲染」
- 后期向下划动,再渲染其他屏幕的内容
首次渲染完成之后,页面运行时的性能优化
俗话说:操作DOM非常的消耗性能==>因为操作DOM会导致夜页面的回流(重排)和重绘
回流(重排):当某个节点的位置或者大小发生改变(新增或者删除节点,调整节点的位置以及大小等),或者浏览器的视口大小发生改变,浏览器都需要重新计算每一个节点在适口中的布局位置(或者只调整节点所在文档流中的位置),也就是重新layout,我们把这个过程操作称之为“重排(回流)”
重绘:节点的位置和大小不变,知识修改了节点的背景颜色,文字等样式,此时只需要重新绘制这些更改的样式即可
- 第一次渲染页面必然会引发一次layout和painting
- 触发重排必定会引起重绘
所以操作DOM的性能优化,核心在于如何减少页面的重排次数!!
@1 告别直接操作DOM,基于数据驱动试图的渲染「框架:vue,react,Angular...」
@2 分离“读写”「把设置样式和获取样式的代码分开」
浏览器渲染队列机制:
当前上下文当中,遇到修改元素样式的代码,浏览器并咩有立即渲染,而是把其放在“渲染队列中”,如果再遇到其他修改样式的代码,也是一样放在“渲染队列中”...直到当前上下文执行结束,才会把“渲染队列中”的操作统一执行一次,引发一次重排!!
刷新渲染队列:代码执行过程当中,遇到获取元素样式的操作,会刷新渲染队列「立即把现有渲染队列中的操作处理一次,引发一次冲重排」
@1这样的写法是错误的「引发两次重排」
` box.style.top=box.offSetTop+10+"px"
box.style.left=box.offSetLeft+10+"px" `
@2正确写法
`let t=box.offSetTop,
l=box.offSetLeft
box.style.top=t+10+"px" box.style.left=l+10+"px"
` @3 样式集中修改
`.active{
width:100px;
height:100px;
margin:20px auto;
}
box.className = "active";
box.style.cssText = "width:100px;height:100px;"
`
@4 使用CSS3中的transform 修改样式,这种爆炸,浏览器内部开启了硬件加速「不会发生重排」 即便不使用transform,我们也尽可能修改脱离文档流的元素的样式「这样的元素在一个新的平面上,后期重排也只是对这个平面中的元素进行重新排列...」
@5批量新增元素
引发10次重排的写法
`for(let i=0;i<10;i++){
let div=document.creatElement("div");
document.body.appendChild(div)
}`
引发一次重排的写法
- 拼接字符串的问题:innerHTML+=会把之前的内容当作字符串获取到,和新的字符串拼接起来,最后整体插入容器中,原先的内容都会消失
2.文档碎片:原来存储DOM元素的容器 引发一次重排
`let frag=document.creatElementFragment();
for(let i=0;i<10;i++){
let div=document.creatElement("div");
frag.appendChild(div)
}
document.body.appendChild(frag);```