前端性能优化

前言

前端性能优化对于一个前端来说比如是“演员的自我修养”,想作一名好的前端,必定要懂如何性能优化。另外性能优化基本上是面试必问的一个问题了,这一块能回答的很漂亮,无疑是一个加分项。javascript

感知性能

对于用户来讲,用户的感知性能才是最重要的,简单讲,就是让用户感受你的网站访问很快,而且感知性能没有衡量标准。css

若是一个页面的加载时间很长,咱们也能够经过一些方式让用户以为没有那么慢。html

loading

最基础的菊花等待~前端

骨架屏

能够参考antd-design的骨架屏java

客观性能

对于开发者来讲,性能指标是能够客观度量的,咱们能够经过一些手段来优化 Web 性能,使这些度量指标达到开发者设定的标准。node

客观性能是指,从用户输入url开始,到下载、解析和执行全部资源以及最终绘制的整个过程的时间度量。webpack

浏览器打开网页的过程web

1.浏览器对URL进行DNS解析面试

2.浏览器与服务器进行TCP链接npm

3.浏览器发出HTTP请求

4.服务器返回HTTP响应

5.浏览器进行页面渲染

性能指标

image.png Google提出的网站用户体验的三大核心指标

LCP、FID、CLS

LCP 表明了页面的速度指标

FID 表明了页面的交互体验指标

CLS 表明了页面的稳定指标

经常使用的性能优化方法

减小请求次数

资源合并

使用打包工具,对js、css资源进行打包,避免文件过多

使用雪碧图

图片走cdn等

缓存

HTTP Cache

强缓存

Expires http1.0的产物,如今已经不用

Cache-Control

针对浏览器和服务器时间不一样步,加入了新的缓存方案;此次服务器不是直接告诉浏览器过时时间,而是告诉一个相对时间Cache-Control=10秒,意思是10秒内,直接使用浏览器缓存

app.get('/demo.js',(req, res)=>{
    let jsPath = path.resolve(__dirname,'./static/js/demo.js');
    let cont = fs.readFileSync(jsPath);
    res.setHeader('Cache-Control', 'public,max-age=120') //2分钟
    res.end(cont)
})
复制代码

强缓存的特色是不须要询问服务器

协商缓存

强制缓存的弊端很明显,即每次都是根据时间来判断缓存是否过时;可是当到达过时时间后,若是文件没有改动,再次去获取文件就有点浪费服务器的资源了

协商缓存是请求服务器后,服务器来判断是返回新的资源,仍是告诉浏览器使用旧的资源

有根据最后修改时间、和文件内容是否改动两种协商方法

Last-Modified和If-Modified-Since

只能精确到秒,若是一秒内屡次修改,就感知不到

确实修改后,但内容没变,也会新请求

ETag和If-None-Match

解决文件修改时间不精确带来的问题,只有当文件内容改变时,ETag才改变

image.png 服务器读取磁盘文件demo.js,返给浏览器,同时带上文件上次修改时间 Last-Modified(GMT标准格式)

app.get('/demo.js',(req, res)=>{
    let jsPath = path.resolve(__dirname,'./static/js/demo.js')
    let cont = fs.readFileSync(jsPath);
    let status = fs.statSync(jsPath)

    let lastModified = status.mtime.toUTCString()
    if(lastModified === req.headers['if-modified-since']){
        res.writeHead(304, 'Not Modified')
        res.end()
    } else {
        res.setHeader('Cache-Control', 'public,max-age=5')
        res.setHeader('Last-Modified', lastModified)
        res.writeHead(200, 'OK')
        res.end(cont)
    }
})
复制代码

服务器读取磁盘文件demo.js,返给浏览器,同时带上文件的惟一标识ETag

Memory Cache & Disk Cache

他们是配合http缓存的。 memory cache命中最快,可是它周期较短,base64的图片,较小的js和css可以有较大概率被写进内存,这没有肯定的定论。 其余较大的js、css和图片等会被直接写进硬盘,进行缓存。

存储

cookie

最大4K,存储一些用户登陆状态

webStorage

分为sessionStorage和localStorage,大小在5-10M,键值对存储,区别是生命周期不一样,sessionStorage在tab关闭后,就不存在了,localStorage永久存在,除非主动删除。

减小请求体积

资源压缩

Gzip

传输的时候能够在服务器端开启gzip压缩,能够有效减小传输文件的大小,能够在响应头content-encoding: gzip中看到。

代码压缩

使用一些代码压缩工具,删除掉无用的注释、空行和缩减名称等操做来减小文件体积。

图片压缩

图片是网页上占用不少流量的一种资源。若是在图片损失一些颜色和像素的状况下并不会对用户体验有太大影响,那么就应该对图片进行压缩。

图片压缩

PNG无损格式,压缩率通常,支持透明背景,经常使用于透明图片或者Icon等。

JPG有损格式,压缩率较好,经常使用于复杂的大图,不支持透明背景。

SVG矢量图形,可编程。在各分辨率下不失真,可是渲染复杂图形较消耗性能。经常使用于简单图形。

WEBP无损格式,相较于PNG和JPG来讲,压缩率更好,同时支持透明背景。惟一的缺点是兼容性很差。可用于兼容性好的浏览器,用JPG和PNG作回退机制。

