[TOC]
chrome性能优化检测工具
1.谷歌浏览器工具资源管理工具network查看
识别network单个资源的加载过程中的过程和耗时
可以在netword里面将此次与后台交互的资源保存下来,下次可分析或者在别的工具使用
右键>save all as HAR with content
性能优化-加载测量指标
性能优化-响应
性能测量模型-RAIL模型
处理事件应在50ms内完成
10ms一帧
尽可能增加空闲时间
5s内加载完资源
网站整体测量工具
webpagetest使用总结
性能优化方案
后台开启网络传输的压缩
审查页面js的运行耗时长性能问题
减少重绘回流,页面抖动(layout trashing)
cord.style.width = cards.offsetTop+1+'px'; // cards.offsetTop引起读取高度触发回流,再赋予高度又触发回流,连续的读写触发强制更新,引起页面抖动
以下会引起回流
改变窗口大小
font-size大小改变
增加或者移除样式表
内容变化(input中输入文字会导致)
激活CSS伪类(:hover)
操作class属性,新增或者减少
js操作dom
offset相关属性读取和计算
设置style的值 ......
解决方案:日常读写分离/使用fast-dom插件
// 实现
fastdom.measure(()=>{
let top = cards.offsetTop+1+'px';
fastdom.mutate(()=>{
cord.style.width = top;
减少布局和重绘(repaint)
如何识别paint的瓶颈-如何查看重绘
减少图层的消耗-使用transform和 opacity
使用willchange使动画提取到单独的图层里
willchange: transform;
使用raf进行动画-解决页面卡顿,抖动问题
缩短js的解析开销
解析&编译
加载解决方案
code splitting 代码拆分,按需加载
tree shaking 代码减重
执行解决方案
避免长任务
避免超过1kb的行间脚本
使用rAF和rIdC进行时间调度
解析&编译解决方案
懒解析 lazy parsing VS 饥饿解析 eager parsing
// 通过括号来触发饥饿解析,饥饿解析就是立即解析,页面加载的时候就立即解析,这样再运行时会减少解析时间,却会增多加载页面的时间,有利有弊;
var sum = (a,b)=>a+b; // 正常解析-懒解析
var sum = ((a,b)=>a+b); // 饥饿解析,
利用Optimize.js优化初次加载的时间
压缩文件插件会去掉触发饥饿解析的括号,Optimise.js可帮忙找回
以相同顺序初始化对象成员,避免隐藏类的调整
var a = {color:'blue'} // HC0-隐藏类
a.seat = 4; // HC1
var a2 = {seat:4}, // 重新创建HC0,无法复用上面的隐藏类对象
a2.color = 'red'; // HC1
var a3 = {color:'blue'} // 沿用HC0
a.seat = 4; // 沿用HC1
实例化后避免添加新属性
var a = {color:'blue'} // In-object 属性
a.seat = 4; // normal/fast属性,存储property store属性里,需要通过描数组简介查找
尽量使用Array代替array-like对象
避免读取超过数组的长度-越界的时候会返回undefind,当循环中有对数组元素进行比较时,会导致沿着原型链向上查找,增加额外的开销
避免元素类型转换
元素类型有隐藏的自降级优化体系
// 对数组的数据类型转化而采取的优化策略,越往后开销越大。holey是数据删除后留下的坑的意思
packed_smi_elements->packed_double_elements->packed_elements
| | |
holey_smi_elements -> holey_double_elements-> holey_elements
减少iframes的使用,iframes资源下载会占用父级的资源下载,解析进程,可以在父窗口load后再加
压缩空白符
避免节点深层次嵌套,子级越多,生成dom树消耗(遍历dom树节点)越多
避免使用table布局
css&js尽量外链
删除元素默认属性
css优化
devtools查看css性能开销
performance录制 > Main里面的recalculate Style块
降低css对渲染的阻塞(较少代码块大小)
利用GPU进行完成动画
使用contain属性
contain 属性的主要目的是隔离指定内容的样式、布局和渲染。开发人员可以使用这个 contain 属性来限制指定的DOM元素和它的子元素同页面上其它内容的联系;我们可以把它看做一个iframe。跟iframe很相似,它能建立起一个边界,产生一个新的根布局;保证了它和它的子元素的DOM变化不会触发父元素重新布局、渲染等。
.a li {
padding:10px;
contain:layout;
font-display属性
资源压缩与合并
html压缩
使用在线工具压缩
使用html-minifier等npm工具
css压缩
使用在线工具压缩
使用clean-css等工具
js压缩与混淆
使用在线工具压缩
使用webpack
js或css文件是否合并
若干小文件,也许可以
无冲突,服务相同的模块,可以
优化加载,不考虑
imagemin,也有github
imagemin-pngquant
图片懒加载-插件很多不列举
使用渐进式图片
baseline JPEG行扫描图片,图片加载显示是一行一行的,体验不是很好
Progressive JPEG 从低像素到高像素加载
美工可以制作渐进式图片或者下面的工具自己转
progressive-imge ImageMegick libjpeg jpegtran jpeg-recompress imagemin
使用响应式图片
srcset属性,设置多尺寸图
sizes属性,识别边界
// 其中srcset指定图片的地址和对应的图片质量。sizes用来设置图片的尺寸零界点
<img srcset="a.png 100w,a.png 200w" sizes="100vw">
picture新属性
文字因为下载,闪烁不可避免
使用font-display规避闪烁效果
font-display:block; // 阻塞3s不显示,直到下载完成显示
font-display:swap; // 先用默认字体,然后字体下载完成再更换
font-display:optional; // 移动端解决方案, 加载下载字体,如果一定时间后显示不出来替换成系统字体,不再改变成加载字体
babel7优化配置
在需要的地方引入polyfill
babel 添加垫片polyfill配置在babel.config.js里可配置,减少打包不需要的垫片代码
"useBuiltIns":"usage"
辅助函数按需引入
根据目标浏览器按需转换代码
babel.config.js里可配置
"targets":{
"browsers":[">0.25%"] //这个百分比是保留浏览器兼容版本列表
webpack配置
忽略某些库 noParse
// webpack.config.js
module:{
noParse:/lodash/, //忽略lodash库,不进行递归解析
避免打包的时候对不变的库重复构建,提高构建速度-DllPlugin
基于webpack的代码拆分
把单个bundle文件拆分成若干小bundles/chunks-缩短首屏加载时间
splitChunks提取公有代码,拆分业务代码与第三方库
optimization:{
splitChunks:{
cacheGrounps:{
vendor: {
name:'vendor',
test:/[\\/]node_module[\\/]/,
minSize:0,
minChunks:1,
priority:10,
chunks:'initial'
common:{
name:'common',
test:/[\\/]src[\\/]/,
chunks:'all',
minSize:0,
minChunks:2,
// 常规加载
import {add} from './math'
console.log(add(1,2)
// 动态加载
import('./math').then(math=>{
console.log(math.add(1,2))
基于webpack 的代码压缩mimification
terser-js代码压缩
mini-css-extract-plugin+optimizeCssAssetsPlugin压缩css
htmlwebpackpackplugin-minify压缩html
持久化缓存方案
每个打包的资源文件有唯一的hash值
修改后只有受影响的文件hash变化
充分利用浏览器缓存
webpack打包监测和分析的工具
stats分析和可视化图
webpack-charts
webpack-bundle-analyzer 进行体积分析
speed-measurt-webpack-plugin 速度测试
也可以用官方的
启用Gzip压缩
对资源传输进行压缩可达90%
用ngnix
服务器自己加
启用keep alive
建立tcp连接在initial connection里面
启用keep alive后只有第一个连接有initial connection消耗,其他连接都没有了
keep alive 能保持连接,无需重新重新建立连接
keep alive 默认自己启用 在response headers里的connection可见
http缓存
Service Workers作用
加速重复访问
http2
二进制传输
请求响应多路复用
Server push
如何访问不安全的网站,在浏览器报不安全界面,直接输入thisisunsafe并且回车,即可直接访问
适用于请求量高的网站
具体ngnix配置百度即可
SSR服务端渲染
加速首屏加载
更好的SEO
PerformanceNavigationTiming 浏览器接口
// 获取时间接口
// 在load事件后触发
window.addEventListener('load',(event)=>{
// 获取可交互时间
let timing = performance.getEntriesByType('navigation')[0];
let tti = timing.domInteractive - timing.fetchStart;
console.log("TTI:"+tti);
DNS 解析耗时: domainLookupEnd - domainLookupStart
TCP 连接耗时: connectEnd - connectStart
SSL 安全连接耗时: connectEnd - secureConnectionStart
网络请求耗时 (TTFB): responseStart - requestStart
数据传输耗时: responseEnd - responseStart
DOM 解析耗时: domInteractive - responseEnd
资源加载耗时: loadEventStart - domContentLoadedEventEnd
First Byte时间: responseStart - domainLookupStart
白屏时间: responseEnd - fetchStart
首次可交互时间: domInteractive - fetchStart
DOM Ready 时间: domContentLoadEventEnd - fetchStart
页面完全加载时间: loadEventStart - fetchStart
http 头部大小: transferSize - encodedBodySize
重定向次数:performance.navigation.redirectCount
重定向耗时: redirectEnd - redirectStart
navigationStart 加载起始时间
redirectStart 重定向开始时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回开始重定向的fetchStart的值。其他情况,则返回0)
redirectEnd 重定向结束时间(如果发生了HTTP重定向,每次重定向都和当前文档同域的话,就返回最后一次重定向接受完数据的时间。其他情况则返回0)
fetchStart 浏览器发起资源请求时,如果有缓存,则返回读取缓存的开始时间
domainLookupStart 查询DNS的开始时间。如果请求没有发起DNS请求,如keep-alive,缓存等,则返回fetchStart
domainLookupEnd 查询DNS的结束时间。如果没有发起DNS请求,同上
connectStart 开始建立TCP请求的时间。如果请求是keep-alive,缓存等,则返回domainLookupEnd
(secureConnectionStart) 如果在进行TLS或SSL,则返回握手时间
connectEnd 完成TCP链接的时间。如果是keep-alive,缓存等,同connectStart
requestStart 发起请求的时间
responseStart 服务器开始响应的时间
domLoading 从图中看是开始渲染dom的时间,具体未知
domInteractive 未知
domContentLoadedEventStart 开始触发DomContentLoadedEvent事件的时间
domContentLoadedEventEnd DomContentLoadedEvent事件结束的时间
domComplete 从图中看是dom渲染完成时间,具体未知
loadEventStart 触发load的时间,如没有则返回0
loadEventEnd load事件执行完的时间,如没有则返回0
unloadEventStart unload事件触发的时间
unloadEventEnd unload事件执行完的时间
检查页面中所有的长任务
let observer = new PerformanceObserver((list)=>{
for(const entry of list.getEntries()){
console.log(entry)
observer.observe({entryTypes:['longtask']});
检查页面是否可见
这是只有浏览器的行为会触发,如切换页签和最小化
let vEvent = 'visibilitychange';
if(document.webkitHidden != undefined){
// webkit事件名称
vEvent = 'webkitvisibilitychange';
function visibilityChanged(){
if(document.hidden || document.webkitHidden){
console.log("Web page is hidden");
} else { // 页面可见
console.log("Web page is visibile");
document.addEventListener(vEvent, visibilityChanged,false);
检测网络变化状况
如果网络发生变化,就要加载不同大小的资源来保证网站打开速度流畅
let connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
let type = connection.effectiveType;
function updateConnectionStatus() {
console.log("connection type change from " + type + " to " + connection.effectiveType);
connection.addEventListener('change',updateConnectionStatus,false);
浏览器的绘制过程
这里说的是重新计算布局,也就是当使用css或者js进行动画或者crud dom节点时候的过程
js/css > style > layout > paint > composite
js/css 重新设计页面元素的驱动者
style 重新计算关联元素的颜色,字体这些
layout 布局计算每个节点精确的位置和大小-“盒模型”
paint 绘制是像素化每个节点的过程
composite 复合图层
将页面拆分图层进行绘制再进行复合
如何再浏览器控制台查看图层
老版本谷歌:
performance > 点击frames中要查看的某一块>下面选项卡的layer>显示出的列表就是每一个图层啦
新版本谷歌(和performance同级的选项卡,勾出来即可):
控制台汉堡包选项(竖向三个点) > more tools> layers
如何查看重绘
<span id="如何查看重绘"></span>
performance > 键入esc > 下方选项卡勾选出rendering > 勾选 Paint Flashing
重绘的元素会以绿色显示
v8引擎工作原理
源码的编译过程 => 抽象语法树 > 字节码bytecode > 机器码
编译过程会进行优化
运行时可能发生反优化
第二,三点,比如数据类型的编译,如果再js代码中循环对一个数组进行函数数字运算,编译过程会将它优化成只有数据类型,不进行类型检测,运行速度会快许多,当参与运算的数组中放入一个字符串的时候,编译过程无法优化,需要每次迭代都进行参数类型验证,运行速度明显下降。
V8优化机制
脚本流 (边下载边解析,比如当一个脚本超过30kb,认为足够大,就单独开一个线程进行解析,不用等所有都下载完成)
字节码缓存 (使用频率高的字节码片段会临时存储,等下次运行从缓存中使用)
懒解析 (函数声明,但是不解析里面的逻辑,等运行时再解析)<i id="懒解析"></i>
tree-shaking
上下文未用到的代码(dead code)
基于ES6 import export
package.json中配置sideEffects