浏览器&优化相关知识点

200 阅读8分钟

1.事件机制

事件触发三阶段

  • window往事件触发处传播,如果遇到注册的捕获事件会触发
  • 事件触发处的绑定事件会发生
  • 从事件触发处往winodw传播,如果有冒泡事件则触发

但是如果一个节点同时注册了触发和捕获事件,又是事件触发处,按照顺序执行。

注册事件

EventTarget.addEventListener(type, listener, options)

  • type代表事件类型,最典型的就是click
  • listener 一般是一个函数,表示监听触发的事件
  • options有好几个属性,capture表示正常的捕捉事件,每次都能触发;once只能触发一次,true会置false;passive表示回调函数永远不会preventDefault()

阻止事件

  • stopImmediatePropagation 阻止绑定在该target上的其他事件发生,从此一个type只会触发这一个事件
  • e.preventDefault() 阻止传播进来的事件的默认行为
  • stopPropagation 阻止事件进一步冒泡传播,

事件代理

当一个节点是动态的时候,将事件绑定在其父节点上以节省内存,不用注销节点事件。

2.跨域

首先了解,浏览器有同源策略,即域名不同,端口不同,协议有一个不同就是跨域,AJAX请求会失败。主要是用于防御CSRF攻击(Cross site request forgy)跨站脚本攻击。

同域代理

向后端发送http请求,后端再根据请求携带的真实地址转发请求。

script img-scr--JSONP

这两个标签是允许跨域的,我们可以将scr指向脚本的地址并设置一个回调函数来接受数据,但是仅限于get请求

CORS

在CORS(Cross Origin Resource Sharing)出现之前同域代理和JSONP是跨域的主流解决方案。

CORS主要是利用HTTP1.1的特性,http header 里面有两个功能

  • 允许或者组织浏览器向其他域名发送请求
  • 用于接收还是拒绝其他域名返回的响应数据

正常的跨域请求是 浏览器发送请求,跨域服务器返回数据,但是浏览器不接受返回的响应数据。 在发送http响应请求的时候,会加上originheader向服务器表面这是一个跨域请求。

如果要允许跨域,需要服务器后端设置Access-Control-Allow-Origin。跨域请求有简单请求和复杂请求。简单请求仅限于 GET,HEAD,POST和Content-Type = text/plain 或者multipart/form-data。

如果响应请求没有这个header,那么浏览器就不会接收数据。

如果不是简单请求,那么浏览器会先发送一个OPTION请求,带上Access-Control-Request-Headers and Access-Control-Request-Method。如果响应数据http header里面没有Access-Control-Allow-Header/Method 或者对应的内容对不上,浏览器就不会发送正式请求。

还可以通过设置二级域名和使用postMessage解决跨域

3.浏览器缓存机制

一个请求可以分为三个阶段,网络请求,后端处理,浏览器响应。缓存可以帮助我们在第一和第三阶段优化性能。而缓存机制分为缓存位置和缓存策略

缓存位置

  • Service Worker:SW并不是存储的位置而是说SW可以控制哪些内容是否缓存,缓存在哪
  • Memory Cache: 页面上的部分的大内容都会被缓存进Memory Cache, 但是大文件或者内存利用率高的使用使用disk Cache
  • Disk Cache: 将部分内容保存在Disk Cache
  • Push Cache: 是HTTP2 协议支持的缓存地点,主要特点是可以支持多页面共用缓存,只在Session中存在 页面关闭立即消失
  • 网络请求:以上缓存都没有命中,那么就会发送网络请求

缓存策略

Cache-Control 控制优先级

  1. no-store: 完全不使用缓存
  2. no-cache: 即使缓存命中也要发送到服务端验证
  3. public/private: 是否允许代理服务器缓存
  4. max-age 查看是否过期
  5. expires 是否过期

是否命中缓存流程

  1. 先检查是否过期(max-age, expire)
  2. 过期了进行协商缓存,没过期直接使用不发送请求
  3. 协商缓存先检查Etag,没有Etag检查 LastModified 都没有请求服务器(200)
  4. 有Etag 检查If-None-Match 相同请求返回304
  5. 有LastModified 检查If-Modified-Since 未改变返回304
  6. 有一个不通过请求数据返回200

频繁变动的资源

频繁变动的资源不应该使用固定缓存而是应该使用no-cache 配合Etag来检查是否需要新资源。不会减少请求次数但是可以减少数据请求的大小

4.浏览器渲染机制

HTML -> DOM tree

  1. 通过HTTP协议传输HTML的字节数据。
  2. 浏览器将字节数据转变为字符串
  3. 分析字符串,打上token
  4. 将每个token变成一个个NODE
  5. 使用tree结构将这些NODE连接起来

