一路繁花似锦绣前程
失败的越多,成功才越有价值

导航

 

一、简介

1、性能优化
* 性能优化指标与测量工具
    - 行业标准
    - 优化模型
    - 测量工具
    - 性能相关APIs
* 代码优化
    - javascript优化
    - html优化
    - css优化
* 渲染优化
    - 现代浏览器渲染原理
    - 可优化的渲染环节和方法
* 资源优化
    - 压缩&合并
    - 图片格式
    - 图片加载
    - 字体优化
* 构建优化
    - webpack的优化配置
    - 代码拆分
    - 代码压缩
    - 持久化缓存
    - 监测与分析
    - 按需加载
* 传输加载优化
    - GZip
    - KeepAlive
    - HTTP缓存
    - Service Worker
    - HTTP/2
    - SSR
    - Nginx
* 更多流行优化技术
    - SVG优化图标
    - FlexBox布局
    - 预加载
    - 预渲染
    - 窗口化提高列表性能
    - 骨架组件

二、性能优化的指标和工具

1、f12-network
* network settings(右上角)
    - use large request rows(√):使用大请求行
        ~ transferred over network:资源网络传输大小
        ~ resource size:资源实际大小
    - group by frame:按帧分组
    - show overview(√):显示概述
    - capture screenshots(√):捕获屏幕截图
* 状态栏(最下方)
    - requests:请求数
    - resources:总资源量
    - domcontentloaded:dom加载时间(瀑布图蓝竖线)
    - load:总资源加载时间(瀑布图红竖线)
* 请求行瀑布图(状态栏上方)
    - Queued at:排队时间
    - Started at:开始时间
    - Resource Scheduling:资源调度
        ~ Queueing:排队时间
    - Connection Start:连接开始
        ~ Stalled:暂停时间
        ~ DNS Lookup:域名解析时间
        ~ Initial connection:初始连接时间(TCP三次握手)
        ~ SSL:SSL(协商)时间
    - Request/Response:请求/响应
        ~ Request sent:请求发送时间
        ~ Waiting (TTFB):等待时间(获取在接收到响应的首字节前花费的毫秒数)
        ~ Content Download:内容下载时间
    - Explanation:解析时间
* 瀑布图菜单(右击)
    - save all as har with content:保存性能测试结果
* filter输入框
    - 筛选请求行名称
* disable cache:缓存
* throttling:网络吞吐
2、f12-lighthouse
* performance:表现分数
    - accessibility:可访问性
    - best practices:最佳实践
    - seo:搜索引擎优化
    - progressive web app:渐进式应用加载(pwa)
* metrics:指标
    - First Contentful Paint:绘制第一幅画的时间
    - Speed Index:速度指数(4.0s)
    - Largest Contentful Paint:最大内容绘制
    - Time to Interactive:网站用户可交互时间
3、f12-帧数计量表
* 显示每秒帧数(fps)计量表
    - 按键ctrl+shift+p
    - 输入frame
    - 选择show frames per second (fps) meter
4、rail测量模型
* 什么是rail
    - response:响应
    - animation:动画
    - idle:空闲
    - load:加载
* rail评估标准
    - 响应:处理事件应在50ms以内完成
    - 动画:每10ms产生一帧
    - 空闲:尽可能增加空闲时间
    - 加载:在5s内完成内容加载并可以交互
* 性能测量工具
    - chrome devtools开发调试、性能评测
    - lighthouse网站整体质量评估
    - webpagetest多测试地点、全面性能报告
5、使用webpagetest评估web网站性能
* 网址:webpagetest.org
* 测试操作:
    - website url:网站url
    - test location:测试地点
    - browser:浏览器
    - test settings
        ~ connection:网络连接情况
        ~ number of test to run:测试轮数
        ~ repeat view:首次访问和带有缓存的访问(First View and Repeat View)
        ~ capture video:捕捉视频(√)
* 结果分析
    - performance results
        ~ first byte:首次请求
        ~ start render:首次渲染
        ~ total blocking time:总阻塞时间
    - test results(瀑布视图)
        ~ page is interactive:页面可交互时间
        ~ browser main thread:浏览器主线程占用情况
* 本地部署webpagetest工具(windows)
    - docker pull webpagetest/server
    - docker pull webpagetest/agent
    - docker run -d -p 4000:80 webpagetest/server
    - docker run -d -p 4001:80 --network="host" -e "SERVER_URL=http://localhost:4000/work/" -e "LOCATION=Test" webpagetest/agent
    - 浏览器访问:localhost:4000
6、使用lighthouse分析性能
* 使用方式
    - npm i -g lighthouse
    - lighthouse https://www.bilibili.com/
    - 浏览器访问:LH:Printer
