首先我们需要明确一个问题:将url输入浏览器到页面的出现经历了什么?可以归纳为两个关键方面:浏览器渲染流程;网络交互层面上的优化。
一、浏览器渲染流程
构建DOM树、CSSOM树、渲染树
(1)构建DOM树
- 转换: 浏览器从磁盘或网络读取 HTML 的原始字节,并根据文件的指定编码(例如 UTF-8)将它们转换成各个字符。
- 令牌化: 浏览器将字符串转换成 W3C HTML5 标准规定的各种令牌,例如,“<html>”、“<body>”,以及其他尖括号内的字符串。每个令牌都具有特殊含义和一组规则。
- 词法分析: 发出的令牌转换成定义其属性和规则的“对象”。
- DOM 构建: 最后,由于 HTML 标记定义不同标记之间的关系(一些标记包含在其他标记内),创建的对象链接在一个树数据结构内,此结构也会捕获原始标记中定义的父项-子项关系:HTML 对象是 body 对象的父项,body 是 paragraph 对象的父项,依此类推。
整个流程的最终输出是我们这个简单页面的文档对象模型 (DOM),浏览器对页面进行的所有进一步处理都会用到它。
浏览器每次处理 HTML 标记时,都会完成以上所有步骤:将字节转换成字符,确定令牌,将令牌转换成节点,然后构建 DOM 树。这整个流程可能需要一些时间才能完成,有大量 HTML 需要处理时更是如此。
根据以上结构分析,使DOM树构建更快:
1. 标签语义化。
2. 避免深层次嵌套。
(2)CSSOM树(CSS 对象模型)
cssom的构建与dom树构建流程基本相同
根据以上分析,使CSSOM树构建更快:
CSS是从右到左解析的,因此也要避免嵌套层级过多。(使用less、scss等css预处理器时,也要尽可能少的使用层级嵌套)
(3)渲染树
1、 真正渲染到页面中的时候:一定发生在DOM-tree和CSSOM树都已经完成了。
2、 HTML和CSS都是阻碍页面渲染的东西。
3、 当加载html代码时,是主线程加载,此时是自上而下的解析代码。当遇到link时就相当于发送一个http请求,每个http请求都是单独的线程去处理,http在谷歌里面一般同时可以发送6-7个。@import是同步的,会阻塞渲染。遇到<style></style>时,它是在html拿回来时就已经请求,减少发送请求,(当style样式比较少时,比link稍微较好)。
优化方案:
● 标签语义化和避免深层次嵌套
● CSS选择器渲染是从右到左,避免多层级选择器的嵌套
● 尽早尽快地把CSS下载到客户端(充分利用HTTP多请求并发机制),当css样式较少时,优先使用style。其次就是link,它就相当于发送一个http请求,是异步的。不建议使用@import,因为它是同步的。将css放在顶部,从而更早的发送请求。
● 避免js阻塞,故将其放在底部。(也可以使用异步加载 ,使用async(无序的加载)或者defer(等页面渲染完了再依次加载))
● 减少DOM的回流和重绘(【回流:元素的大小或者位置发生了变化(当页面布局和几何信息发生变化的时候),触发了重新布局,导致渲染树重新计算布局和渲染】。【重绘:元素样式的改变(但宽高、大小位置等不变)】)
修改了元素的位置或者结构等,就会触发回流
元素样式更改,则会触发重绘。
回流一定会引发重绘,重绘不一定会引发回流。
二、网络交互层面上的优化
具体什么是DNS?
DNS( Domain Name System)是“域名系统”的英文缩写,是一种组织成域层次结构的计算机和网络服务命名系统,它用于TCP/IP网络,它所提供的服务是用来将主机名和域名转换为IP地址的工作。DNS就是这样的一位“翻译官”,它的基本工作原理可用下图来表示。
1. DNS方面的优化
每一次DNS解析时间预计在20—120毫秒
● 减少DNS请求次数
● DNS预获取(DNS Prefetch)
// 提前解析DNS,并缓存到本地,此过程可以理解成异步
<meta http-equiv="x-dns-prefetch-control" content="on">
<link rel="dns-prefetch" href="//static.360buyimg.com" />
<link rel="dns-prefetch" href="//misc.360buyimg.com" />
<link rel="dns-prefetch" href="//img10.360buyimg.com" />
<link rel="dns-prefetch" href="//d.3.cn" />
2. 减少HTTP请求次数和请求资源大小
● 资源合并压缩
● 字体图标(iconfont)
● Base64
● GZIP(一般的文件能压缩60%多)
● 图片懒加载
● 数据延迟分批加载
● CDN资源
3. 应用缓存
缓存位置
- Service Worker:浏览器独立线程进行缓存
- Memory Cache:内存缓存(内存相对硬盘小,但是读取快)
- Disk Cache:硬盘缓存(内存大,但是读取慢)
- Push Cache:推送缓存(HTTP/2中的)
打开网页,地址栏输入地址:查找disk cache中是否有匹配,如果有则使用,如果没有则发送网络请求。
普通刷新(F5):因为TAB并没有关闭,因此memory cache是可用的,会被优先使用(如果匹配的话),其次才是disk Cache。
强制刷新(Ctrl+F5):浏览器不使用缓存,因此发送的请求头部均带有Cache-control:no-cache(为了兼容,还带了Pragma:no-cache),服务器直接返回200和最新内容。
1)强缓存Expires/Cache-Control
浏览器对于强缓存的处理:根据第一次请求资源时返回的响应头来确定的
两者同时存在的话,Cache-Control优先级高于Expires
2)协商缓存Last-Modified/ETag
协商缓存就是强制缓存失效后,浏览器携带缓存标识向服务器发起请求,由服务器根据缓存标识决定是否使用缓存的过程。
● 协商缓存生效,返回304和Not Modified