Optimize
# 优化指标
写给中高级前端关于性能优化的9大策略和6大指标 | 网易四年实践 (opens new window)
让用户感觉这个网站很「快」 , 一种是「真的快」一种是「觉得快」
「真的快」 可 客观衡量的指标, 网页访问时间、交互响应时间、跳转页面时间
「觉得快」 用户主观感知的性能,通过视觉引导等手段转移用户对等待时间的关注
动画流畅
表单提交速度
列表滚动页面切换是否卡顿
响应速度:页面初始访问速度 + 交互响应速度
页面稳定性:页面出错率,资源加载错误,JS 执行报错
外部服务调用:网络请求访问速度
最好的性能指标:100ms 内响应用户输入;动画/滚动在 10ms 内下一帧;最大化空闲时间;页面加载时长不超过 5 秒
系统架构、性能分析、网站优化、网站监控
# 权衡
一种优化方案可能适用于大多数项目,但是特殊情况下可能会起反效果
浏览器有单域名下并发请求限制,通常我们将依赖统一打包减少首屏请求数,依赖不变动文件指纹不变,利用缓存。依赖不多这么处理有助于提升加载速度,一旦依赖多起来,包就会特别 大, 弱网条件 严重拖慢页面显示
所以 根据情况 对vendor拆分, 如拆分到CDN,或者直接拆分到页面
因此, 在 性能优化过程中,必须根据最终能给用户体验带来的提升权衡后做出适合 的选择
# 优化手法
- 聚焦用户
- 尽快响应用户输入
- 动画执行流畅
- 最大化主线程空闲时间
- 网页可交互性
# HTML
- 避免HTML中直接写CSS
- viewport加速页面渲染
- 使用语义化标签
- 减少标签的使用,DOM解析是一个大量遍历的过程
- 避免src和href空值
- 减少DNS查询数
# CSS
避免后代选择符
避免链式~
避免!important
link代替@import
@import会将请求变得 串行化,导致加载增加延迟
- 减少回流与重绘
- CSS 放在
head中 - 压缩CSS 开启gzip压缩
- 骨架屏+合理的loading
- 优化选择器路径,避免 过多嵌套
- 选择器合并:压缩空间和资源开销
- 精确样式
比如设置{padding-left:10px}的值,避免{padding:0 0 0 10px}这样的写法
- 异步加载CSS
- 避免通配符
.a .b *{} 像这样的选择器,从右到左解析,在解析过程中遇到通配符(**)会去遍历整个dom
- 少用float:渲染时计算量大
- 0值不加单位:兼容性
- 避免使用 昂贵 的属性
因为他们渲染成本挺高,渲染速度慢一些
- border-radius
- box-shadow
- opacity
- transform
- filter
- position: fixed
- 使用先进布局方式——flex
# 会造成阻塞吗
- CSS加载不会阻塞DOM树解析
- CSS加载会阻塞DOM树渲染
- CSS加载会阻塞后面JS执行
# JS
- 避免循环操作DOM
- 事件委托
绑定事件时,不绑定到目标元素上,而是绑定到其祖先元素上
- 监听事件少
- 新增节点时,无需增加事件绑定
- scrip标签放在body后
CSS放在
head因为JS阻塞DOM的构建(因为DOM解析遇到JS会停止解析,开始下载脚本并执行) ,CSSOM的构建阻塞JS执行
- 压缩文件
- 按需加载
- 避免逐个操作DOM样式,尽可能预留好CSS样式,通过样式名的修改改变DOM样式,集中式操作减少reflow的次数
- 减少iframe数量
# 合成
- 合成层的位图 交由GPU处理,比CPU块
- repaint本身,不影响其它层
- transform和opacity不触发重绘
# 代码问题
- 频繁使用JSON.parse/JSON.stringify大对象
- 正则灾难性回溯
- 内存泄漏
- 服务端开启文件压缩功能
- 执行 JS 代码过长会卡顿,对需要很多时间计算的代码可用
web worker
# 网络相关
# DNS预解析
预先获得域名所对应的 IP,href的值是预解析的域名
<link rel="dns-prefetch" href="//xxx.example.com" />
preload和prefetch
preload强制浏览器立即获取资源, 具有较高优先级
prefetch的资源获取时可选 和 较低 优先级的,是否获取取决于浏览器
# 缓存
强缓存
协商缓存
选择合适缓存策略
对于大部分的场景都可以使用强缓存配合协商缓存解决
- 使用
Cache-control: no-store,表示该资源不需要缓存 - 使用
Cache-Control: no-cache并配合ETag,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新 - 使用
Cache-Control: max-age=31536000并配合策略缓存使用,对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件
# 使用HTTP2.0
- 解析速度快
- 头部压缩
- 多路复用
- 服务器推送
- 浏览器由并发请求限制
# 预加载(首屏优化不提)
有些资源不需要马上用到,但希望尽早获取
预加载强制请求资源,不会阻塞
onload事件<link rel="preload" href="http://example.com" />1
# 预渲染
将下载的文件预先在后台渲染
<link rel="prerender" href="http://example.com" />
预渲染可以提高页面的加载速度,但是要确保该页面百分百会被用户,否则白白浪费资源
# 减少HTTP请求
# SSR
SSR (service side render),后端将HTML拼接好返回前端
优点
- 前端耗时少
- 利于SEO,有完整HTML页面,爬虫更易获取信息
- 无需占用客户端资源
- 后端生成静态化文件
- 首屏加载更快
缺点
- 不利于前后端分离
- 占用服务器资源
客户端渲染
优点
- 前后端分离
- 体验更好
缺点
- 前端响应较慢
- 不利于SEO
# Gzip压缩
# 避免重定向
# 渲染优化
# 懒执行
将某些逻辑延迟到使用时再计算。该技术可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒执行。懒执行需要唤醒,一般可以通过定时器或者事件的调用来唤醒。
# 懒加载
将不关键的资源延后加载——尽量只加载用户正 浏览 或即将会 浏览的图片
只加载自定义区域(通常是可视区域,但也可以是即将进入可视区域)内需要加载的东西
对于图片来说,先设置图片标签的 src 属性为一张占位图,真实的图片资源放入自定义属性data-src 中,当进入自定义区域时,就将自定义属性替换为 src 属性,这样就会下载图片资源
# 节流防抖
防抖:单位时间内多次触发,只执行最后的那一次,原理:延迟执行,期间但凡有新的触发就重置定时器
节流:单位时间只触发一次,原理:上锁,只有满足一定间隔时间才能执行
# 图片
电商类项目,存在大量图片,banner 广告图、菜单导航栏、列表头图等
图片众多以及体积过大影响页面加载速度
为啥?
有些图片请求并发,Chrome最多支持并发请求数有限,其他请求被push进队列中等待或停滞,直到上轮请求完成后才被发出,一部分资源需要排队等待时间,过多的图片影响页面加载展示
# 合适图片格式
- WebP 格式具有更好的图像数据压缩算法,更小的图片体积,拥有肉眼识别无差异的图像质量,缺点是兼容性并不好
- 小图使用 PNG,对于大部分图标,完全可以使用 SVG 代替
- 照片使用 JPEG
- 雪碧图( 将多个图标文件整合到一张图片中 )
可能请求非常多的小图片,会受到浏览器并发 HTTP 请求数的限制
- 图片压缩
- 不用图片,用CSS代替
- 对于移动端来说,屏幕宽度就那么点,完全没有必要去加载原图浪费带宽。一般图片都用 CDN 加载,可以计算出适配屏幕的宽度,然后去请求相应裁剪好的图片
# JPEG/JPG
- 高质量 有损压缩,体积小,不支持透明
- 应用于轮播图 大的背景图、banner
# PNG
- 无损压缩,质量好,体积大,支持透明
- 应用小的logo
# SVG
- 体积小,不失真,兼容好
- 应用于图标
# GIF
- 支持透明
# Webp
有损压缩与无损压缩(可逆压缩)的图片文件格式
比PNG/JPEG格式小
支持透明度
体积和效果上都做的不错
<picture>
<source type="image/webp" srcset="/static/img/perf.webp">
<source type="image/jpeg" srcset="/static/img/perf.jpg">
<img src="/static/img/perf.jpg">
</picture>
2
3
4
5
# webpack压缩
配置 image-webpack-loader
# 雪碧图
CSS Sprites ,精灵图,图像合成技术,主要用于小图片显示
同原域名请求有最大并发限制,Chrome为6个,如 页面有10个小图,需要10次请求,2次并发
若把10个图合成一个大图,只需1次请求
- 减少请求次数
- 减少服务器压力
- 减少并发
- 提高加载速度
- 减少鼠标滑过的一些bug
- 解决网页设计师在图片命名上的困扰
# iconfont
通过字体方式展示图标,用户 图标渲染、简单图形、特殊字体等
- 轻量,已修改
- 减少请求次数
# 内联Base 64
图片转为base64串,解析图片不会请求下载,而是解析字符串
缺点
- 比使用二进制体积增大 33%
- 全部内联后,原本可并行加载的图片会串行放入请求
适用于 更新频率低、首屏或骨架图上的小图标
# CSS代替图
实现修饰效果,半透明、阴影、圆角、渐变等
# CDN图片
# 图片懒加载
暂时不设置图片的src属性,先卸载data-src中,等图片到了可视区域再将真实src放进src属性
使用background-url,应用到具体元素时,才会下载图片
# 图片预加载
需要展示大量图,将图提前加载到本地缓存
# 响应式图加载
在不同分辨率的设备上显示不同尺寸的图
# 渐进式图片
和骨架屏 原理类似
在图完全加载完前先显示低画质版本,让用户产生图片加载变快的印象,而不是盯着一片空白
# CDN
内容分发网络
静态资源使用 CDN 加载,由于浏览器对于单个域名有并发请求上限,可以考虑使用多个 CDN 域名
# 其他
# Webpack 优化
- 对于 Webpack,打包项目使用 production 模式,会自动开启代码压缩
- ES6 模块开启 tree shaking,移除没有使用的代码
- 优化图片,对于小图使用 base64 的方式写入文件
- 按照路由拆分代码,实现按需加载
- 给打包出来的文件名添加哈希,实现浏览器缓存文件
# 监控
采集——>上传——>分析——>报警
# 渲染几万条数据?
requestAnimationFrame 每 16 ms 刷新
分页+虚拟滚动、图片懒加载、图片的动态裁剪
# SPA首屏优化
浏览器从响应用户输入网址地址,到首屏内容渲染完成时间,整个网页不一定要完全渲染完成,但需要展示当前视窗内容
# 加载慢的原因
- 网络延时
- 资源文件体积过大
- 资源加载重复发送请求
- 加载脚本时,渲染内容阻塞
# 解决
- 减少入口文件体积
- 静态资源本地缓存
- UI框架按需加载
- 图片资源压缩
- 组件重复打包
- GZip压缩
- 使用SSR
- JS外联文件放在HTML文档底部,CSS外联文件放在HTML文档头部
- 减少cookie体积
- 骨架屏
- h5方式一次性渲染
# 减少入口文件体积
路由懒加载,将不同路由对应组件分割成不同代码块,待路由被请求时单独打包路由,使得入口文件变小
以函数形式动态加载路由,可以把各自的路由文件分别打包,只在解析给定路由时,才会加载路由组件
# 组件重复打包
若A.js是一个常用库,多个路由使用它会造成重复下载
在webpack的config中,修改CommonChunkPlugin的配置
minChunks为3表示会把使用3次及以上的包抽离,放进公共依赖文件
# SSR
服务端渲染,Server Side ,组件或页面通过服务器生成html字符串,发送到浏览器
- 二次启动时先利用缓存渲染,后台进行异步数据更新
- 减少不必要的请求 和数据获取
- 提前请求或减少http请求
- 优化图片文件尺寸,压缩图片格式,压缩代码
- 启用gzip 压缩功能
- 使用CDN
- 网址后面加上“/”:对服务器而言,不加斜杠服务器会多一次判断的过程,加斜杠就会直接返回网站设置的存放在网站根目录下的默认页面。
- Ajax采用缓存调用
# 网页卡顿?
- 网络请求是否过多,导致数据传输变慢,可通过缓存优化
- 资源bundle太大,考虑拆分
- 代码是否有太多循环在主线程上花费太长时间
- 浏览器某个帧 中 渲染太多东西
- 页面渲染时,大量回流和重绘
- 内存泄露
# 动画优化
- 合理布局
- transform代替left、top 减少重排
- 硬件加速
- 避免不必要的图形层
- requestAnimationFrame实现动画
动画每一帧都是re-render,显示器刷新频率 60 HZ,意味着每一帧任务耗时不超过 16ms
# React官网优化
React 渲染的未来 (opens new window)
# 补充
react官网不完全由react开发,得益于Gatsby库
Gatsby性能很好,开发很自由,基于React和GraphQL构建网站的库。用于构建静态网站,如博客、企业官网、静态内容相对比较多的网站
监听link如何实现?
result:使用 Intersection Observer,兼容问题