浏览器从输入url到页面呈现的过程做了哪些事情?
- 域名解析:解析输入的域名查找对应的IP地址
1.1查找浏览器自身的DNS缓存
1.2没有找到条目,浏览器会搜索操作系统自身的DNS缓存
1.3如果在DNS缓存也没有找到,那么尝试读取hosts文件
(位于C:\Windows\System32\drivers\etc)
1.4如果hosts文件也没有找到,浏览器就会发起DNS解析请求,直到找到对应的IP地址
复制代码
- TCP请求:
浏览器向web服务器发起TCP连接请求: 发起TCP的3次握手
复制代码
- HTTP请求:
浏览器会通过TCP连接向远程服务器发送HTTP请求
复制代码
- 服务器端响应HTTP请求:
服务器端处理请求,返回html文件
复制代码
- 浏览器解析html文件:
解析html文件,加载js,css,image等静态资源
向服务器端请求下载,会使用多线程去下载,不同浏览器之间线程数不一样
可以用上keep-alive特性,建立一次HTTP连接,连续请求多个静态资源
比如: link标签,发送请求获取css
script标签,发送请求获取js
img标签,发送请求获取image图片
复制代码
- 浏览器根据html和css形成DOM树和渲染树,绘制到浏览器。
- 数据加载完成,断开连接
资源合并与压缩
-
合并:减少HTTP请求的数量
-
压缩:减少请求资源的大小
html压缩:压缩在文本文件中有意义,但是在html中不显示的字符,包括空格,制表符,换行符等, 还有压缩一些其他意义的字符,如HTML注释
方式: 1. 在线网站压缩 2. nodejs提供了html-minifier工具 复制代码
css压缩: 无效代码删除 css语义合并
方式: 1. 使用clean-css对cssj进行压缩 2. 使用html-minifier对html中的css进行压缩 复制代码
js压缩与混乱: 无效字符的剔除: 比如注释回车空格,比如无效的变量 剔除注释 代码语义的缩减和优化: 比如变量名称特别长sommmmesuisan, 压缩为s变量 代码保护: 代码的混乱对前端代码的保护,将代码的风格变的混乱降低可读性
方式: 1. 使用uglifyjs2对jsj进行压缩 2. 使用html-minifier对html中的js代码进行压缩 复制代码
文件合并:
文件合并的带来的问题: 1. 首屏渲染问题: 首屏js文件加载过长,比如vue就会出现这样的情况 2. 缓存失效问题: a17abdh12.js 和b128hsg12h.js两个文件合并后,如果 a17abdh12.js发生了变化,那么这两个文件合并后的js文件肯定也会变化 。导致用户在访问网站的时候之前缓存的js文件失效。 真实场景: 1. 一般公共库会合并成一个文件,业务代码会合并为一个文件。公共库修改 非常少,而业务代码变动的可能性非常高,所以业务代码改动不会影响 到公共库代码的缓存的失效。 方式: 1. 利用nodejs实现文件合并,比如webpack来进行合并 复制代码
开启gzip:
服务端开启gzip压缩,缩小文件大小,浏览器获取到gzip文件后在解压缩 复制代码
图片相关
png8/png24/png32之间的区别
1. png8 --- 256色 + 支持透明 + 文件小很多
2. png24 --- 2^24色 + 不支持透明 + 文件大很多
3. png32 --- 2^24色 + 支持透明 + 文件最大
备注:每种图片格式都有自己的特点,针对不同的业务场景选择不同的图片格式非常重要
不同格式图片常用的业务场景:
1. jpg有损压缩,文件体积小,不支持透明 ---大部分不需要使用透明图片的场景
2. png支持透明,浏览器兼容性好 --- 大部分需要使用透明图片的场景
3. webp压缩体积最小,但是兼容性存在问题 --- ios支持存在问题,安卓尝试使用
4. svg矢量图,代码内嵌,相对较小,图片样式相对简单的场景 --- 图片样式相对简单的场景
复制代码
图片压缩
-
css雪碧图,整合到一张图片上,减少网站的HTTP请求数量。带来的缺点: 整合图片比较大的时候,一次加载就比较慢了。所以雪碧图也要控制大小
-
Image inline,将图片的内容内嵌到html当中,减少网站HTTP请求数量, 也就是base64格式的图片,直接写在html中,就不会发送HTTP请求了。
-
使用矢量图, 使用SVG进行矢量图的绘制,使用iconfont解决icon问题。
-
在安卓下使用webp,图片体积非常非常小,同时具有无损和有损压缩。 在使用webp应该采用降级方式,如果支持使用webp,否则默认还是使用jpg。
html页面加载渲染的过程
浏览器解析html标签渲染成DOM树,当加载到link标签,会并发的向服务器发送css的资源请求。 当加载到script标签,会向服务器请求js资源,对css进行解析后结合DOM树生成渲染树,接着进行布局,然后绘制。
html渲染过程的特点
-
顺序执行, 并发加载
- 词法分析: 浏览器对html文档解析的方式,从最开始的部分对标签进行从上到下的解析,解析过程从上到下,顺序执行!
- 并发加载: html引入的外部资源是并发加载的
- 并发上限: 某个域名下的请求数量是存在上限的,所以对某个域名下所请求资源的数量从而避免达到并发上限
-
是否阻塞
- css阻塞
1. head标签中阻塞页面渲染 推荐使用link标签的方式在head中在html中引入 2. css不阻塞外部脚本js的并发加载,但是会阻塞外部脚本js的执行 因为js在操作DOM的时候,有可能涉及到css样式的修改,而这个css样式的修改 是基于之前css样式的渲染。所以在css代码渲染完毕,才会进而执行js代码, 也就是css代码阻塞了外部js的执行 复制代码
- js阻塞
1. 直接引入的js阻塞页面的渲染,比如<script src='a.js'></script> js代码中很有可能去调用document.write方法来修改DOM文档的结构, 所以需要等待js代码执行完毕,进而去继续执行html标签的分析, 所以js脚本代码中不要出现document.write这样的代码 2. js不阻塞外部资源的加载 3. js顺序执行,会阻塞后续js逻辑的执行 举个例子:<script src='a.js'></script> <script src='b.js'></script> 按照这样的顺序引入,会在a.js里面的代码执行完毕接着执行b.js里面的代码 因为js的执行是单线程的,也就是b.js里面执行的代码可以依赖a.js中的代码 复制代码
-
引入方式
- js引入方式
1. 直接引入
2. defer: 确保所有的DOM树都生成
3. async:不能确保脚本js文件之间的依赖关系,所以async引入的js脚本不能存在依赖关系
4. 异步动态引入js: 在需要某个js文件的时候,通过生成script标签来引入
复制代码
css和js文件加载过程的优化点
- css样式表存放在head中,确保css和html一起生成渲染树,而不至于从没有样式,跳转到有样式的情况,出现页面闪动。
- 用link标签代替@import这样引入css的方式,@import的方式无法并发加载,另外@import这样的方式会在DOM加载结束后才会加载
- js脚本文件放在body底部
- 合理使用js的异步加载能力,就是采用动态生成script标签,动态加载所需要的js文件
async 和 defer
- async 确保在所有html页面dom渲染完之后才会加载
- async 加载的js脚本文件,不是按照顺序加载的,所以只有几个js脚本没有依赖关系,才可以通过async的方式来加载脚本文件
- defer 不阻塞页面渲染,是按照顺序执行的,会按照script的顺序依次执行。在DOM渲染完成之后才会执行。
动态引入js脚本
function loadScript(src) {
let ele = document.createElement('script');
ele.type = 'text/javascript';
ele.src = src
document.body.appendChild(ele)
}
loadScript('demo.js')
复制代码
懒加载和预加载
懒加载
- 图片进入可视区域之后请求图片资源
- 对于电商等图片很多,页面很长的业务场景非常适用
- 减少无效资源的加载,因为可能用户只访问100张图片,剩下的100张是没有必要加载出来,这些资源就是属于无效资源。
预加载
- 图片等静态资源在使用之前提前请求图片资源
- 资源在使用到的时候从缓存中加载,提升用户体验
重绘和回流
回流(reflow): 当render tree中的一部分因为元素尺寸,布局,隐藏等改变而需要重新构建,称为回流。 当页面布局和几何属性改变时就会回流。
触发回流的属性
- 盒子模型相关属性
width, height, padding, margin,display,border-width, border,min-height
复制代码
- 定位属性
top, bottom, left, right, position, float, clear
复制代码
- 改变节点内部文字结构像相关属性
text-align, overflow-y, font-weight, overflow, font-family, line-height
vertical-align, white-space, font-size
复制代码
重绘:当render tree中一些元素需要更新属性,而这些属性只是影响元素颜色风格,而不影响布局的,比如background-color,color等 称为重绘。
只会触发重绘的属性
color, border-style, border-radius, visibility, text-decoration, background,
background-size, background-image,background-position, background-repeat,
outline-color, outline, outline-style, box-shadow
复制代码
重绘不一定会触发回流,但是回流一定会绘制重绘
重绘回流优化方式:
- 避免使用触发重绘,回流的CSS属性
- 将重绘和回流的范围限制在单独的图层之内(暂时不理解.....)
实际工作中CSS关于回流和重绘的总结
-
translate 替换top的变化
translate不会触发回流,top会触发回流 -
opacity 替换visiblity
visiblity只会触发重绘的过程,opacity不会触发重绘的过程// bad setTimeout(() => { document.getElementById('box').style.visiblity = 'hidden' }, 2000) // good #box { width: 100px; height: 100px; background: red; transform: translateZ(0); // 在浏览器的层面建立一个新图层 opacity: 1; } setTimeout(() => { document.getElementById('box').style.opacity = 0 }) 复制代码
-
不要一条一条的地修改DOM的样式,预先定义好class,然后修改DOM的ClassName
// bad
var box = document.getElementId('box')
setTimeout(() => {
box.style.width = '200px';
box.style.height = '200px';
box.style.background = 'red'
})
// good
.box {
width: 200px;
height: 200px;
background: red;
}
setTimeout(() => {
box.classList.add('box');
})
复制代码
- 把DOM离线后修改(比如,需要频繁操作DOM并将DOM的样式进行修改,那么可以先将DOM的display:none(有一次回流),然后你修改DOM样式100次,在display:block显示出来)
- 不要使用table布局,可能很小的小改动会造成整个table的重新布局
- 合理选择动画的速度
- 当使用实现动画的时候,启用GPU硬件渲染加速,当然如果数据量很大,需要从cpu到gpu的过程也是非常耗费时间的,这个也是需要考量的
.box {
transform: translateZ(0);
transform: translate3d(0,0,0); // 启用GPU硬件加速
}
```
复制代码