* 报告解读
    - Opportunities:优化点
        ~ Remove unused JavaScript:删除未使用的js
        ~ Eliminate render-blocking resources:消除渲染阻塞资源
    - Diagnostics:诊断
    - Passed audits:通过项
* 确认资源是否需要第一时间加载和解析
    - f12:ctrl+shift+p
    - 输入:show request blocking
    - enable network request blocking(√)
    - add pattern:添加需要阻塞的资源并分析,可使用通配符(如:log*.js)
7、f12-performance
* 方式
    - record:点击开始记录,stop完成记录
    - start profiling and reload page:开始分析并重新加载页面,直到页面加载完毕
* 指标
    - main(主线程):函数调用堆栈
    - timings(时间点):dom加载时间等
    - frames:图像和刷新率
* 耗时活动分析(最下方):summary、bottom-up
8、其它隐藏面板
* network request blocking:网络请求阻塞
* rendering:渲染
    - paint flashing:重绘
    - layout shift regions:布局抖动
    - fps meter:刷新频率
* performance monitor:性能监测
9、常用的性能测量apis
* web标准apis
    - 关键时间节点(navigation timing,resource timing)
    - 网络状态(network apis)
    - 客户端服务端协商(http client hints)&网页显示状态(ui apis)
* 计算时间节点
    - 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
// load事件后触发
window.onload = function () {
    // time to interactive:可交互时间
    let timing = performance.getEntriesByType('navigation')[0];
    // 计算tti = dom
    let tti = timing.domInteractive - timing.fetchStart;
    console.log("TTI: " + tti)
}
// 通过PerformanceObserver得到所有的long tasks对象
let observer = new PerformanceObserver(list => {
    for (const entry of list.getEntries()) {
        console.log(entry)
    }
})
// 监听long tasks
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 changed from " + type +
        "to " + connection.effectiveType)
}

connection.addEventListener('change', updateConnectionStatus)

三、渲染优化

1、现代浏览器网页渲染原理关键渲染路径(critical rendering path)
* 浏览器的渲染流程
    - javascript
    - style
    - layout
    - paint
    - composite
* 浏览器构建对象模型
    - 构建dom对象:html -> dom
    - 构建cssom对象:css -> cssom
* 浏览器构建渲染树
    - dom + cssom -> render tree
2、布局(layouts)与绘制(paint)
* 布局与绘制
    - 渲染树只包含网页需要的节点
    - 布局计算每个节点精确的位置和大小-“盒模型”
    - 绘制是像素化每个节点的过程
* 影响回流的操作
    - 添加/删除元素
    - 操作styles
    - display:none
    - offsetLeft,scrollTop,clientWidth
    - 移动元素位置
    - 修改浏览器大小,字体大小
* 避免layout thrashing
    - 避免回流
    - 读写分离
3、使用fastdom
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>性能优化</title>
    <script src="fastdom.min.js"></script>
</head>
<body>
<div class="app"></div>
<style>
    .app {
        width: 100px;
        height: 100px;
        background: red;
    }
</style>
<script>
    /**
     * 一、使用fastdom批量对dom的读写操作
     *     - 什么是fastdom
     *     - 如何使用fastdom的apis
     */
    window.onload = function () {
        const app = document.querySelector(".app");
        let step = 2;
        (function animationFun() {
            fastdom.measure(() => {
                const osw = app.offsetWidth
                fastdom.mutate(() => {
                    if (osw < 100 || osw > 600) {
                        step *= -1
                    }
                    app.style.width = `${osw + step}px`
                })
            })
            window.requestAnimationFrame(animationFun)
        })()
    }
</script>
</body>
</html>
4、复合线程(compositor thread)与图层(layers)
* 复合线程做什么
    - 将页面拆分图层进行绘制再进行复合
    - 利用devtools了解网页的图层拆分情况
        ~ ctrl+shift+p
        ~ show layers
    - 哪些样式仅影响复合
        ~ transform
        ~ opacity
5、减少重绘(repaint)
* 减少重绘的方案
    - 利用devtools识别paint的瓶颈
    - 利用will-change创建新的图层
* devtools查看重绘
    - ctrl+shift+p
    - show rendering
    - paint flashing
6、高频事件处理函数防抖
function animationFun() {
    console.log(new Date().getTime())
}

let ticking = false
window.addEventListener('pointermove', e => {
    if (ticking) return
    ticking = true
    window.requestAnimationFrame(() => {
        animationFun()
        ticking = false
    })
})
7、react时间调度实现
* 基本原理
    - requestIdleCallback的问题
    - 通过rAF模拟rIC
        ~ rAF是在paint之前执行,rIC是在paint之后执行。
          paint之后可能会有一段idle时间,可以使用rIC执行一些低优先级工作
        ~ rIC参数2(timeout):给任务设置超时时间,如果超时任务还没有被调用,
          则会把任务放入事件循环中

