前端性能优化主要有如下几块
- 代码执行效率的优化,极致追求js执行效率意义不大,重点注意下内存泄漏等问题,此块不展开
- 界面渲染的优化,一般后端不会一次返回大量的数据给到前端渲染,都会有分批分页加载,此块不展开
- 网络请求优化,本文主要讨论这一块,主要有如下手段:
- 减少http请求
- icon等多个小图片加载用雪碧图只用请求一次,不过现在一般用webpack打包,这些小图片都通过url-loader工具打包成了base64在代码里,所以此处不再展开
- js css等静态资源合并打包,现在webpack打包都是这样做的,所以此处不再展开
- 合并后端接口,由于后端接口一般要遵循单一职责原则(一个接口做一件事),所以不能随便合并,所以此处不再展开
- 减少每个http请求的大小
- 减少图片的体积,此处主要是和UI沟通,此处不再展开
- js css等静态资源的压缩,例如用webpack插件
new webpack.optimize.UglifyJsPlugin({ minimize: true })
等 - 开启gizp压缩,linux服务器上打开nginx配置文件
/usr/local/nginx/conf/nginx.conf
,写入gzip on;
- js中用体积小的库替代同样功能的体积大的库,例如用只有几k的
dayjs
库替换有几百k的moment
库完成日期处理功能,可以用webpack插件new BundleAnalyzerPlugin({ analyzerPort: 3011 })
查看每个库占用的体积。 - webpack打包优化,如下展开讨论
- 静态资源的缓存,如下展开讨论
- 进入页面前加loading降低加载感知,此处不展开
- 需要的才加载比如路由懒加载,参考xxx
- 减少http请求
entry配置vendor包
// webpack.base.config.js
entry: {
// 很少变化的大体积库打包成vendor.js
vendor: ['react', 'react-dom', 'antd', 'babel-polyfill'],
...
},
plugins配置CommonsChunkPlugin
// webpack.base.config.js
plugins: [
...
// 抽取chunk vendor.js
new webpack.optimize.CommonsChunkPlugin({
names: ['vendor']
}),
// 每次打包 vendor.js中变化的部分抽取为manifest.js
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
chunks: ['vendor']
})
...
]
打包的filename必须用chunkhash
// webpack.base.prod.js
output: {
// 必须用chunkhash 否则vendor每次打包后hash都会变化就无法缓存了
filename: '[name].[chunkhash].js',
...
},
这样每次打包机会出现三个文件
- main.hashxxx.js: 主文件,一般几百K
- vendor.hashxxx.js: 一般1-2M,output filename用的chunkhash,所以这个文件的hashxxx永远不会变化,然后设置一个长的max-age缓存,之后就都是缓存加载了
- mainfest.hashxxx.js: 一般几K,每次打包vendor中变化的部分抽取到这里来
这样每次打包完只请求几百K的main.hashxxx.js和几K的mainfest.hashxxx.js,而几M的vendor.hashxxx.js都走的是缓存。
关于CommonsChunkPlugin需要用chunkhash具体参考https://www.jb51.net/article/131865.htm
详情参考https://www.jianshu.com/p/54cc04190252
主要有如下四类缓存,优先级从高到低。
- Service Worker:PWD的内容,还不普及暂不讨论
- Memory Cache:内存缓存,返回
200 OK (from memory cache)
,浏览器会自动设置并自动处理是否失效,一般不用设置 - Disk Cache:硬盘缓存,主要通过设置http头实现,分为强缓存和协商缓存两种,如下展开讨论
- Push Cache:HTTP2的内容,还不普及暂不讨论
不会向服务器发送请求,直接从缓存中读取资源,返回200(from memory cache)
或者200(from disk cache)
。
如果强缓存失效再进行下一步的协商缓存。
强缓存可以通过设置两种HTTP Header实现:HTTP/1的Expires,HTTP/1.1的Cache-Control,现在一般用Cache-Control。
Cache-Control部分字段如下:
max-age:max-age=xxx
表示缓存内容将在xxx秒后失效。no-cache
跳过强缓存,直接进入协商缓存。no-store
所有内容都不会被缓存,即不使用强制缓存,也不使用协商缓存。
Cache-Control的具体设置自行搜索nginx Cache-Control设置
强缓存失效后进入协商缓存,发起http请求,如果生效返回304(Not Modified)
(304只返回请求头不返回body,body从缓存读取)。
协商缓存可以通过设置两种HTTP Header实现:HTTP/1的Last-Modified,HTTP/1.1的ETag,现在一般用ETag。
Etag机制:服务器响应请求时,response header
返回当前资源Etag
(由服务器生成的一个唯一标识,如果资源有变化,Etag就会重新生成)。浏览器在下一次请求资源的时候会将此资源上一次返回的Etag
放到request header
里的If-None-Match
里。服务器比较客户端传来的If-None-Match
跟服务器上该资源的ETag
是否一致,从而判断该资源相对客户端而言是否被修改过了。如果服务器发现ETag
匹配不上,那么返回200
将新的资源(当然也包括了新的ETag)发给客户端。如果ETag
是一致的,则直接返回304(Not Modified)
。
Etag的具体设置自行搜索nginx Etag设置