2021-08-17¶
统计信息:字数 9178 阅读19分钟
12-2 前端性能优化¶
这节课讲的比较粗,没有具体将优化非内容,具体情况看网易的笔记。
分析角度:从面试题入手¶
浏览器输入 URL 到界面显示的过程是什么?具体怎么优化?
浏览器输入 URL,通过DNS域名解析,转换成IP地址。访问这个IP地址,服务器准备HTML返回浏览器。浏览器解析HTML,同时请求HTML内部的CSS和媒体文件等,解析HTML生成 DOM 树,CSS 返回后,生成渲染树,浏览器显示网页。
在这个过程中实现优化:
网络优化:DNS、CDN(全球不同位置部署服务器,用户选择最近的位置接收文件)少加载文件,减少请求数量(webpack压缩JS和CSS文件),CSS图标使用雪碧图;
多媒体优化:图片优化、Gzip压缩组件(压缩请求头等,减少HTTP的响应头的大小,减少响应时间)压缩图片大小(加快传输速度,使用小格式jpg或者webp文件);懒加载图片;
JS优化:使用SSR中间层;首屏优化;优化cookie;;回流重绘;防抖节流。减少DOM数量:DOM数量越少,那么需要下载的内容和需要处理的事件就越少。
移动端:列表部分渲染。移动端和PC端请求不同的资源
性能指标¶
Performance API
window.performance.timing 获取时间对象,然后判断不同阶段耗时
TCP 链接耗时:connectEnd - connectStart
DNS 查询耗时:domainLookupEnd - domainLookupStart
TTFB(获取首字节耗时):responseStart - navigationStart
Dom Ready 耗时: domContentLoadedEventStart - navigationStart
DOM 资源下载:responseEnd - responseStart
主要是后两项耗时。
或者直接打开浏览器Performance可视化显示界面加载过程,可以分析具体某短时间耗时的原因。
FPS:衡量动画流畅度(帧率),越大越好
CPU:展现CPU的使用情况(空闲性能、脚本加载时间等)
NET:请求花费具体时间
HEAP:请求过程中堆内存使用情况
FP(first paint):某一元素首次渲染时间
FCP(first contentful paint):白屏时间(界面首次渲染文本图像等非白色画布)
TTI(time to interactive)可交互时间(DOM树构建后,用户就可以正常交互,这个是重要的用户体验指标)。
可以本地安装 Lighthouse 工具,查看某个网站的性能渲染情况,会输出一个可视化报告来分析性能。——现在浏览器下面已经安装这个插件,可以查看某个网页的性能等。
DNS prefetch
IP协议负责找到对应的地址
TCP协议负责数据完整性和有序性,三次握手
HTTP负责应用层数据
缓存¶
缓存的目的:客户端发出请求,请求到资源。如果第二次发出请求,直接使用本地的资源,可以减少请求的时间和消耗。第三方的库(vue)或者不变的JS代码,可以使用缓存资源,这样可以减少开销。
缓存分类:强缓存(expires、cachecontrol )、协商缓存
强制缓存¶
expires:强缓存的关键字段,指定一个具体的绝对之间作为缓存资源的过期时间。再次发起请求前,看当前的时间下,已经缓存的文件是否过期。如果没有过期,直接使用缓存文件。如果过期,重新请求资源。存在的问题:如果本地时间和服务端时间存在误差,那么缓存可能失效。
cache-control:强缓存的关键字段:设置一个相对时间,精确控制资源的缓存(设置 public private no-cache no-store max-age s-maxage 等具体参数控制缓存的使用对象和使用时间)。cache-control 优先于 expires。
协商缓存¶
客户端发起请求时,把本地缓存的 last-modify 发送到服务端。服务端协商判断是否使用缓存。如果这个资源更改了,服务端返回200,并返回新的资源;如果服务端资源没有更改,服务端返回304。
如果客户端发送了一个带条件的GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个304状态码。简单的表达就是:服务端已经执行了GET,但文件未变化。
缺陷:这个时间精确到秒,如果文件在1S内已经修改,就不能使用这个技术。可以设置 last-modified/If-modified-since 加上 etag/if-none-match 匹配缓存系统,相对稳定。
可以使用 webpack 结合 nginx Apache 设置缓存字段(服务端运维)。
缓存流程¶
- 获取文件
- 文件返回 expires or cache-control,设置过期时间,带上 etag or last-modified 字段
- 再次请求。浏览器先查询 express or cache-control 是否过期。如果没有过期,强缓存生效,不发出网络请求,直接使用缓存文件。如果强缓存失效,带上 etag or last-modified,使用 if-none-match or if-modified-since 字段,询问服务器是否过期
- 如果没有过期,直接使用304,使用缓存文件;如果过期,直接使用200,返回新文件。
webpack 打包缓存问题 1. hash 整个项目的hash 2. chunkhash 入口文件依赖 hash 3. contenthash 文件内容的 hash
浏览器渲染原理¶
首页白屏优化¶
page-skeleton-webpack-plugin 插件,或者 ant-design 插件
界面初始加载时,具体的数据和图片还没有加载,可以先加载一个骨架屏(loading),然后数据请求结束后,使用真实的数据替换原来的loading状态。
蚂蚁 ant-design 使用了这个组件 https://ant.design/components/skeleton-cn/
网络较慢,需要长时间等待加载处理的情况下。
图文信息内容较多的列表/卡片中。
只在第一次加载数据的时候使用。
import { Skeleton } from 'antd';
ReactDOM.render(<Skeleton />, mountNode);
微任务:microTask
不同的JS执行的事件队列不同,执行顺序不同。微任务优先执行。
防抖节流¶
防抖:界面滚动时,隔100ms触发一次回调函数,共执行N次函数。
节流:频繁滚动界面,直到最后一次滚动完,间隔100ms后,执行一次函数。
这两个很关键,最好实现手写函数(locash中实现了这个函数)
const debounce = (fn, wait = 100) => {
let timer = null;
return (...args) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
fn.apply(this, args);
}, wait);
};
};
const throttle = (fn, wait = 100) => {
let lastTime = 0; // 这里不能用const
return function(...args) {
let now = new Date();
if (now - lastTime > wait) {
lastTime = now;
fn.apply(this, args);
}
}
}
setInterval(
throttle(() => {
console.log(1);
}, 1000),
20
);
注意,apply 和 call 的 区别:apply 第二个参数是数组,call 的第二-N个参数是独立的元素。所以 apply 使用范围更多。A.fn.apply(B, args) 这样B就可以使用A上面的方法。上面的例子中,this 函数使用了 func 的方法,参数是 args,并执行
let A = {
add: function(a, b) {
return a + b;
}
};
let B = {
'age': 20,
'age2': 30
};
A.add.apply(B, [10, 20]);
A.add.call(B, 20, 30);
图片懒加载¶
技术流程:如果界面很长,需要到滚动到图片位置时,才显示具体的图片。
默认的图片中,设置一个 SRC 属性,暂时存放一个虚拟的路径,这个路径不会发出请求。然后在 data-src 中,放置一个真实的路径。当界面滚动时,计算图片的位置和界面滚动的距离,如果图片即将显示或者已经显示,那么使用JS更改图片的SRC属性,发出请求,显示图片。否则不显示真实的图片。
const imgs = document.getElementsByTagName('img');
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
let num = 0;
function lazyload() {
for (let i = num; i < imgs.length; i++) {
let distance = viewHeight - imgs[i].getBoundingClientRect().top;
if (distance >= 0) {
imgs[i].src = imgs[i].getAttribute('data-src');
num = i + 1;
}
}
}
<img src='loading.png' data-src='http://123.jpg'/>
服务端性能优化¶
SSR 实现
如果是单页面,很少的用户交互,在客户端渲染;如果是首页,在服务端直接渲染成HTML,用户端直接显示,不需要浏览器再次渲染JS代码。具体内容详见网易课程。
移动端性能优化¶
移动端的特点:界面很小,内容很多,通常使用列表显示部分内容。此时可以使用列表中部分显示实现性能优化(获取界面的显示的列表,和未显示的列表Index,未显示的部分使用DIV占位实现)。
框架优化¶
pureComponent
async-component
代码可视化,webpack 插件
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = {
plugins: [
new BundleAnalyzerPlugin()
]
};
企业中如何实现性能优化?从上面的角度回答(Performance进行测试,性能监控,然后根据问题,有针对性的进行优化)。