当前位置: 首页 > Web前端 > HTML

如何做前端性能优化(下)——直奔黄龙

时间:2023-03-28 17:10:04 HTML

前言上一篇文章,如何做前端性能优化(上)——开门见山,一些相关的前置知识介绍了前端性能优化,所以这篇文章总结了优化方案。核心方向还是上一篇文章提到的内容:保证资源加载更快:渲染越快,视图显示越快,保证视图的渲染越快/交互速度:用户与页。前提是页面要渲染出来,其次是页面需要尽快反馈。服务器层面的优化,所以看起来前端性能优化的方向还是很明确的,但是具体的方向还是会涉及到不同方面的具体优化方法。还是要回顾一下从输入URL到页面加载完成的核心流程:进行DNS解析,建立TCP连接,客户端发送HTTP请求,服务端响应HTTP资源浏览器获取响应内容,解析并渲染它。从以上内容不难发现,每一步都需要一定的时间,所以可以围绕这些内容来考虑优化的方向。长文提醒??????长文提醒??????长文提醒??????长文提醒??????如何保证更快的资源加载?以下内容主要针对DNS解析、TCP连接、HTTP请求/响应等阶段进行优化。核心优化的核心其实是网络层面。使用dns-prefetch减少DNS查询时间dns-prefetch可以提前解析可能用到的不同域的域名,让解析结果缓存在系统缓存中,缩短DNS解析时间提高网站访问速度.比如在掘金中体现如下:【扩展】DNS解析的核心流程浏览器访问域名时,需要解析一次DNS,获取对应域名的IP地址:浏览器会从自身缓存是否有对应的域名如果有IP地址则返回,如果不存在则进入下一步查询操作系统中的/etc/hosts文件是否有IP域名对应的地址,如果存在则返回,如果不存在则进入下一步请求客户端本地域名服务器(LDNS)进行解析。本地域名服务器收到客户端的请求后,首先检查自己的缓存信息中是否有对应域名的IP地址。如果有返回结果,下一步就是向本地域名服务器请求根域名。服务器解析域名,根域名告诉本地域名服务器寻找对应的一级域名服务器,本地域名服务器请求一级域名服务器解析域名,第一个-级域名服务器告诉它去寻找对应的二级域名服务器。一级域名服务器解析域名,二级域名服务器告诉它去寻找对应的子域名服务器。本地域名服务器请求子域名服务器解析域名。子域名服务器返回对应的IP地址。本地域名服务器将IP地址记录在缓存中。并返回给客户端(会被缓存),客户端根据收到的IP地址访问网站,使用preconnect提前建立连接。连接有效,但这种连接通常只持续10秒。比如当前域请求一个资源之前,可能会涉及到DNS寻址、TLS握手、TCP握手、重定向等,这个过程也会耗费一定的时间。比如在掘金中体现为:使用preload/prefetch来预加载资源preloadpreload的作用是预加载页面对应的关键资源,以加快页面的渲染速度。preload的优先顺序和as属性有关,具体可以看。[注意]必须设置as属性。除了上面提到的设置优先级之外,还涉及到浏览识别的问题:如果不设置as属性,后面的请求会被当作XHR请求,这意味着资源的预加载功能可能会失败,因为一个新的每次都可能发起请求。比如在掘金中体现如下:比如vue-cli中默认的webpack配置是这样的:prefetchpreload是资源的预加载。虽然它会提前加载但是只有在需要执行的时候才加载,即资源必须是当前页面需要的资源。如果需要为下一个页面提前加载资源,那么就应该使用prefetch,即在浏览器空闲时下载资源。比如vue-cli中默认的webpack配置是这样的:压缩资源体积资源需要通过http数据包在网络中传输,所以只要能够减少传输的数据包体积,资源就可以到达客户端更快,这也是压缩资源量的核心目的。HTTP压缩HTTP压缩的典型代表是gzip,它是一种优秀的压缩算法,可以压缩http请求中的一些文件资源。一般来说,需要在服务器端进行处理,可以在响应头中传递。设置Content-encoding:gzip表示当前资源使用的压缩方式(如:gzip、deflate、br等),以便客户端使用正确的方式解压。[注意]gzip不是万能的。它不能保证每个文件的压缩都能使它的大小变小。关于Content-Encoding的内容可以点这里,或者参考content-encoding除了gzip,你还可以知道什么?比如在京东:比如在掘金:用HTTP压缩的Webpack压缩还不够?为什么你甚至需要Webpack压缩?首先,必须明确压缩过程本身是会消耗时间的。如果所有的资源在访问后都被服务端压缩,那么在压缩完成之前,客户端仍然处于等待状态,即仍然不能保证资源可用。最快的速度到达客户。那么优化方案就是把压缩资源的时间放到打包构建上。毕竟只有上线生产环境需要发布的时候,才需要进行一系列的打包和优化操作,而这比http的请求/响应速度要稍微长一些。产品包装时间没有大问题。下面会列出一些Webpack插件,但是具体的使用方法就不多说了,因为这些只是实现目的的不同方案。如果每个解决方案都详细解释,它可以是一篇独家文章。这里没有必要。具体用法大家可以自行查看。使用CompressionPlugin压缩文件。webpack文档提供了一个插件合集,其中包含了这个插件。它的功能是:准备资产的压缩版本以使用内容编码为它们提供服务。使用HtmlWebpackPlugin压缩HTML文件。通常我们需要HtmlWebpackPlugin插件来生成相应的HTML或者ExistingHTML模板自动注入到webpackbundles资源中。此外,它还可以配置minify选项来达到压缩模板的目的。可以在vue项目下执行vueinspect--modeproduction>webpack.config.js查看脚手架默认的webpack配置内容,例如:使用SplitChunksPlugin自定义分包策略。Webpack默认会打包尽可能多的模块代码。这个默认规则优缺点很明显:优点:可以减少最终页面的HTTP请求次数缺点:页面初始代码包过大,影响首屏渲染性能,不能有效应用浏览器缓存SplitChunksPlugin是Webpack4之后内置的最新分包方案,相比Webpack3中的CommonsChunkPlugin,它可以根据一些更灵活合理的启发式规则将Modules安排到不同的Chunks中,最终构建出性能更佳,更缓存友好的应用程序产品。例如vue-cli中默认的webpack配置如下:使用MiniCssExtractPlugin提取压缩CSSMiniCssExtractPlugin会将CSS提取到一个单独的文件中,为每个包含CSS的JS文件创建一个CSS文件,支持CSS和SourceMaps按需加载.例如vue-cli中默认的webpack配置如下:使用ImageMinimizerWebpackPlugin压缩图片资源。图片仍然是Web应用程序中不可或缺的资源,图片资源的大小也是加载首屏页面的瓶颈之一。因此,压缩图片也是性能优化需要考虑的事情。ImageMinimizerWebpackPlugin可用于优化/压缩所有图像,它可以支持无损(不损失质量)和有损(质量降低)压缩方式。通过TreeShaking去除无用代码TreeShaking依赖于ES6模块语法的“静态结构”特性(如:import和export)。当webpack的模式为“生产”时,可以启用更多优化项,包括压缩代码和TreeShaking。但同时要保证:尽量使用ES6模块语法,即import和export保证没有任何编译器(如:babel)会把对应的ES6模块语法转换为CommonJS语法(如:default@babel/preset-env的行为)可以在项目的package.json文件中添加“sideEffects”属性来识别当前内容是否有副作用。操作可以通过/*#__PURE__*/注解将函数调用标记为无副作用[](https://webpack.docschina.org...)以减少http请求次数不同协议下的请求次数可能仍然是请求/响应缓慢的原因:合并公共资源,例如Sprite和其他内置模块资源,例如生成base64图像,通过符号合并svg引用等代码块,例如使用公共组件构建工具分包策略封装、组件复用逻辑提取等按需资源加载,如路由懒加载、图片懒加载、上拉加载、页面加载等减少不必要的cookie不必要的cookie来回传输会造成带宽浪费:减少存储在cookie中的内容。对于静态资源,使用CDN托管(即非同域)。不同域名默认不携带cookies。CDN托管静态资源+HTTP缓存CDN加速的本质是缓存加速。资源缓存在CDN节点上。以后访问这些静态内容时,不需要访问服务器源站,可以选择访问就近的CDN节点,从而达到加速的效果,减轻服务器源站的压力。在掘金中体现如下:协议升级为Http2.0http1.x问题:HTTP的底层协议是TCP,而TCP是面向连接的,需要三次握手建立连接,其中:http1。0使用短连接,即一个请求/响应结束后连接将断开。这个过程很耗时。HTTP1.1使用长连接,可以通过在request/responseheader中设置Connection:keep-alive来开启。长连接的好处是允许多个请求共享一个TCP连接。缺点是带来队头阻塞:每个TCP连接中的多个请求需要排队。只有队头的请求得到响应,才能处理下一个请求。缓解方案之一是如果当前TCP连接发生队头阻塞,将一些请求放到其他TCP连接中。浏览器一般会限制同一个域名建立6-8个TCP连接,这也是为什么应用需要划分子域名和静态资源。托管CDN的原因之一是http1.x中header部分的内容可能非常大,每次请求可能需要携带大量重复的header文本内容,这些也是原因之一缓慢的请求/响应。以上问题http2。0可以解决:对于TCP连接数限制的问题,http2.0采用了多路复用,一个域名只对应一个TCP连接,对于http队列头阻塞的问题,在http2.0中,二进制分帧layer用于为每个request/response添加streamid,保证请求和响应一一对应,即不用等待上一个请求处理完毕,也可以添加优先级对每个请求。针对头部数据量大的问题,http2.0中传输的头部帧在处理后会以二进制格式表示,替换掉原来的文本格式,并使用HPACK算法进行压缩。接收端和发送端都会维护一个索引表,通过下标来标识headers,对应的索引可以用于后续重复的header信息。http2.0不再针对传统的request->response模式,而是提供了推送服务端的能力,让服务端主动推送关键资源给客户端,加速资源加载,比如在掘金:Howtoensurefasterviews渲染和交互?在保证资源快速到达客户端之后,就要优化浏览器的解析和渲染,当然还有后续页面交互的优化,其实就是浏览器层面的优化。浏览器渲染HTML文件的核心流程:HTML解释器:通过对HTML文档的词法分析输出DOMTreeCSS解释器:解析CSS文档,生成样式规则CSSOM样式计算:结合DOMTree和CSSOM生成RenderTree布局计算:计算Render的Tree节点在页面中的坐标位置,创建一个LayoutTree来划分图层:页面中有很多复杂的效果,比如一些复杂的3D变换,页面滚动,或者使用z-index进行z轴排序等.,为了更方便的实现这些效果,渲染引擎还需要为特定的节点生成一个专用的图层,并生成对应的图层树LayerTree图层绘制:染色引擎在实现图层绘制时,会将一个层的绘制分为多个绘制指令,这些指令形成一个列表,按顺序绘制。当图层的绘制列表准备好后,主线程会将要绘制的列表提交(commit)给合成线程。栅格化:由于视口有限,用户只能看到页面的一小部分,没有必要绘制所有图层内容,所以合成线程会将图层(layer)划分为瓦片(tile)渲染进程向GPU发送生成瓦片的指令,执行瓦片的生成位图合成和显示:所有瓦片光栅化后,合成线程生成绘制瓦片的命令——DrawQuad,然后将命令提交给浏览器进程.浏览器进程中有一个viz组件,会根据DrawQuad命令将其页面内容绘制到内存中,最后在渲染层将内存显示在屏幕上,减少阻塞渲染的因素。在真正渲染视图之前,必须生成DOMTree和CSSOM,所以必须保证HTML解释器和CSSOM尽早处理CSS解释器,而JavaScript的加载和执行可能会阻塞这个过程:渲染的节点数HTML文档中的第一次尽量小,避免深层嵌套结构,避免大量使用慢速标签(如:iframe)将CSS资源放在文档的头部,以降低CSS复杂度。比如合理使用CSS选择器将JavaScript资源放在文档底部,合理使用延迟和异步加载等方式。图片资源、大量列表数据的展示等:图片资源:优先加载可视区域内的图片,可视区域外的图片延迟加载,或者移入可视区域时加载列表数据:列表数据通常数据由于内容量大,不可能一次渲染所有数据。一般白屏是通过页面加载、上拉加载等方式批量渲染的,白屏优化是因为SPA应用需要等待JavaScript加载执行完毕,才会生成特定的页面结构。内容,即初始化模板中没有需要渲染的有意义的HTML结构:添加白屏加载,可以在模板中添加一个默认的加载效果,等到真正的页面内容渲染出来再替换加载内容和添加骨架屏,和上面的方案是一致的。在显示真实页面内容之前,先显示默认视图内容,避免服务端渲染白屏。现代框架默认属于客户端应用框架,即组件的代码会在浏览器中运行,然后输出DOM元素到页面,也称为客户端渲染(CSR):优点是用户体验更好,基于前端路由的方式实际上不进行页面跳转,即不刷新页面,不加载,带来更高的流畅度,占用服务器端资源更少。CSR呈现由客户端处理。服务器端不需要关心渲染计算过程,减轻了服务器端的压力。“白屏”的缺点是时间更长。主要原因是CSR渲染需要*.js的支持,而*.js必须保证接收并解析*.html,而*.html对当前网络环境依赖性强,所以会造成白屏在较差的网络环境下时间过长,尤其是在移动网络环境下,对SEO支持不友好,因为白屏时间长导致重要的内容没有被搜索引擎分析、分类、标记为一个一段时间内,搜索引擎不会等待页面渲染完成,所以对SEO优化不友好服务端渲染(server-siderendering,SSR)可以将服务中的同一个组件渲染成对应的HTML字符串,并发送给浏览器进行渲染,即客户端不需要等待所有JavaScript下载并执行后再显示,因此用户可以更快地看到完整渲染的内容预渲染(prerender)虽然上面说的服务端渲染(SSR)可以解决客户端的一些问题,但是它也带来了另外一些问题:需要保证开发的一致性,比如服务端和客户端可以执行的组件生命周期钩子是不同的。一些外部库可能需要在服务器端渲染应用程序中进行特殊处理,需要更多的构建设置和部署要求。完全静态的SPA可以部署在任何静态文件服务器上,但是服务器端渲染应用程序需要一个能够运行Node.js服务器的环境,并且服务器端负载更大。在Node.js中渲染一个完整的应用程序会产生比仅服务于静态文件更多的CPU密集型操作,并且您需要考虑过多的流量因此,并非所有应用程序都适合服务器端渲染。如果只是想通过SSR提升一些推广页面(如/、/about、/contact等)的SEO,应该优先考虑预渲染的方式:预渲染在打包和打包的过程中building(离屏状态),对应路由预生成对应页面内容的预渲染。需要配合webpack等打包构建工具(webpack、rollup等),通过prerender-spa-plugin来支持预渲染交互级别,减少reflow/redrawredraw:whenthechangeofthe元素在页面中的样式不影响其在文档流中的位置(如:color、background-color、visibility等),浏览器会使用新的样式给元素重绘reflow:当尺寸、结构、和RenderTree中部分或全部元素的某些属性发生变化,浏览器重新渲染部分或全部文档,以减少对DOM的频繁操作,并将频繁变化的元素保持在文档流之外,如果有持久的动画效果,会一直触发回流和重绘,避免访问或减少访问,导致浏览器强制刷新队列的属性,如:offsetTop,offsetLeft,offsetWidth等。[扩展]浏览器的渲染队列机制会过去s队列会触发回流或重绘操作进行存储,一定时间或一定量后再进行这些操作,避免单次修改css。比如在JavaScript中修改多个样式时,尽量使用css选择器来实现样式。集中更改使用will-change来启用GPU加速。will-change指定的属性可以让浏览器在元素属性真正发生变化之前提前做出相应的改变。优化预先设置图片大小避免图片资源加载后回流防抖/节流防抖:多次频繁触发执行操作,以最后一个为准,忽略中间过程节流:在指定时间间隔内,只允许一个对应的操作。使用防抖/节流来优化应用程序中的操作。比如节流可以用来优化滚动事件、模糊搜索等,防抖可以用来优化一些按钮点击操作等。WebWorkerJavaScript是单线程的,如果有需要大量计算的场景(比如视频解码),UI线程会被阻塞,甚至浏览器会直接卡死。WebWorker可以让脚本在新的线程中运行,它们独立于主线程,可以在不影响主线程UI渲染的情况下进行大量的计算活动,但是WebWorker不能被滥用。虚拟列表最常用的方式是页面加载:基于表格渲染,只会渲染固定数量的DOM。基于上拉加载列表渲染,随着加载数据的增加,相应的DOM节点也会增加,达到一定程度虚拟列表的核心是固定渲染的DOM数量,通过动态切换来更新视图数据内容,并保证文档中真实DOM的数量不会随着数据量的增加而增加(其实很像表格分页之类的,只是支持滚动)。如果你想了解它的核心实现,你可以查看虚拟滚动是如何优化性能的。大部分大文件分片上传的项目都必须一直有文件上传功能,但是大文件上传优化还是很有必要的。所谓断点续传,无论是转传还是二次转传,都是以分片上传为核心功能。如果想了解它的核心实现,可以去看看。请问:如何实现大文件的快速上传?Excel导入/导出对于Excel导入/导出功能,相信很多人的第一印象是后台工作,但在大多数情况下,后台界面的处理速度会受到各种因素的影响,导致在不令人满意的速度。需要前端优化。例如导入时,前端不发送文件,只发送解析后的JSON数据。导出时不需要单独发送额外接口,直接使用当前显示数据实现导出等。如果想了解它的核心实现,可以查看我实现一个前端Excel导入导出功能的Vue项目的优化。这部分相信大家都不陌生。下面是一些简单的内容(包括但不限于):减少响应式数据对于纯展示和需要在模板模板中使用的数据,可以使用Object.freeze()进行冻结,避免被转换成不需要的响应式数据。Vue组件初始化是有性能损失的。使用函数式组件,减少组件初始化的过程,适合实现只显示内容,没有业务逻辑的简单组件合理使用v-show和v-if,给v-for组件设置唯一键(不是索引),v-for和v-if不要一起使用使用KeepAlive复用组件,避免重复创建和销毁组件带来的性能损失使用()=>import(xxx)实现路由的懒加载使用ESM封装自定义工具库和其他第三方库引入按需合理使用闭包,避免内存泄漏,及时清除组件中的副作用,比如setTimeout、setInterval、addEventListener等都是基于vue.config.js或webpack进行优化。有关详细信息,请参阅如何优化您的vue-cli项目?综上所述,上述优化方案对应上一篇文章中提到的性能指标,如下:CumulativeLayoutShift(CLS)FirstInputDelay(FirstInputDelay,FID)关键资源越早到达客户端,TTFB时间越短,这也可以间接减少FP和FCP的时间;压缩资源意味着尽可能提高LCP的时间;减少页面的回流/重绘,可以让CLS的值更小,视图更稳定;FID是一个指标,用于跟踪浏览器对用户输入(包括点击和点击)作出反应之前的延迟时间,以确保资源的快速加载和页面的早期渲染。对应值越小,视图响应越快。最后,前端性能优化的范围太大。上面列举的优化主要围绕资源加载、页面渲染/交互两大方向展开,具体的优化方案其实有很多(包括但不限于上述内容)。注意的方向不同。如果以上内容对您有帮助,就来一键三连,需要更多的光!!!