HTML 下面可以有HEAD,BODY这两个子树。HEAD下面可以有meta link之类的node BODY下面可以有p标签之类的NODE

CSS => CSSOM tree

与DOMtree是很相似的,CSS因为可以有子类选择器,也会有tree结构。而且CSS应该放在最前面引入,因为通过CSS可能要重新渲染一次页面,如果HTML先渲染然后CSS再来一次会浪费性能,不如一开始就引入CSS。

生成渲染树

浏览器会将DOMtree和CSSOMtree 合并成渲染树,只会渲染要显示的节点,如果某个节点是display:none那么就不会显示。

为什么操作DOM性能消耗大?

因为DOM是渲染引擎的东西,JS是JS引擎的东西。它们由两个线程控制在同一个进程中且这两个线程互斥,跨线程通信设计到线程的死锁,通信等操作,十分消耗资源。

经典面试题:插入几万个 DOM,如何实现页面不卡顿?

可以考虑使用懒加载和预加载的方式加载这些DOM,读入这些DOM但是display:none 只渲染在浏览器可视范围内的节点。 预加载就是display:none 懒加载需要几个API比如document.documentElement.clientHeight, document.querySelectorAll,加上监听scroll事件 其中需要一次性读入全部的DOM节点,这个操作可能不太能接受。

第二种方法是 虚拟滚动(vitural scroll)

什么情况会阻塞渲染?

HTML,CSS的渲染本身就是一种阻塞,JS的执行也会。除非加上defer属性 表示并行下载+最后执行。async属性表示 JS的下载和解析不会阻塞渲染。

重绘和回流

reflow一般是指渲染树上某个节点的几何形状改变或者页面布局改变。需要重新渲染页面 paint是指DOM元素节点的颜色 风格的改变,导致的重新渲染

回流必定重绘,反之不一定。回流成本比重绘高得多。

会导致性能问题的动作

  • 改变盒模型
  • 定位or浮动
  • 改变文字/字体
  • 添加/删除样式
  • 改变window大小

几个小技巧

  • visibility 代替 dispaly:none(注意业务场景,有时不能替换)
  • top 代替 transform
  • 尽量不使用table
  • 不要再循环里面使用节点当变量
  • 将多个DOM操作合并一起操作而不是逐条操作

关键渲染路径

在不考虑缓存和优化网络协议的情况下,怎么尽可能渲染页面?

  • 从文件大小考虑: 压缩JS CSS HTML
  • 从 script 标签使用上来考虑 defer async
  • 从 CSS、HTML 的代码书写上来考虑: 避免过多的子结构
  • 从需要下载的内容是否需要在首屏使用上来考虑 懒加载 预加载 虚拟滚动

网站从开启到加载的过程

  1. 预处理
  2. 查询DNS,返回IP
  3. 建立TCP连接
  4. 发送HTTP请求
  5. 等待响应数据
  6. 静态资源下载
  7. DOMtree CSSOM tree 合成 渲染树 然后渲染 执行JS

前端性能指标

  • 白屏时间---等待响应
  • 首屏时间---静态资源下载(主要是图片,最后图片加载时间-加载开始时间 = 首屏时间)
  • 用户可操作---计算布局
  • 页面总下载(还是图片,加载慢)---渲染完成

性能优化思路

  • 减少数据请求次数
  • 减小数据请求体积
  • 加快请求速度?
  • 缩短渲染时间/加载优化
  • 代码性能优化

减少数据请求次数

主要是缓存的使用,通过对资源使用情况的分析配置缓存。如果资源不经常变,可以使用缓存并将过期时间加长。如果资源频繁变动,那么过期时间短或者no-cache,配合Etag来检查资源。后者的方法其实是减少数据请求体积,因为不能减少请求次数。 HTTP keep-alive

减小数据请求体积

  • HTML,CSS,JS压缩
  • accept-coding: gzip,
  • 图片合并 雪碧图
  • 使用合适的图片类型

加快请求速度

  • CND--缩短服务器距离
  • DNS缓存

代码性能优化

  • CSS不要多级查询子结构
  • HTML不要过多嵌套
  • JS尽量不要直接操作DOM空间(尤其是循环中
  • 避免使用arguments,debugger,使用扩展运算符代替arguments

缩短渲染时间

  • CSS放开头,JS放最后
  • 减少repaint,reflow
  • 缓存也能缩短渲染时间
  • 懒加载/快加载 滚动加载
  • 避免302重定向
  • 合理使用3D硬件加速