谈谈你所知道的前端性能优化方法?
首先这道题是发散题,涉及的点可多可少,主要考察平时对该领域的一个总结。
总体来说可以分为客户端和服务端的优化。
客户端:
1 减少HTTP请求
都说要减少http请求,那请求多了到底会怎么样呢?
- 首选,每个请求都是有成本的,需要经过dns寻址,与服务器建立连接,发送数据,等待服务器响应,接手数据这样一个“漫长”而复杂的过程。
- 其次,浏览器对单个域名下的并发请求的数量有上限,所以浏览器需要分批进行请求,因此会增加用户的等待时间。
减少http请求的主要途径包含如下:
- 从设计层面简化页面
如果你的页面像google或者百度首页一样简单,保持页面简洁、减少资源的使用是最直接有效的。
- 合理的使用缓存
缓存的力量是强大的,通过设置expires
和cache-control
实现一个很长的过期头,可以有效的减少资源请求,通过last-modifed
和etag
可以节省带宽的资源。
- 资源的合并与压缩
资源的压缩包含:图片资源压缩(如雪碧图、图片base64等),css样式合并,js脚本合并等。
- 图片懒加载
这条策略实际上并不能减少页面的http请求数,但是却能在特定情况或者说在首屏加载的时候减少http请求数。当用户滚动屏幕的时候,将之前缓存的图片地址依次添加到图片的src
中进行加载。
2 将script脚本置底
这其实是一个很好又特容易的优化方案,为什么要这样做呢?
原因是由于浏览进行页面渲染是自上而下的,当遇到js脚本内容时会阻塞页面继续向下渲染,会先下载js脚本并执行。这样一来,就会影响到整个页面的加载速度从而影响到用户的等待时间。
3 将css放在head中
同上,简单而又有效
如果将css放在body中,则浏览器有可能还未下载或解析到css就已经开始渲染页面了,当解析到css或者遇到css下载时,页面则又会停止渲染(因为浏览器渲染机制问题),等css解析完成以后再进行页面的重新渲染(之前已经渲染的内容也需要重新渲染)。
4 代码分割
听到这一条感觉有点违背了第一条减少http请求,但其实并未然。如果所有的资源都打包到一个js文件中,毫无疑问,体积会非常庞大,首屏加载会有非常长的一段白屏时间,用户体验差。
- 分离第三方库代码
第三方库代码单独提取出来,可以放置在cdn进行非常长时间的缓存。这里介绍一下webpack4的抽离方法。
module: {...},
optimization: {
splitChunks: {
cacheGroups: {
venders: {
test: /node_modules/,
name: 'vendors',
chunks: 'all'
}
}
}
},
plugins: ...复制代码
- 动态导入
// 修改模块为动态导入形式
<Route path="/about" render={() => import('src/About').then(module => module.default)}/>复制代码
现在只是简单的使用了dynamic import
,很多边界情况没有考虑进去,比如:加载进度、加载失败、超时等处理。可以开发一个高阶组件,把这些异常处理都包含进去。社区有个很棒的react-loadable。
react-loadable还提供了preload功能,可以在首页加载完成的时候去加载预加载其他的js。
- 提取业务复用代码
module: {...},
optimization: {
splitChunks: {
cacheGroups: {
venders: {
test: /node_modules/,
name: 'vendors',
chunks: 'all'
},
default: {
minSize: 0,
minChunks: 2,
reuseExistingChunk: true,
name: 'utils'
}
}
}
},
plugins: ...复制代码
- 分离复用程度小的第三方库
...
venders: {
test: /node_modules\/(?!(lodash)\/)/, // 去除 lodash,剩余的第三方库打成一个包,命名为 vendors-common
name: 'vendors-common',
chunks: 'all'
},
lodash: {
test: /node_modules\/lodash\//, // lodash 库单独打包,并命名为 vender-lodash
name: 'vender-lodash'
},
default: {
minSize: 0,
minChunks: 2,
reuseExistingChunk: true,
name: 'utils'
}
...复制代码
- 代码级优化
(1)尽量减少dom操作
JS 引擎和渲染引擎(浏览器内核)是独立实现的。当我们用 JS 去操作 DOM 时,本质上是 JS 引擎和渲染引擎之间进行了“跨界交流”。
浏览器进行layout(布局)和paint(绘制)的代价是最大的。
(2)css避免过多的父级
css解析的过程:从右向左解析。
(3)对高频触发的事件进行节流或防抖,比如scroll
或者resize
。
(4)使用 Web Worker 来处理复杂的计算
服务端:
- 减少文件响应体积,比如gzip
- 优化网络路由,如增加cdn缓存
- 增加服务端缓存,如redis