Web性能优化
fghfgh
8年前
<h2>1 Web性能优化</h2> <p>Web网站的性能细线在几个方面:</p> <ul> <li> <p>网站首页加载速度</p> </li> <li> <p>动画的流畅度</p> </li> </ul> <p>通过分析浏览器的渲染原理、资源对渲染的影响,得出优化网站性能的办法。</p> <h2>2 查看性能的工具</h2> <p>Chrome的 Timeline 面板录制网页加载的过程,分析记录浏览器渲染过程中每个过程的耗时。</p> <h2>2.1 录制时注意事项</h2> <ol> <li> <p>禁用浏览器缓存: Network Tab 下的 disable cache</p> </li> <li> <p>关闭Chrome扩展或者启用隐身模式</p> </li> <li> <p>根据使用场景,模拟真实的网络加载情况: Network Tab 下的 throttling 下拉按钮</p> </li> </ol> <h2>2.2 Timeline 工具的各个组成</h2> <ul> <li> <p>在 Main Thread 中可以看到页面渲染的整个过程及耗时</p> </li> </ul> <p style="text-align: center;"><img src="https://simg.open-open.com/show/4ffcf6fcab15555284a269819bed25f5.png"></p> <h2>3 浏览器渲染原理</h2> <p style="text-align: center;"><img src="https://simg.open-open.com/show/ba1c2d16dddda675e0791672b150e77f.png"></p> <h2>3.1 DOM树构建</h2> <p>DOM树的构建过程</p> <ol> <li> <p>根据HTML文档的内容,根据标签进行分词 Token</p> </li> <li> <p>根据 Token 生产对应的节点 Node</p> </li> <li> <p>将节点根据嵌套关系组合为一棵对象节点树 DOM</p> </li> </ol> <p>浏览器解析文档对象模型 DOM 是 <strong>增量进行</strong> 的,无需等待整个HTML文档加载完毕,便可以开始解析 DOM</p> <p>CSSOM 解析会阻塞 HTML Parser ;JavaScript脚本文件 <strong>执行</strong> 会阻塞HTML解析; CSS、JavaScript、Images和Font等静态资源的异步加载的,渲染页面与CSS解析与JavaScript执行会有相互的依赖</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/c89d6a33bede16aa10c75ee5be6a6b49.png"> <img src="https://simg.open-open.com/show/fe7dcc4757e4abf539b21d723553bda6.png"></p> <h2>3.2 CSSOM树的构建</h2> <p>CSSOM 的解析依赖于 <strong>选择器</strong> ,选择器的匹配是从内到外的。所以选择器嵌套层次越深,匹配的时间会越长。</p> <p>CSSOM 只解析可视部分 body 标签中的内容,将所有匹配的元素共同构建一个 CSSOM 树, <strong>从根节点一次向下,所有节点的属性向下继承</strong></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/3c1521f3c054528faff09b656c946590.png"></p> <h2>3.3 RenderTree树的构建</h2> <p>利用DOM和CSSOM组合构建生成RenderTree,对应 Recaculate Style</p> <p>RenderTree中包含所有渲染网页必须的节点</p> <p>无需渲染的节点不会被添加到RenderTree中,如 head 和 display:none; 的节点</p> <p>visibility: hidden; 的节点会添加到RenderTree中</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/cab1558b26bc9a486cae2688e17926a8.png"></p> <h2>3.4 Layout</h2> <p>Layout 利用渲染树的信息,计算渲染树中所有节点在页面上的 <strong>位置和大小</strong> 。</p> <p>类似绘画中各个元素位置摆放及尺寸规划</p> <p>会引起页面重新Layout的操作: <strong>所有改变节点位置和大小的操作</strong></p> <ul> <li> <p>屏幕旋转</p> </li> <li> <p>浏览器视窗改变</p> </li> <li> <p>与大小、位置相关的CSS属性</p> </li> <li> <p>增加与删除DOM元素</p> </li> </ul> <p>Layout操作比较耗时,对于动画中频繁引起Layout的操作(元素位置移动), 最好使用transform代替,可以使用GPU进行动画处理(将Layout重绘在GPU完成)</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/c42634f99810d58ac33ea5322f126aab.png"></p> <h3>viewport</h3> <p>如果页面 body 元素设置的宽度为 100% ,并且根元素 html 没有明确设置宽度绝对值, <strong> 此时 body 元素的宽度等于 viewport 的宽度 vw </strong></p> <ul> <li> <p>使用 meta 标签可以设置浏览器 viewport 的尺寸。 <meta name="viewport" content="width=device-width"></p> </li> <li> <p>device-width 为浏览器的理想视口(屏幕的物理分辨率)</p> </li> <li> <p>在移动端,如果不设置 device-width ,默认 viewport 宽度为980px, <strong>导致文字很小,需要放大</strong></p> </li> </ul> <p>viewport 相当于可视内容布局的容器</p> <h2>3.5 Paint</h2> <p>填充Layout中的具体内容和样式,将Layout生成的区域填充为最终显示在屏幕上的像素</p> <h2>3.6 总结</h2> <ol> <li> <p>浏览器通过 GET 请求获取网页HTML,同时将增量解析HTML文档,生成 DOM 树</p> </li> <li> <p>解析 DOM 节点树时,对于需要加载的资源 全部执行异步加载,但是 CSS 的解析、 JavaScript 的执行与 font 文件的下载会阻塞HTML Parser</p> </li> <li> <p>局部 DOM 树与 CSSOM 树构建完成后, <strong> 立即组装 RenderTree 进行渲染 </strong></p> </li> </ol> <p style="text-align: center;"><img src="https://simg.open-open.com/show/8beacde6ad49bf90f65e79b77c930e87.png"></p> <h2>4 资源对渲染的影响</h2> <p>页面中加载的资源主要包括: css 、 js 脚本文件和 font 字体与 images 静态资源,不同资源类型对渲染的影响不同。</p> <h2>4.1 浏览器渲染页面的时机</h2> <p>增量解析解析 DOM 树,并且完成相应 CSSOM 解析后(RenderTree依赖于 DOM 树, CSSOM 树),开始直接渲染页面。</p> <h2>4.2 CSS加载会阻塞初次渲染</h2> <p style="text-align: center;"><img src="https://simg.open-open.com/show/122cc847964be2cbfc48b33e83d8c64a.png"></p> <h2>4.3 非关键资源</h2> <p>对于首页无关的样式,需要使用适当的方式避免其阻塞初次渲染:</p> <ul> <li> <p>document.write() 会阻塞页面初次渲染</p> </li> <li> <p>使用 media=print 媒体查询,虽然加载样式表,但只针对打印时才应用该样式,不会阻塞初次渲染。</p> </li> <li> <p>通过 DOM API引入CSS,可以避免阻塞。</p> </li> <li> <p>CSS中 <link rel="preload" href="index_print.css" as="style" onload="this.rel='stylesheet'"> 。</p> </li> </ul> <p style="text-align: center;"><img src="https://simg.open-open.com/show/bc072069af15b2216c22fd05b3d0d77d.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/9040ff7e03719a355788abc8dfb493a2.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/8f60b6f2675dd764d086cc85bf6fc33f.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/3b3fcdffd7956d5f897735f8b844a327.png"></p> <h2>4.4 JS文件</h2> <p style="text-align: center;"><img src="https://simg.open-open.com/show/c57fb8c13ff6afb8836582e3e648d227.png"></p> <ul> <li> <p>输出:先输出 Hello ,10s之后再输出 World 。JS脚本 <strong>执行</strong> 会阻塞 HTML Parser ,但是 HTML Parser 是增量解析的, <strong>并且CSS样式的解析会阻塞JS脚本执行</strong> ,当解析完 Hello 时,生成对应 DOM 节点,并且完成其 CSSOM ,直接开始渲染 Hello 节点。</p> </li> <li> <p>脚本执行完成后再解析后续的 World</p> </li> </ul> <p>JS脚本执行会阻塞HTML Parser;</p> <p>CSS解析会阻塞JS脚本执行:js可能会读、写CSSOM</p> <p>虽然JS会阻塞HTML Parser解析; 但是浏览器的资源异步加载机制 Preload 会异步加载 head 标签内的资源</p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/ab5a44f6630a75f557cb9c4734d6c63f.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/7efeb0f4fb9299e3c735b6ca5a8f7f55.png"></p> <p style="text-align: center;"><img src="https://simg.open-open.com/show/018cf68a6f045cd6e90173d0ee692782.png"></p> <h2>4.5 非关键JS资源解析阻塞的优化方案</h2> <ul> <li> <p>将JS资源文件放在文档底部,延迟JS的执行(但是存在必须解析完HTML才能加载JS资源,相较于 head 标签中加载会慢)</p> </li> <li> <p>使用 defer 延迟脚本执行: scipt 标签的 defer 属性,脚本会在HTML文档解析完毕后再开始执行; 被 defer 的脚本在执行时严格按照HTML文档中出现的顺序执行 ---优势可以提早加载JS资源,但是解析完HTML再执行</p> </li> <li> <p>使用 async 异步执行脚本:</p> <ul> <li> <p>当 script 标签有 async 属性时,脚本执行不会阻塞HTML Parser,只要脚本加载完毕便开始执行</p> </li> <li> <p>被 async 的脚本,不会严格按照在HTML文档中的顺序执行</p> </li> <li> <p>async 适用于无依赖的外部独立资源(注意不要错误操作状态)</p> </li> </ul> </li> </ul> <p style="text-align: center;"><img src="https://simg.open-open.com/show/4ade22740a5fa81ee250bb69f78f21c9.png"> <img src="https://simg.open-open.com/show/a89dd8012515b70e79f0fda08106c672.png"></p> <h2>4.6 font 字体文件</h2> <ul> <li style="text-align: center;"> <p>font 字体文件会阻塞内容渲染</p> <img src="https://simg.open-open.com/show/c9ef8900100829d260484d6b1543fff8.png"></li> </ul> <h2>4.7 图片资源</h2> <p>图片资源的加载不会阻塞渲染,但是最好在HTML标签中设置图片的高度和宽度,可以在 Layout 时留出图片渲染的空间,避免页面的抖动</p> <h2>5 优化关键渲染路径</h2> <p>优化目标是将下列三个指标压缩到最低:</p> <ul> <li> <p>关键资源数---初次渲染时依赖的资源</p> </li> <li> <p>关键资源的体积最小---压缩文件或图片</p> </li> <li> <p>关键资源网络来回数---网络传输资源消耗很多时间</p> </li> </ul> <p style="text-align: center;"><img src="https://simg.open-open.com/show/5901c13c3e47bf71055e98aa7798da1b.png"> <img src="https://simg.open-open.com/show/544ef2cea0bb33ae930b87f9d6326d02.png"> <img src="https://simg.open-open.com/show/701d04bc9e821673da67c56f43688b78.png"> <img src="https://simg.open-open.com/show/a04de5d53a02d974133e370ca4d8106c.png"></p> <h2>6 其余优化过程</h2> <ul> <li> <p>HTTP2可以在传输HTML页面后向客户端推送页面内包含的资源</p> </li> <li> <p>减少资源的大小:压缩</p> </li> <li> <p>减少请求的来回时间</p> </li> </ul> <p style="text-align: center;"><img src="https://simg.open-open.com/show/db40561ecc623805bb05f6426562e8e3.png"> <img src="https://simg.open-open.com/show/7963c7fba81c22dd404b3e72092c6349.png"></p> <p> </p> <p>来自:https://segmentfault.com/a/1190000008693178</p> <p> </p>