# 前端如何优化网站性能
# 常规优化
- 减少
HTTP
请求数量
- 使用
CSS
雪碧图,减少图片总字节数 - 合并
CSS
和JS
文件 - 采用懒加载
- 利用浏览器缓存
- 减少重排
重排是
DOM
的变化影响到了元素的几何属性(宽和高),浏览器会重新计算元素的几何属性,会使渲染树中受到影响的部分失效,浏览器会验证DOM
树上的所有其它结点的visibility
属性,这也是重排低效的原因。如果重排的过于频繁,CPU
使用率就会急剧上升。减少重排,如果需要在DOM
操作时添加样式,尽量使用增加class
属性,而不是通过style
操作样式。
- 减少
Dom
操作 - 图标使用
IconFont
替换 - 避免
head
标签JS
堵塞
所有放在
head
标签里的css
和js
都会堵塞渲染。如果这些CSS
和JS
需要加载和解析很久的话,那么页面就空白了。
- 减少
head
标签里的CSS
资源
由于
CSS
必须要放在head
标签里面,如果放在body
里面,一旦加载好之后,又会对layout
好的dom
进行重排,样式可能又会发生闪烁。但是一旦放在head
标签里面又会堵塞页面渲染,若要加载很久,页面就会保持空白状态。所以要尽可能地减少CSS
的代码量。
TIP
有两种解决办法,第一种是把script
放到body
后面,这也是很多网站采取的方法。第二种是给script
加defer
的属性,defer
是html5
新增的属性。一旦script
是defer
延迟的,那么这个script
将会异步加载,但不会马上执行,会在readystatechange
变为Interactive
后按顺序依次执行
- 不要放太多
base64
在CSS
里面
放太多
base64
放在CSS
里面,会导致CSS
极速膨胀,把一张3k
的图片转成base64
,体积将会变成4k
- 自适应
DPR
加载图片
在高分辨率显示屏如
2x
上,在页面中使用二倍图可以保证清晰度,但是当此页面在低DPR
设备打开时,我们只需要50%
长宽的图片就能保证显示效果,而此时带宽开销却是一样的。所以为了节约传输流量,我们需要告诉浏览器,根于不同的DPR
加载不同尺寸的图片,通常有以下三种方法:
<!-- 第一种 -->
<picture>
<source srcset="photo@3x.jpg" media="(min-width: 800px)">
<source srcset="photo@2x.jpg" media="(min-width: 600px)">
<img srcset="photo.jpg">
</picture>
<!-- 第二种 -->
<img src="photo.png" srcset="photo@2x.png 2x, photo@3x.png 3x" alt="photo" />
2
3
4
5
6
7
8
9
/* 第三种 */
background-image: image-set("photo.png" 1x,
"photo@2x.png" 2x,
"photo@3x.png" 3x);
2
3
4
TIP
像素相关概念:
DP
设备像素,又名物理像素px,即设备屏幕上真实的物理像素,以矩阵的形式排列,如iphone X
屏幕分辨率为2436*1125
,即屏幕每行包含1125
个物理像素,每列包含2436
个物理像素。DIP
设备无关像素,是一种基于屏幕坐标的抽象像素,应用程序以抽象像素为单位,如我们CSS
中使用的px
,实际渲染时通过底层程序转换为物理像素。DPR
设备像素比,设备像素 / 设备无关像素的值即为设备像素比,在Javascript
中可以通过window.devicePixelRatio
来获取。
# vue项目中的性能优化
v-if/v-show
v-if
是 真正 的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建;也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。v-show
就简单得多, 不管初始条件是什么,元素总是会被渲染,并且只是简单地基于CSS
的display
属性进行切换。所以,v-if
适用于在运行时很少改变条件,不需要频繁切换条件的场景;v-show
则适用于需要非常频繁切换条件的场景。
v-for
避免使用v-if
v-for
比v-if
优先级高,如果每一次都需要遍历整个数组,将会影响速度,尤其是当之需要渲染很小一部分的时候,必要情况下应该替换成computed
属性。
- 长列表性能优化
Vue
会通过Object.defineProperty
对数据进行劫持,来实现视图响应数据的变化,然而有些时候我们的组件就是纯粹的数据展示,不会有任何改变,我们就不需要Vue
来劫持我们的数据,在大量数据展示的情况下,这能够很明显的减少组件初始化的时间,那如何禁止Vue
劫持我们的数据呢?可以通过Object.freeze
方法来冻结一个对象,一旦被冻结的对象就再也不能被修改了。
- 事件的销毁
Vue
组件销毁时,会自动清理它与其它实例的连接,解绑它的全部指令及事件监听器,但是仅限于组件本身的事件。如果在js
内使用addEventListener
等方式是不会自动销毁的,我们需要在组件销毁时手动移除这些事件的监听,以免造成内存泄露
beforeDestroy() {
removeEventListener('click', this.click, false)
}
2
3
- 图片懒加载
对于图片过多的页面,为了加速页面加载速度,所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。这样对于页面加载性能上会有很大的提升,也提高了用户体验。我们在项目中使用
Vue
的vue-lazyload
插件
- 路由懒加载
Vue
是单页面应用,可能会有很多的路由引入 ,这样使用webpcak
打包后的文件很大,当进入首页时,加载的资源过多,页面会出现白屏的情况,不利于用户体验。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应的组件,这样就更加高效了。这样会大大提高首屏显示的速度,但是可能其他的页面的速度就会降下来。
TIP
在App.vue
组件中使用公用头部,如果使用了懒加载,那么某个不需要该公用组件的路由,刷新就会短暂显示头部组件
第三方插件按需引入
babel-plugin-component
提取公共代码 如果项目中没有去将每个页面的第三方库和公共模块提取出来,则项目会存在以下问题:
- 相同的资源被重复加载,浪费用户的流量和服务器的成本。
- 每个页面需要加载的资源太大,导致网页首屏加载缓慢,影响用户体验。
所以我们需要将多个页面的公共代码抽离成单独的文件,来优化以上问题 。
Webpack
内置了专门用于提取多个Chunk
中的公共部分的插件CommonsChunkPlugin
- 去除多余
css
--库purgecss
- 动态组件记得使用
keep-alive
# SPA首屏加载速度慢如何解决
- 利用
DOMContentLoad
/performance
计算首屏时间
// 方案一:
document.addEventListener('DOMContentLoaded', (event) => {
console.log('first contentful painting');
});
// 方案二:
performance.getEntriesByName("first-contentful-paint")[0].startTime
// performance.getEntriesByName("first-contentful-paint")[0]
// 会返回一个 PerformancePaintTiming的实例,结构如下:
{
name: "first-contentful-paint",
entryType: "paint",
startTime: 507.80000002123415,
duration: 0,
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
加载慢的原因
- 网络延时问题
- 资源文件体积是否过大
- 资源是否重复发送请求去加载
- 加载脚本的时候,渲染内容堵塞
解决方案
- 减小入口文件体积
- 静态资源本地缓存
- UI框架按需加载
- 图片资源的压缩
- 组件重复打包
- 开启
GZip
压缩 - 使用
SSR
TIP
# 减少入口文件体积
常用的手段是路由懒加载,把不同路由对应的组件分割成不同的代码块,待路由被请求的时候会单独打包路由,使得入口文件变小,加载速度大大增加
# 静态资源本地缓存
后端返回资源:
- 采用
HTTP
缓存,设置Cache-Control,Last-Modified,Etag
等响应头 - 采用
Service Worker
离线缓存
前端合理使用localStorage
# 插件按需加载
# 开启GZip
压缩
npm i compression-webpack-plugin -D
在vue.config.js
中引入并修改webpack
配置
const CompressionPlugin = require('compression-webpack-plugin')
configureWebpack: (config) => {
if (process.env.NODE_ENV === 'production') {
// 为生产环境修改配置...
config.mode = 'production'
return {
plugins: [new CompressionPlugin({
test: /\.js$|\.html$|\.css/, //匹配文件名
threshold: 10240, //对超过10k的数据进行压缩
deleteOriginalAssets: false //是否删除原文件
})]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
全面的首屏优化方案