服务器发送HTTP相应

减小响应时间

利用CDN(高流量大并发状况下)

cdn全称content delivery network。它是依靠部署在各地区的边缘服务器,达到用户就近获取内容,下降网络拥塞,提升用户访问速度和命中率的目的。它主要的关键技术是内容存储和分发技术。

下降页面初始渲染时间

预渲染

将浏览器解析 javascript 动态渲染页面的这部分工做,在打包阶段就完成了,(只构建了静态数据)换个说法在构建过程当中,webpack 经过使用 prerender-spa-plugin 插件生成静态结构的 html。

服务器渲染(SSR)

CSR 项目的 TTFP(Time To First Page)时间比较长,参考以前的图例,在 CSR 的页面渲染流程中,首先要加载 HTML 文件,以后要下载页面所需的 JavaScript 文件,而后 JavaScript 文件渲染生成页面。在这个渲染过程当中至少涉及到两个 HTTP 请求周期

node作为中间层,让 React 代码在服务器端先执行一次,使得用户下载的 HTML 已经包含了全部的页面展现内容,这样,页面展现的过程只须要经历一个 HTTP 请求周期

同时,因为 HTML 中已经包含了网页的全部内容,因此网页的 SEO 效果也会变的很是好。以后,咱们让 React 代码在客户端再次执行,为 HTML 网页中的内容添加数据及事件的绑定,页面就具有了 React 的各类交互能力。

页面渲染

减小阻塞

js阻塞

当html解析遇到js会先下载和执行js文件,这是为了防止js操做了dom等状况的发生。但咱们做为操做者,能够人为的指定,那些元素能够延迟加载。

为script标签指定 async 或 defer来延迟脚本。

async表示js不会阻塞,并行执行,但会在下载完成后马上执行,谁先加载好谁执行

defer则会在下载完成而且整个文档解析完成、DOMContentLoaded事件被触发前开始执行,按照顺序执行

css阻塞

css会阻塞html进行渲染,可是为了界面没有任何样式的展示在用户面前,所以咱们须要将css提早

减小渲染次数

避免回流和重绘

回流又称为重排,即经过某种手段改变了元素的位置大小等信息,致使浏览器须要从新计算和渲染的过程。而重绘只是被改变了样式如背景和颜色等。

不管是哪种,都会耗费性能,因此咱们要避免进行循环操做。

减小渲染节点数量

懒加载

对于一些不在用户视图内的元素,咱们能够在展现的时候先不进行渲染,直到该元素出如今了视图内再进行渲染。

懒加载包括对图片或者dom元素的加载和渲染

提升渲染效率

减小DOM节点的操做

浏览器的渲染引擎和js引擎是分离的,能够想象在js引擎和渲染引擎之间进行”跨界交流“并不简单,这个开销很大,全部咱们要尽可能避免这种操做。

下降选择器的复杂性

.box:nth-last-child(-n+1) .title { /* styles */ }

浏览器计算此结果可能须要大量的时间,但咱们能够把选择器的预期行为更改成一个类:

.final-box-title { /* styles */ }

避免强制同步布局和布局抖动

浏览器每次布局计算时几乎老是会做用到整个DOM,若是有大量元素,将会须要很长时间才能计算出全部元素尺寸和位置。

因此咱们应该避免在运行时动态的修改集合属性(宽高)。若是没法避免,优先使用Flexbox

内联首屏关键css(Critical CSS)

浏览器在将咱们的页面呈现给用户以前必定要先完成页面引用到的CSS文件的下载和解析(download and parse),因此link标签连接的CSS资源是渲染阻塞的(render-blocking)。若是CSS文件很是大或者网络的情况不好,渲染阻塞的CSS会严重影响用户体验。针对这个问题,社区有一种优化方案就是将一些重要的CSS代码(Critical CSS)直接放在头部的style标签内,其他的CSS代码再进行异步加载,这样浏览器在解析完HTML后就能够直接渲染页面了。

/* critical CSS */ ...body goes here

能够直接npm I criticalcss

那么如何定义Critical CSS呢?放在head标签内的CSS固然是越少越好,由于太多的内容会加大html的体积,因此咱们通常把用户须要在首屏看到的(above the fold)页面要用到的最少CSS提取为Critical CSS。因为页面在不一样的设备上展现的效果不一样,对应着的Critical CSS内容也会有所差异,所以Critical CSS的提取是一个十分复杂的过程,虽然社区有不少对应的工具但是效果都差强人意。CSS-in-JS却能够很好地支持Critical CSS的生成。在CSS-in-JS中,因为CSS是和组件绑定在一块儿的,只有当组件挂载到页面上的时候,它们的CSS样式才会被插入到页面的style标签内,因此很容易就能够知道哪些CSS样式须要在首屏渲染的时候发送给客户端,再配合打包工具的Code Splitting功能,能够将加载到页面的代码最小化,从而达到Critical CSS的效果。

换句话来讲,CSS-in-JS经过增长一点加载的JS体积就能够避免另外发一次请求来获取其它的CSS文件。并且一些CSS-in-JS的实现(例如styled-components)对Critical CSS是自动支持的。

相关文章
相关标签/搜索