为什么要做性能优化?性能优化有多重要?网站的性能对用户的留存率和转化率有很大的影响。说白了,提高网站的性能可以直接增加网站带来的收入。性能优化的分类前端的性能优化主要分为两类:loading-time优化;运行时优化;比如压缩文件,使用CDN加载静态资源,都属于loading-time优化;及时解除绑定事件,减少DOM操作,属于运行时优化。吉德林定律:遇到问题,只有先想清楚,才能很好地解决问题。所以在做性能优化之前,最好先看看网站的加载性能和运行性能。人工检查加载性能一个网站看加载性能如何,主要看首屏时间和白屏时间。白屏时间:指从输入网址到页面开始显示内容的时间;首屏时间:指输入网址后,页面完全加载完成的时间;你可以在之前添加以下代码来获取白屏页面Face白屏时间。在window.onload事件中执行newDate().getTime()-performance.timing.navigationStart获取第一次所用时间屏幕完全加载。查看运行性能这里我们使用chrome的开发者工具来查看运行性能。打开网站,按F12进入浏览器控制台,选择性能,点击左上角灰点,会变成红色开始录制。这时,你可以模仿用户使用网站。使用后点击停止,即可看到网站运行过程中的性能报告。如果有红色块,说明有掉帧;如果是绿色,说明帧率高,网页比较流畅。在性能选项下,按ESC会弹出一个小框,点击小框左边的三个点,勾选渲染。第一个是高亮重绘区域,另一个是显示帧渲染信息。勾选后去浏览网页,可以实时看到网页的渲染变化。使用工具查看chrome工具Lighthouse这个工具会对网站的性能进行评分。加载性能优化1.减少HTTP请求一个完整的http请求要经过DNS查找,TCP握手,浏览器发送http请求,服务器接收请求,服务器处理请求并发回响应,浏览器接收相应的过程。下图是一个http请求的例子:名词解释:Queueing:在请求队列中的时间;Stalled:TCP连接建立与数据真正可以传输的时间差。此事件包括代理协商时间。代理协商:与代理服务器协商连接所花费的时间。DNSLookup:执行一次DNS查找所需要的时间,对于页面上的每个不同域都需要这样的时间。InitialConnection/Connecting:建立连接所花费的时间,包括TCP握手/重试和协商SSL。SSL:完成SSL握手所花费的时间。请求已发送:发出网络请求所花费的时间。Waiting(TTFB):TTFB是从发送页面请求到接收到响应数据的第一个字节的时间。内容下载:接收响应数据所花费的时间。可以看出,真正的下载数据时间为13.05/204.16=6.39%。为了提高这个比例,需要将多个小文件合并为一个大文件,从而减少HTTP请求的数量。2.使用HTTP2快速解析。HTTP/2使用二进制格式来传输数据,而不是HTTP/1文本格式。二进制协议解析起来更有效。HTTP/1请求和响应消息由起始行、标题和实体文本组成,每个部分由文本换行符分隔。HTTP/2将请求和响应数据分成更小的帧,并以二进制编码。在HTTP/2中,同一个域名下的所有通信都在一个连接上完成,可以承载任意数量的双向数据流。在多路复用HTTP/1中,如果要并发发送多个请求,就必须使用多个TCP连接,并且为了控制资源,浏览器对单个域名也有6-8个TCP连接请求的限制。在HTTP/2中,不再依赖TCP连接来实现多流并行。在HTTP/2中:同一域名下的所有通信都在单个TCP连接上完成。单个连接可以承载任意数量的双向数据流。消息以一帧或多帧的形式发送,多个帧可以乱序发送,因为可以根据帧头中的流标识重新组装的特性大大提高了性能。优先级在HTTP/2中,每个请求可以携带一个31位的优先级值,0表示最高优先级,值越大优先级越低。服务器收到这样的请求后,可以优先处理。服务器推送添加到HTTP2的一项强大的新功能是服务器能够向客户端请求发送多个响应。服务器可以在发送页面HTML时主动推送其他资源,而不是等到浏览器解析到相应位置,发起请求再响应。比如服务端可以主动推送JS和CSS文件给客户端,而不是在客户端解析HTML的时候再发送这些请求。服务端可以主动推送,客户端也有选择是否接收的权利。如果服务器推送的资源已经被浏览器缓存,浏览器可以通过发送RST_STREAM帧来拒绝。主动推送也遵守同源策略,服务端不会随便向客户端推送第三方资源。3.使用服务器端渲染和客户端渲染:获取HTML文件,根据需要下载javascript文件,运行文件,生成DOM,然后渲染。服务端渲染:服务端返回一个HTML文件,客户端只需要解析HTML即可。优点:首屏渲染速度快,SEO友好;缺点:涉及构建设置和部署的更多要求,更多的服务器负载。4、静态资源使用CDN内容分发网络(CDN)是一组分布在多个不同地理位置的Web服务器。我们都知道服务器离用户越远,延迟就越高。CDN就是为了解决这个问题,通过在多个地点部署服务器,让用户离服务器更近,从而缩短请求时间。5.头部放css,底部放javascript文件。所有放在head标签中的CSS和JS文件都会阻塞渲染。如果这些CSS和JS加载和解析的时间很长,页面将永远是空白的。所以JS文件应该放在最下面,HTML解析完成后会加载JS文件。CSS放在最前面的原因是:如果CSS放在最后面,用户第一眼看到的是没有样式的页面,比较“难看”。为了避免这种情况,CSS被放在了头部。6、用字体图标iconfont代替图片图标字体图标就是把图标做成字体,使用的时候就跟字体一样,可以设置属性,比如font-size,color等,非常好用方便的。并且字体图标是矢量图形,没有失真。另一个优点是生成的文件非常小。压缩字体文件使用fontmin-webpack插件对字体文件进行压缩,可以进一步减小字体大小。7、利用好缓存,不要重复加载相同的资源。为了避免用户每次访问网站都必须请求文件,我们可以通过添加Expires或max-age来控制这种行为。expires设置了一个绝对时间,只要在这个时间之前,浏览器就不会去请求文件,而是直接使用缓存。而max-age是一个相对时间,建议使用max-age而不是Expires。max-age:设置缓存保存的时间,超过这个时间缓存就被认为过期了。在此之前,浏览器不会重新请求读取文件,而是直接使用缓存。no-cache:表示客户端可以缓存资源,每次使用缓存资源的有效性都要重新校验。8.压缩文件压缩文件可以减少文件下载时间,提高用户体验。在webpack中,可以使用以下插件进行压缩:javascript:UglifyPluginCSS:MiniCssExtractPluginHTML:HtmlwebpackPlugin也可以使用gzip压缩,通过在HTTP请求头中的Accept-Encoding中添加gzip标志来启用。当然,服务器也必须支持这个特性。webpack配置:下载插件npminstallcompression-webpack-plugin--save-dev配置:constCompressionPlugin=require('compression-webpack-plugin')module.exports={plugins:[newCompressionPlugin()]}9.图片优化页面懒加载图片,不先为图片设置路径。只有当图像出现在可见区域时,才加载真实图像。这是延迟加载。对于一个图片很多的网站来说,一次加载过多的图片会对用户体验造成很大的影响。可以参考这篇文章Web前端图片延迟加载降低图片质量例如JPG格式的图片,100%质量和90%质量通常无法区分,尤其是用作背景图片时。压缩方式有两种:一种是在线压缩网站;另一种是使用webpack插件image-webpack-plugin;npmi-Dimage-webpack-loaderwebpackconfiguration{test:/\.(png|jpe?g|gif|svg)(\?.*)?$/,使用:[{loader:'url-loader',options:{limit:10000,name:utils.assetsPath('img/[name].[hash:7].[ext]')}},{loader:'image-webpack-loader',options:{bypassOnDebug:true}}]}尽量使用CSS3效果代替图片,尽量使用CSS效果,比如渐变、阴影等,因为代码体积通常比图片小很多;10、按需加载代码,提取第三方库代码,减少冗余代码根据文件内容生成文件名,结合import动态引入组件实现按需加载通过webpack配置output的filename属性有一个[contenthash]在value选项中,它将根据文件的内容创建一个散列。当文件改变时,[contenthash]也会改变。output:{filename:'[name].[contenthash].js',chunkFilename:'[name].[contenthash].js',path:path.resolve(__dirname,'../dist'),},提取第三方库因为导入的第三方库一般都比较稳定,不会经常变动。所以将它们单独提取出来作为长期缓存是更好的选择。这里需要用到webpack4的splitChunk插件cacheGroups选项。module.exports={optimization:{splitChunks:{chunks:'async',minChunks:30000,cacheGroups:{vendors:{test:/[\\/]node_modules[\\/]/,priority:-10},default:{minChunks:2,priority:-20,reuseExistingChunk:true}}}}}缓存组可能是SplitChunks插件中最有趣的功能。在默认设置下,node_mudules文件夹中的模块会被打包成一个名为vendors的bundle,所有被引用超过两次的模块都会被分配到defaultbundle中。也可以通过priority设置优先级。reuseExistingChunk:表示是否使用已有的chunk,如果为true,则表示如果当前chunk中包含的模块已经被提取出来,则不会重新生成新的。参考文章使用split-chunks-plugin代码拆分具体配置查看官方文档split-chunks-plugin
