虹口建设机械网站制作,罗湖网站建设哪家好,要想提高网站排名应该如何做,建设联结网同类网站5、渲染优化
如果把浏览器呈现页面的整个过程一分为二#xff0c;前面章节所讨论的诸如图像资源优化、加载优化#xff0c;以及构建中如何压缩资源大小等#xff0c;都可视为浏览器为呈现页面请求所需资源的部分#xff1b;本章将主要关注浏览器获取到资源后#xff0c;进…5、渲染优化
如果把浏览器呈现页面的整个过程一分为二前面章节所讨论的诸如图像资源优化、加载优化以及构建中如何压缩资源大小等都可视为浏览器为呈现页面请求所需资源的部分本章将主要关注浏览器获取到资源后进行渲染部分的相关优化内容。
其实优化渲染的实质就是尽量压缩每个阶段的执行时间或跳过某些阶段的执行。
目前大部分设备的屏幕分辨率都在60fps左右也就是每秒屏幕会刷新60次所以要满足用户的体验期望就需要浏览器在渲染页面动画或响应用户操作时每一帧的生成速率尽量接近屏幕的刷新率。若按照60fps来算则留给每一帧画面的时间不到17ms再除去浏览器对资源的一些整理工作一帧画面的渲染应尽量在10ms内完成如果达不到要求而导致帧率下降则屏幕上的内容会发生抖动或卡顿。
1、渲染过程
渲染过程大体可以划分为五个部分JavaScript处理、计算样式、页面布局、绘制与合成。
JavaScript处理前端项目中经常会需要响应用户操作通过JavaScript对数据集进行计算、操作DOM元素并展示动画等视觉效果。当然对于动画的实现除了JavaScript也可以考虑使用如CSS Animations、Transitions等技术。
计算样式在解析CSS文件后浏览器需要根据各种选择器去匹配所要应用CSS规则的元素节点然后计算出每个元素的最终样式。
页面布局指的是浏览器在计算完成样式后会对每个元素尺寸大小和屏幕位置进行计算。由于每个元素都可能会受到其他元素的影响并且位于DOM树形结构中的子节点元素总会受到父级元素修改的影响所以页面布局的计算会经常发生。
绘制在页面布局确定后接下来便可以绘制元素的可视内容包括颜色、边框、阴影及文本和图像。
合成通常由于页面中的不同部分可能被绘制在多个图层上所以在绘制完成后需要将多个图层按照正确的顺序在屏幕上合成以便最终正确地渲染出来。 渲染过程 2、JavaScript 执行优化
1、JavaScript 实现的动画的优化使用 requestAnimationFrame
前端实现动画效果的方法有很多比如在CSS中可以通过transition和animation来实现在HTML中可以通过canvas来实现。
而通过 JavaScript 实现的动画最容易想到的方式是利用定时器setTimeout或setInterval来实现。
但不要用 setTimeout或setInterval而是用 requestAnimationFrame。
requestAnimationFrame方法的执行时机会与系统的刷新频率同步。这样就能保证回调函数在屏幕的每次刷新间隔中只被执行一次从而避免因随机丢帧而造成的卡顿现象。 2、恰当使用 Web Worker
可将一些纯计算的工作迁移到Web Worker上处理它为JavaScript的执行提供了多线程环境主线程通过创建出Worker子线程可以分担一部分自己的任务执行压力。在Worker子线程上执行的任务不会干扰主线程待其上的任务执行完成后会把结果返回给主线程这样的好处是让主线程可以更专注地处理UI交互保证页面的使用体验流程。需要注意的是Worker子线程一旦创建成功就会始终执行不会被主线程上的事件所打断这就意味着Worker会比较耗费资源所以不应当过度使用一旦任务执行完毕就应及时关闭
●DOM限制Worker无法读取主线程所处理网页的DOM对象也就无法使用document、window和parent等对象只能访问navigator和location对象。
●文件读取限制Worker子线程无法访问本地文件系统这就要求所加载的脚本来自网络。
●通信限制主线程和Worker子线程不在同一个上下文内所以它们无法直接进行通信只能通过消息来完成。
●脚本执行限制虽然Worker可以通过XMLHTTPRequest对象发起ajax请求但不能使用alert()方法和confirm()方法在页面弹出提示。
●同源限制Worker子线程执行的代码文件需要与主线程的代码文件同源。
Web Worker的使用方法非常简单在主线程中通过new Worker()方法来创建一个Worker子线程构造函数的入参是子线程执行的脚本路径由于代码文件必须来自网络所以如果代码文件没能下载成功Worker就会失败 在子线程处理完相关任务后需要及时关闭Worker子线程以节省系统资源关闭的方式有两种在主线程中通过调用worker.terminate()方法来关闭在子线程中通过调用自身全局对象中的self.close()方法来关闭。
考虑到上述关于Web Worker使用中的限制并非所有任务都适合采用这种方式来提升性能。如果所要处理的任务必须要放在主线程上完成则应当考虑将一个大型任务拆分为多个微任务每个微任务处理的耗时最好在几毫秒之内能在每帧的requestAnimationFrame更新方法中处理完成代码示例如下 3、事件节流和事件防抖
所谓事件节流简单来说就是在某段时间内无论触发多少次回调在计时结束后都只响应第一次的触发。代码示例如下 事件防抖的实现方式与事件节流类似只是所响应的触发事件是最后一次事件。具体来说首先设定一个事件防抖的时间间隔当事件触发开始后启动计时器若在定时器结束计时之前又有相同的事件被触发则更新计时器但不响应回调函数的执行只有当计时器完整计时结束后才去响应执行最后一次事件触发的回调函数。 如果用户操作过于频繁每次在防抖定时器计时结束之前就进行了下一次操作那么同一事件所要触发的回调函数将会被无限延迟。频繁延迟会让用户操作迟迟得不到响应同样也会造成页面卡顿的使用体验这样的优化就属于弄巧成拙。
如果用户操作过于频繁每次在防抖定时器计时结束之前就进行了下一次操作那么同一事件所要触发的回调函数将会被无限延迟。频繁延迟会让用户操作迟迟得不到响应同样也会造成页面卡顿的使用体验这样的优化就属于弄巧成拙。 4、恰当的 JavaScript 优化
通过优化执行JavaScript能够带来的性能优化除上述几点之外通常是有限的。很少能优化出一个函数的执行时间比之前的版本快几百倍的情况除非是原有代码中存在明显的BUG。
若花费大量精力进行这类微优化可能只会带来零点几毫秒的性能提升当然如果基于游戏或大量计算的前端应用则另当别论。所以对于渲染层面的JavaScript优化我们首先应当定位出导致性能问题的瓶颈点然后有针对性地去优化具体的执行函数而避免投入产出比过低的微优化。
Chrome浏览器开发者工具中的Performance页签使用它可让我们逐帧评估JavaScript代码的运行开销。
在工具的顶部有控制JavaScript采样的分析器复选框Disable JavaScript samples由于这种分析方式会产生许多开销建议仅在发现有较长时间运行的JavaScript脚本时以及需要深入了解其运行特性时才去使用。除此之外在可开发者工具的Setting 〉 More tools中单独调出JavaScript分析器针对每个方法的运行时间及嵌套调用关系进行分析并可将分析结果导出为.cpuprofile文件保存分享。 JavaScript Profiler工具界面 该功能将帮助我们获得更多有关JavaScript调用执行的相关信息据此可进一步评估出JavaScript对应用性能的具体影响并找出哪些函数的运行时间过长。然后使用优化手段进行精准优化。比如尽量移除或拆分长时间运行的JavaScript脚本如果无法拆分或移除则尝试将其迁移到Web Worker中进行处理让浏览器的主线程继续执行其他任务。
3、计算样式优化
1、减少要计算样式的元素数量使用类选择器替代标签选择器 CSS选择器的匹配规则实际上是从右向左的这样再回看上面的规则匹配其实开销相当高因为CSS引擎需要首先遍历页面上的所有li标签元素然后确认每个li标签有包含类名为product-list的父元素才是目标元素所以为了提高页面的渲染性能计算样式阶段应当尽量减少参与样式计算的元素数量 使用类选择器替代标签选择器 对于上面li标签的错误示范如果想对类名为product-list下的li标签添加样式规则可直接为相应的li标签定义名为product-list_li的类选择器规则 避免使用通配符做选择器 使用通配符就意味着在计算样式时浏览器需要去遍历页面中的每一个元素这样的性能开销很大应当避免使用。
2、降低选择器的复杂性 比如使用名为final-container-content的类选择替代上述的复杂样式计算直接添加到目标元素上。而且复杂的匹配规则可能也会存在考虑不周从而导致画蛇添足的情况例如通过id选择器已经可以唯一确定目标元素了就无须再附加其他多余的选择器 3、使用 BEM 规范最好只有一个选择器
BEM是一种CSS的书写规范它的名称是由三个单词的首字母组成的分别是块(Block)、元素(Element)和修饰符(Modifier)。理论上它希望每行CSS代码只有一个选择器这就是为了降低选择器的复杂性对选择器的命名要求通过以下三个符号的组合来实现。
●中画线(-)仅作为连字符使用表示某个块或子元素的多个单词之间的连接符。
●单下画线(_)作为描述一个块或其子元素的一种状态。
●双下画线(__)作为连接块与块的子元素。
通常来说凡是独立的页面元素无论简单或是复杂都可以被视作一个块在HTML文档中会用一个唯一的类名来表示这个块。具体的命名规则包括三个只能使用类选择器而不使用ID选择器每个块应定义一个前缀用来表示命名空间每条样式规则必须属于一个块。比如一个自定义列表就可视作为一个块其类名匹配规则可写为 元素即指块中的子元素且子元素也被视作块的直接子元素其类名需要使用块的名称作为前缀。以上面自定义列表中的子元素类名写法为例与常规写法对比如下 修饰符可以看作是块或元素的某个特定状态以按钮为例它可能包含大、中、小三种默认尺寸及自定义尺寸对此可使用small、normal、big或size-N来修饰具体按钮的选择器类名示例如下 BEM样式编码规范建议所有元素都被单一的类选择器修饰。
4、页面布局与重绘的优化
页面布局也叫作重排和回流指的是浏览器对页面元素的几何属性进行计算并将最终结果绘制出来的过程。凡是元素的宽高尺寸、在页面中的位置及隐藏或显示等信息发生改变时都会触发页面的重新布局。
1、触发页面布局与重绘的操作 第一类 对DOM元素几何属性的修改这些属性包括width、height、padding、margin、left、top等某元素的这些属性发生变化时便会波及与它相关的所有节点元素进行几何属性的重新计算这会带来巨大的计算量 第二类 更改DOM树的结构浏览器进行页面布局时的计算顺序可类比树的前序遍历即从上向下、从左向右。 这里对DOM树节点的增、删、移动等操作只会影响当前节点后的所有节点元素而不会再次影响前面已经遍历过的元素。 第三类 获取某些特定的属性值操作比如页面可见区域宽高offsetWidth、offsetHeight页面视窗中元素与视窗边界的距离offsetTop、offsetLeft类似的属性值还有scrollTop、scrollLeft、scrollWidth、scrollHeight、clientTop、clientWidth、clientHeight及调用window.getComputedStyle方法。 这些属性和方法有一个共性就是需要通过即时计算得到所以浏览器就需要重新进行页面布局计算。
2、避免对样式的频繁改动
如果一定要修改样式则可通过以下几种方式来降低触发重排或回流的频次
1、使用类名对样式逐条修改 2、缓存对敏感属性值的计算 3、使用 requestAnimationFrame 方法控制渲染帧
在requestAnimationFrame方法的回调函数中应始终优先样式的读取requestAnimationFrame方法可以控制回调在两个渲染帧之间仅触发一次如果在其回调函数中一开始就取值到即时敏感属性其实获取的是上一帧旧布局的值并不会触发页面布局的重新计算。然后再执行相应的写操作 3、通过工具对绘制进行评估
1、监控渲染信息
打开Chrome的开发者工具可以在“设置”→“更多工具”中发现许多很实用的性能辅助小工具比如监控渲染的Rendering工具 Paint flashing当我们开启该功能后操作页面发生重新渲染Chrome会让重绘区域进行一次绿色闪动。 这样就可以通过观察闪动区域来判断是否存在多余的绘制开销比如若仅单击Select组件弹出下拉列表框却发现整个屏幕区域都发生了闪动或与此操作组件的无关区域发生了闪动这都意味着有多余的绘制开销存在需要进一步研究和优化。 店铺管理系统在切换一级菜单项时牵涉二级菜单的图层闪动情况 Layer borders功能开启后会在页面上显示出绘制的图层边界。 FPS meter功能开启后会在当前页面的左上角显示实时的帧率情况GPU功能是否开启及GPU内存占用情况 Rendering工具对帧率的监控
2、查看图层详情
当我们通过Rendering工具发现存在有多余的图层渲染时由于闪动是难于捕捉的所以还需要工具辅助显示出各个图层的详细信息这便需要用到Layers图层工具。 Layers图层工具 上图所示工具界面大体分为三部分
①号矩形框区域为当前页面的图层列表
②号矩形框区域为页面带有图层边框的视图
③号矩形框区域为选中图层的详细信息包括页面尺寸、内存占用、绘制次数等。
通过这些信息能够帮助我们快速定位到所要查看的图层信息。当我们使用Rendering工具监控页面交互过程中有不恰当的图层存在时便可使用Layers工具进行问题复现首先打开目标页面然后从左侧图层列表中依次查找出问题图层接着分析引起该图层发生重绘的原因。
4、降低绘制复杂度
绘制是在页面布局确定后将元素的可视内容绘制到屏幕上的过程。虽然不同的CSS绘制样式看不出性能上明显的不同但并非所有属性都有同样的性能开销。例如绘制带有阴影效果的元素内容就会比仅绘制单色边框所耗费的时间要长因为涉及模糊就意味着更高的复杂度。CSS属性如下 比如位图的阴影效果可以考虑使用Photoshop等图像处理工具直接为图片本身添加阴影效果而非全交给CSS样式去处理。
例如页面的顶部有一个固定区域的header标头若它与页面其他位置的某个区域位于同一图层当后者发生重绘时就有可能触发包括固定标头区域在内的整个页面的重绘。对于固定不变不期望发生重绘的区域建议可将其提升为独立的绘图层避免被其他区域的重绘连带着触发重绘。
5、合成处理
合成处理是将已绘制的不同图层放在一起最终在屏幕上渲染出来的过程。在这个环节中有两个因素可能会影响页面性能一个是所需合成的图层数量另一个是实现动画的相关属性。
可通过将固定区域和动画区域拆分到不同图层上进行绘制来达到绘制区域最小化的目的。接下来我们就来探讨如何创建新的图层最佳方式便是使用CSS属性will-change来创建 该方法在Chrome、Firefox及Opera上均有效而对于Safari等不支持will-change属性的浏览器则可以使用3D变换来强制创建 虽然创建新的图层能够在一定程度上减少绘制区域但也应当注意不能创建太多的图层因为每个图层都需要浏览器为其分配内存及管理开销。如果已经将一个元素提升到所创建的新图层上也最好使用Chrome开发者工具中的Layers对图层详情进行评估确定是否真的带来了性能提升切忌在未经分析评估前就盲目地进行图层创建。
仅合成相关的动画属性
如果一个动画的实现不经过页面布局和重绘环节仅在合成处理阶段就能完成则将会节省大量的性能开销。目前能够符合这一要求的动画属性只有两个透明度opacity和图层变换transform。它们所能实现的动画效果如表所示其中用n来表示数字。 在使用opacity和transform实现相应的动画效果时需要注意动画元素应当位于独立的绘图层上以避免影响其他绘制区域。这就需要将动画元素提升至一个新的绘图层。
6、服务器端渲染
对于数据相关的页面比如用户中心的例子需要获取到与用户相关的数据后再去进行编译和渲染对此可以考虑将这些步骤放在服务器端去执行。
能这么做的原因是首先数据获取本身就需要向服务器端发起请求这一步服务器端具有天然的优势其次服务器端的nodejs与浏览器同样都使用JavaScript语言这就使得服务器端能在获取到数据后就去执行Vue核心代码进行编译及渲染从而生成可在浏览器端直接渲染的HTML文件。当然这个HTML文件最终还需要在浏览器端与Vue框架进行混入让Vue框架来管理相应的数据。
这就是所谓的服务器端渲染简单说就是将原本在客户端执行的与首屏渲染相关JavaScript处理逻辑移到服务器端进行处理。
这样做虽然可以减少等待Vue框架加载与执行的时间但会增加服务器的算力压力同时也有可能面临服务器端内存泄漏的风险。可是考虑到服务器端集群的运算能力肯定会高于用户端单个手机或电脑等设备上浏览器的运算能力所以在有限的页面上采取服务器端渲染能够明显提升首屏页面的渲染速度同时在具体使用的页面范围上也应当参考运算能力平衡考虑。 Vue 服务器端渲染 由于Vue组件生命周期在服务器端和在客户端上不一致因此需要针对服务器端渲染编写相应的组件代码。
比如Vue组件在进行服务器端渲染的时候不存在真实DOM节点渲染的情况所以并不存在mounted这个生命周期函数那么原本在客户端编写的组件就需要将mounted中的业务逻辑迁移到组件的其他位置上。
接着往右看业务源代码从app.js处分出了两个构建入口webpack会根据不同的入口配置分别生成用于服务器端渲染所需的Server Bundle和客户端渲染所需的Client Bundle。其中Server Bundle会在所定义的包渲染器中被编译生成可以在浏览器端直接进行渲染的HTML文件。
这里还存在一个小问题由于这份服务器端渲染所得的HTML文件也是由Vue组件和相应的数据生成的其包含的数据到了客户端之后还是需要通过浏览器端的Vue框架进行管理的。
Vue 的 SSR 项目实例
一个demo的目录结构如下 build下存放与项目构建相关的配置文件
public中存放着项目中用到的一些静态资源文件
dist存放着工程构建打包的输出文件
src目录下为项目的主要源代码文件
可以看出这是一个基于Vue的典型前端项目。
其中包含了组建目录components、路由设置router、基于Vuex状态管理的store、页面视图views及相应的入口文件。接下来将对该Vue项目的服务器端渲染过程进行简要介绍。
1、服务器端渲染所返回的 HTML 文件
服务器端渲染的目的是为浏览器返回一个可供直接进行绘制的HTML文件从而减少首屏出现的时间在该项目中文件index.template.html即为最终所要生成的服务器端渲染结果的模板文件其内容如下 在head标签中包含了title、显示设置、样式文件及一些预加载和预获取的文件配置而在body标签中则通过注释的方式(vue-ssr-oulet)标定出了服务器端渲染DOM所要注入的节点位置。
2、输出 HTML 文件的编译过程
明确了模板文件index.template.html的作用后接下来我们分析该模板文件如何处理并最终生成给浏览器直接渲染的HTML文件这个过程必定是通过webpack构建完成的可在配置文件中搜索模板文件的文件名在webpack.client.config.js中查到如下配置信息 该配置插件HTMLPlugin的作用是编译入参中指定的模板文件并在dist目录下生成最终所需的index.html文件。要追溯编译构建过程可从启动项目的命令npm run dev开始查询服务器启动代码server.js代码如下 这里是服务器启动处理的一个中间环节
一方面开发环境下更具体的处理流程在/build/setup-dev-server.js文件中进行在其中会启动一个开发调试用的服务器
另一方面当文件修改发生后会调用createRenderer方法生成服务器返回给浏览器的HTML文件中的内容字符串。
在服务器启动环节中的主要操作分别根据webpack.client.config.js和webpack.server.config.js的配置文件构建打包出Client Bundle和Server Bundle其中处理Server Bundle的代码如下 3、服务器端渲染方法
createRenderer方法代码如下 通过createBundleRenderer方法可根据上一步构建生成的Server Bundle和模板配置选项共同生成一个BundleRenderer实例该实例包含两个成员方法renderToString和renderToStream它们分别可以将服务器渲染的内容以字符串和可读数据流的形式输出输出结果即为浏览器请求首屏页面后服务器端返回可供直接渲染的结果。
可以看出该项目并非对所有页面都进行了服务器端渲染它仅对首屏页面的顶部进行了服务器端渲染下半部分的资源列表采用的是客户端渲染因此能够根据实际的业务情况去平衡需要客户端渲染与服务器端渲染是十分必要的。
服务器端渲染大部分解决的应当是首屏性能问题。