四、代码优化

1、javascript的开销和如何缩短解析时间
* 开销在哪里
    - 加载
    - 解析&编译
    - 执行
* 解决方案
    - code splitting代码拆分,按需加载
    - tree shaking代码减重
* 减少主线程工作量
    - 避免长任务
    - 避免超过1kb的行间脚本
    - 使用rAF和rIC进行时间调度
* progressive bootstrapping
    - 可见不可交互 vs 最小可交互资源集
2、v8编译原理
// 测试返优化过程
// node --trace-opt --trace-deopt index.js:可查看对什么做了优化,对什么做了返优化
const {performance, PerformanceObserver} = require("perf_hooks")
const add = (a, b) => a + b
const num1 = 1
const num2 = 2
performance.mark("start")
for (let i = 0; i < 10000000; i++) {
    add(num1, num2)
}
add(num1, 's')
for (let i = 0; i < 10000000; i++) {
    add(num1, num2)
}
performance.mark("end")
const observer = new PerformanceObserver(list => {
    console.log(list.getEntries()[0])
})
observer.observe({entryTypes: ['measure']})
performance.measure('测量1', 'start', 'end')
* 抽象语法树
    - 源码 -> 抽象语法树 -> 字节码bytecode -> 机器码
    - 编译过程会进行优化
    - 运行时可能发生返优化
* v8优化机制
    - 脚本流:下载的脚本超过30kb,则边下载边开一个独立线程解析,最后合并解析内容
    - 字节码缓存
    - 懒解析:函数声明了并不会立即解析,当使用到该函数时才会解析
3、函数优化
* 函数的解析方式
    - lazy parsing懒解析 vs eager parsing饥饿解析
        ~ 可以给函数体加括号(),使函数体饥饿解析
    - 利用optimize.js优化初次加载时间
4、对象优化
* 对象优化可以做哪些
    - 以相同顺序初始化对象成员,避免隐藏类的调整
    - 实例化后避免添加新属性
    - 尽量使用Array代替array-like对象
    - 避免读取超过数组的长度
    - 避免元素类型转换
// js有21种隐藏类型(hidden class)
class RectArea { // HC0
    constructor(l, w) {
        this.l = l // HC1
        this.w = w // HC2
    }
}

const rect1 = new RectArea(3, 4)
const rect2 = new RectArea(5, 6)
// ********
const car1 = {color: "red"} // HC0
car1.seats = 4 // HC1
const car2 = {seats: 2} // HC2
car2.color = "blue" // HC3
// in-object属性
const car1 = {color: "red"} 
// normal/fast属性,存储property store里,需要通过描述数组简介查找
car1.seats = 4 
// 不如在真实数组上效率高
Array.prototype.forEach.call(arrObj, (value, index) => {
    console.log(`${index}:${value}`)
})
// ********
// 转换的代价比影响优化小
const arr = Array.prototype.slice.call(arrObj, 0)
arr.forEach((value, index) => {
    console.log(`${index}:${value}`)
})
function foo(array) {
    for (let i = 0; i <= array.length; i++) { // 越界比较
        // 1、造成undefined跟数字进行比较
        // 2、沿原型链的查找
        if (array[i] > 1000) {
            console.log(array[i]) // 业务上无效、出错
        }
    }
}
const array = [3, 2, 1] // PACKED_SMI_ELEMENTS
array.push(4.4) // PACKED_DOUBLE_ELEMENTS
5、html优化
* html优化也重要
    - 减少iframes使用
    - 压缩空白符
    - 避免节点深层级嵌套
    - 避免table布局
    - 删除注释
    - css&javascript尽量外链
    - 删除元素默认属性
* 借助工具
    - html-minifier
<iframe id='a'></iframe>
document.getElementById('a').setAttribute('src','url')
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>性能优化</title>
    <style></style>
</head>
<body>
<div></div>
<!-- script最好在body最底部 -->
<script></script>
</body>
</html>
6、css对性能的影响
* 样式计算开销
    - 利用devtools测量样式计算开销
        ~ f12 -> performance -> main -> Recalculate Style
* css层级选择器是从右向左查找元素的
* css优化
    - 降低css对渲染的阻塞
    - 利用gpu进行完成动画
    - 使用contain属性
        ~ contain: layout;:元素内部无法影响元素外部的布局,避免回流
    - 使用font-display属性
posted on 2022-07-09 00:15  一路繁花似锦绣前程  阅读(322)  评论(0编辑  收藏  举报