性能指标和性能检测:前端性能检测指南 - 掘金 (juejin.cn)
1. 资源加载优化
1.1 资源大小方面优化
资源大小方面,主要通过压缩减少体积,删除无用代码减少代码体积。
借助 webpack 的 pligin 等
- TerserWebpackPlugin
webpack5 内置的 plugin , 可以对 JS 进行丑化和压缩
TerserWebpackPlugin | webpack 中文文档
- CssMinimizerPlugin
对 CSS 进行压缩
CssMinimizerWebpackPlugin | webpack 中文文档
- 开启 GZIP
通过 webpack 开启 GZIP 可以对资源进行格式压缩。
CompressionWebpackPlugin | webpack 中文文档
- html-webpack-plugin
在生成环境默认会对 HTML 进行压缩。 也可以修改配置
- Tree-shaking
Webpack 在生成环境下默认是开启的。可以删除一些无用的代码。
-
图片压缩 ,或使用 WEBP
1.2 资源数量方面
在资源数量上,有些地方可以合并请求,少数请求解决多个资源的问题。有些地方需要分多个请求,加快资源下载速度
减少资源数量
-
雪碧图
-
将一些小的图片转成 base64 , svg 。 内嵌到 HTML 中。
-
SSR ,服务端渲染
增加资源数量
- SplitChunksPlugin
进行分包的好处其实也可以帮助减少包的总体积。它可以把一些共享的包单独打包出来。
第二就是可以把大的包分成小的多个包。
浏览器是支持多个连接下载资源的。因此将一个大的包分成多个小的包。可以加快资源下载的速度。
1.3 资源位置方面
- 使用 CND
CND 可以把静态资源缓存到 CDN 服务器。从距离和速度上加快资源的访问
- 使用 service worker
使用 service worker 将资源缓存到用户本地
- 资源划分到子域名
浏览器可以对一个域名建立多个连接。不过是有限制的。一般 6个。
而将资源分到子域名,则可以增加并行请求的数量
- 避免重定向
重定向会多一个请求的往返
1.4 请求时机
- Script 标签后置 ,或者增加 async 或 defer 属性
Script 的下载会堵塞 HTML 的解析,下载完执行也会堵塞 HTML 的解析,那么就可能导致 script 标签后的资源无法及时的请求
- 懒加载和预加载
懒加载可以减少并发的请求数量。预加载可以利用空闲时间进行加载
- DNS 预查询和 TCP 预连接
dns-prefetch 可以进行 NDS 预查询。在真正请求资源时,减少查询的时间。preconnect 可以进行预连接
不过查询的域名和文档的域名需要不一样,也就是文档需要有使用到第三方服务器的资源才有用。
<link rel="preconnect" href="https://abc.com" crossorigin>
<link rel="dns-prefetch" href="https://abc.com">
- 资源预加载
Rel = preload 可以预加载资源 。
<link rel="preload" href="style.css" as="style" />
<link rel="preload" href="main.js" as="script" />
1.5 使用 http2
http2 有多路复用的特性。一定程度上解决了队头堵塞的问题。多个请求并行使用一个 TCP 连接。可以加快资源的加载
2. 网页优化
2.1 首屏加载
首屏加载可参考上面的资源加载。资源加载的优化可加快首屏文档,样式表等的加载。
此外还有其他方法可以加速首屏的加载!
- CSS 放在 head 内进行加载
HTML 是可以边解析边渲染的。如果 CSS 放在末尾,就会先渲染没有样式的DOM 。 而如果是先加载 CSS , 只有 CSSDOM 解析完成,之后 HTML 才可以边解析边渲染。那么就可以一次性渲染出有样式的页面。
- Script 放在末尾 , 或者增加 defer 或 async
script 的加载和执行都会堵塞 HTML的解析和渲染。
- 减少使用 iframe 或者异步添加
Iframe 于主页面共享共享连接池 。 占用部分的连接
- 使用 SSR
服务端渲染可以减少部分请求。并且服务器渲染时从自己身上请求资源更快
- 使用骨架屏
降低或消去用户对屏幕空白的感知 。起码让用户知道在加载中,而不是打不开
- 避免重定向
首页重定向并不明智,增加了一个请求往返。
- 修改首页内容
首页如果有一些大的图片,那么加载的时间可能就会增加很多。合理使用 CSS 。
降低首页渲染的复杂度。当首页基本完成后
- Service worker
Service worker 可以缓存一些资源。在用户再次打开时,可以快速的从磁盘中读取。
- HTML 内容优化
HTML有些内容,如果暂时不显示,可以通过 JS 动态插入。因为即使不显示 ,也会被解析。
- CSS 内容优化
使用 CSS3的一些特性,可以开启 GPU 加速渲染
另外是避免重复的叠加类目,标签名。因为 CSSDOM 的解析是递归解析的。即使目前 CSS 解析很快
2.2 渲染优化
合理利用 GPU 加速渲染
页面的渲染大部分还是使用 CPU 的。 CSS3 中一些属性,可以使页面分层交给 CPU 进行渲染。从而加速渲染过程
- 使用 CSS 动画或者静态图片代替 GIF (GIF 比较消耗 CPU)
- 使用 CSS 动画代替 JS 动画
JS 引擎和渲染引擎是不同的。JS 操作 DOM 的需要昂贵的代价
- 利用 CSS3的一些属性,开启 GPU 渲染
比如以下这些:
-
-
Transform
-
Filter
-
Will-change
-
Opacity
-
减少回流
DOM 操作很昂贵 。特别是可能触发回流的 DOM操作。会使页面变得卡顿,消耗更多的 CPU
常见引起回流的情况:
-
添加或删除 DOM 节点
-
** 修改 DOM 节点的位置和大小**
-
游览器的大小发生改变
-
读取 width,getComputedStyle ,getBoundingClientRect 等需要计算的属性
针对这些情况,我们需要有一些相应的措施,去减少这些情况的发生:
-
visibility:hidden 或者 opacity:0 替换 display: none 。
Hidden 是隐藏,但还在,而display:none 会删除节点,可能导致其他节点的位置变化!导致回流!
-
一次性批量处理节点
频繁的删除,添加节点,会都在页面变得很卡顿!因此要么减少次数,要么一次批量的添加或删除!
- 一次性删除
比如把可能频繁删除阶段列表放在一个盒子中 ,那么删除时删除列表的父节点即可。
- 使用 innerHTML
当你需要拼接比较复杂和内容和节点比较多的节点时,可以使用 字符串 。然后通过 innerHTML 添加到页面中!
需要注意 XSS 攻击!
- 使用文档片段 (document fragment)
Document.createDocumentFragment() - Web API 接口参考 | MDN
文档片段可以理解为一个节点,但这个节点插入页面后,最后是不会被渲染出来的!
相当于一个临时的容器!
举个栗子:
let Parent = document.querySelector("#parent")
for(let i = 0;i<3;i++){
let Son = document.creatElemet('div')
Son.innerText = 'Son:'+i
parent.append(Son)
}
上面的代码,会执行3个循环,三个循环都会对页面进行插入操作!三次都会重复回流!
当次数过多过频繁时,浏览器吃不消了,就可能出现卡顿!
使用文档碎片
let fragment = document.createDocumentFragment()
for(let i = 0;i<3;i++){
let Son = document.creatElemet('div')
Son.innerText = 'Son:'+i
fragment .append(Son)
}
let Parent = document.querySelector("#parent")
Parent.append(fragment )
如此,则仅触发一次回流!
- 隐藏再显示
如果对某个节点内部需要有很大删改。可以先让节点 display:none 。使
对节点操作完后,再显示回去!
- 克隆代替
使用 cloneNode 深克隆节点 , 这样子如果此节点操作过久,此期间也不会影响页面!
待完成后代替原节点!
-
批量处理样式
对样式频繁的修改,也可能导致多次重复回流!比如:
听说旧一些的浏览器可能会
let node = documet.querySelector("#node")
node.style.width = '100px'
node.style.height= '200px'
node.style.padding= '10px'
因此,应该尽可能的,一次性处理完
- 使用 class
通过 className , classList , setAttribute 等API 修改节点的 Class !一次性修改多个样式!
- 使用 cssText
cssText 。cssText 可以以字符串方式读取和设置样式。因此一次性直接修改多个样式!
-
缓存需要计算计数的数据
let node = documet.querySelector("#node")
for(let i = 0;i<3;i++){
console.log(node.offsetWidth)
}
上面的代码,模拟了我们需要多个地方使用到 node 的宽度的情况。但每次访问其实都会重新计算 node 的宽度。
因此可以把它缓存起来!
let width= documet.querySelector("#node").offsetWidth
for(let i = 0;i<3;i++){
console.log(width)
}
2.3 内存优化
内存比较任意出现的问题就是内存泄漏。
以下这些情况,就可能导致内存泄漏,不利于 GC (garbage collection)
或者还保持着无用的数据。占用空间
- 添加全局变量
变量添加到全局,当变量不使用时,是无法被回收的
- 变量一直保存对 DOM 的引用
当我们操作DOM 时,会把DOM保存到变量。但操作完,就可以把变量设置为 Null 了。特别是该变量不会及时被回收时。还可能的情况就是,DOM 从页面删除了,但变量还引用着
- 谨慎使用闭包
闭包的作用之一就是保存变量不被回收。大量使用闭包,可能导致大量的内存无法被及时回收
- 使用 weakMap 和 weakSet
如果把对象放入 map 或 set 中,很容易忘记去及时的删掉。那么对象就一直被引用则,无法回收
2.4 事件响应优化
-
使用防抖和节流
-
使用
requestAnimationFrame -
事件代理
-
及时移除不需要的事件监听
2.5 长任务和复杂计算
- requestIdleCallback
在浏览器空闲时执行
- Web worker
开启另一个线程处理复杂任务
3. SEO 优化
3.1 合理的文档描述
- Title 文本标题,或网站名称
- Keywords 关键词
- description 描述
- robots 设置可以被爬虫读取的文件
<title>摆烂</title>
<meta name="keywords" content="摆烂,XXX" />
<meta name="description" content="一个摆烂的网站" />
<meta name="robots" content="index" />
3.2 避免使用 iframe
Iframe 的内容不被搜索引擎考虑
3.3 使用语义化标签
语义化标签可以使文档的结构更清晰
3.4 文案处理
使用更可能搜索到的文本和图片,增加被搜索到的概率
3.5 及时的,经常的更新内容
内容的更新也是SEO 参考的一部分
3.6 还有就是,非前端的任务了
比如网站的合理定位,用户多,访问多 ,SEO 也就好了