Skip to content

五、性能解决

1、网络优化策略

  • 减少 HTTP 请求数,合并JSCSS,合理内嵌CSSJS
  • 合理设置服务端缓存,提高服务器处理速度。 (强制缓存、对比缓存)
    html
    Expires/Cache-Control

Etag/if-none-match/last-modified/if-modified-since


- 避免重定向,重定向会降低响应速度 (301,302)
- 使用`dns-prefetch`,进行`DNS`预解析
- 采用域名分片技术,将资源放到不同的域名下。解决同一个域名最多处理6个TCP链接问题。
- 采用`CDN`加速加快访问速度。(指派最近、高度可用)
- `gzip`压缩优化 对传输资源进行体积压缩 (`html`,`js`,`css`)

```html
Content-Encoding: gzip
  • 加载数据优先级 :

    1. preload(预先请求当前页面需要的资源)
    2. prefetch(将来页面中使用的资源) 将数据缓存到 HTTP 缓存中
    html
    <link rel="preload" href="style.css" as="style" />

HTTP 缓存问题

2、关键渲染路径

browser

  • 重排(回流)Reflow: 添加元素、删除元素、修改大小、移动元素位置、获取位置相关信息
  • 重绘 Repaint:页面中元素样式的改变并不影响它在文档流中的位置。

我们应当尽可能减少重绘和回流

(1)强制同步布局问题

JavaScript 强制将计算样式和布局操作提前到当前的任务中

html
<div id="app"></div>
<script>
  function reflow() {
    let el = document.getElementById("app");
    let node = document.createElement("h1");
    node.innerHTML = "hello";
    el.appendChild(node);
    // 强制同步布局
    console.log(app.offsetHeight);
  }
  requestAnimationFrame(reflow);
</script>

(2)布局抖动(layout thrashing)问题

在一段js代码中,反复执行布局操作,就是布局抖动

js
function reflow() {
  let el = document.getElementById("app");
  let node = document.createElement("h1");
  node.innerHTML = "hello";
  el.appendChild(node);
  // 强制同步布局
  console.log(app.offsetHeight);
}
window.addEventListener("load", function () {
  for (let i = 0; i < 100; i++) {
    reflow();
  }
});

(3)减少回流和重绘

  • 脱离文档流
  • 渲染时给图片增加固定宽高
  • 尽量使用css3 动画
  • 可以使用 will-change 提取到单独的图层中

3、静态文件优化

(1)图片优化

图片格式:

  • jpg:适合色彩丰富的照片、banner 图;不适合图形文字、图标(纹理边缘有锯齿),不支持透明度
  • png:适合纯色、透明、图标,支持半透明;不适合色彩丰富图片,因为无损存储会导致存储体积大
  • gif:适合动画,可以动的图标;不支持半透明,不适和存储彩色图片
  • webp:适合半透明图片,可以保证图片质量和较小的体积
  • svg格式图片:相比于jpgjpg它的体积更小,渲染成本过高,适合小且色彩单一的图标;

图片优化:

  • 避免空src的图片

  • 减小图片尺寸,节约用户流量

  • img标签设置alt属性, 提升图片加载失败时的用户体验

  • 原生的loading:lazy 图片懒加载

    html
    <img loading="lazy" src="./images/1.jpg" width="300" height="450" />
  • 不同环境下,加载不同尺寸和像素的图片

    html
    <img
      src="./images/1.jpg"
      sizes="(max-width:500px) 100px,(max-width:600px) 200px"
      srcset="./images/1.jpg 100w, ./images/3.jpg 200w"
    />
  • 对于较大的图片可以考虑采用渐进式图片

  • 采用base64URL减少图片请求

  • 采用雪碧图合并图标图片等

(2)HTML优化

  1. 语义化HTML:代码简洁清晰,利于搜索引擎,便于团队开发
  2. 提前声明字符编码,让浏览器快速确定如何渲染网页内容
  3. 减少 HTML 嵌套关系、减少 DOM 节点数量
  4. 删除多余空格、空行、注释、及无用的属性等
  5. HTML 减少iframes使用 (iframe会阻塞onload事件可以动态加载iframe)
  6. 避免使用 table 布局

(3)CSS优化

建议

  1. 减少伪类选择器、减少样式层数、减少使用通配符

  2. 避免使用CSS表达式,CSS表达式会频繁求值, 当滚动页面,或者移动鼠标时都会重新计算 (IE6,7)

    css
    background-color: expression((new Date()) .getHours() %2 ? "red": "yellow");
  3. 删除空行、注释、减少无意义的单位、css进行压缩

  4. 使用外链css,可以对CSS进行缓存

  5. 添加媒体字段,只加载有效的css文件

    html
    <link
      href="index.css"
      rel="stylesheet"
      media="screen and (min-width:1024px)"
    />
  6. CSS contain属性,将元素进行隔离

  7. 减少@import 使用,由于@import 采用的是串行加载

(4)CSS 书写顺序

建议遵循以下顺序:

js
// 1. 布局定位属性:(建议 display 第一个写,毕竟关系到模式)
display / position / float / clear / visibility / overflow

// 2. 自身属性:
width / height / margin / padding / border / background

// 3. 文本属性:
color / font / text-decoration / text-align / vertical-align / white- space / break-word

// 4. 其他属性(CSS3):
content / cursor / border-radius / box-shadow / text-shadow / background:linear-gradient
css
/* 一个示例 */
.jdc {
  display: block;
  position: relative;
  float: left;
  width: 100px;
  height: 100px;
  margin: 0 10px;
  padding: 20px 0;
  font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
  color: #333;
  background: rgba(0, 0, 0, 0.5);
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -o-border-radius: 10px;
  -ms-border-radius: 10px;
  border-radius: 10px;
}

(5)缩小样式计算范围

  • 降低选择器的复杂性和深度
  • 减少 dom 元素的数量和选择器的数量

缩小样式计算的范围并降低其复杂性

(6)JS优化

  1. 通过asyncdefer异步加载文件

browser

  1. 减少 DOM 操作,缓存访问过的元素
  2. 操作不直接应用到 DOM 上,而应用到虚拟 DOM 上。最后一次性的应用到 DOM 上。
  3. 使用webworker解决程序阻塞问题
  4. IntersectionObserver
js
const observer = new IntersectionObserver(function (changes) {
  changes.forEach(function (element, index) {
    if (element.intersectionRatio > 0) {
      observer.unobserve(element.target);
      element.target.src = element.target.dataset.src;
    }
  });
});
function initObserver() {
  const listItems = document.querySelectorAll("img");
  listItems.forEach(function (item) {
    observer.observe(item);
  });
}
initObserver();
  1. 虚拟滚动 vertual-scroll-list
  2. requestAnimationFramerequestIdleCallback

  1. 尽量避免使用eval, 消耗时间久
  2. 使用事件委托,减少事件绑定个数。
  3. 尽量使用 canvas 动画、CSS动画

(7)字体优化

css
@font-face {
  font-family: "Bmy";
  src: url("./HelloQuincy.ttf");
  font-display: block;
  /* block 3s 内不显示, 如果没加载完毕用默认的   */
  /* swap 显示老字体 在替换 */
  /* fallback 缩短不显示时间, 如果没加载完毕用默认的 ,和block类似*/
  /* optional 替换可能用字体 可能不替换*/
}
body {
  font-family: "Bmy";
}

FOUT(Flash Of Unstyled Text) 等待一段时间,如果没加载完成,先显示默认。加载后再进行切换。

FOIT(Flash Of Invisible Text)字体加载完毕后显示,加载超时降级系统字体 (白屏)

4、优化策略

  • 关键资源个数越多,首次页面加载时间就会越长
  • 关键资源的大小,内容越小,下载时间越短
  • 优化白屏:内联css和内联js移除文件下载,较小文件体积
  • 预渲染,打包时进行预渲染
  • 使用SSR加速首屏加载(耗费服务端资源),有利于SEO优化。 首屏利用服务端渲染,后续交互采用客户端渲染

5、浏览器的存储

  • cookie: cookie 过期时间内一直有效,存储大小4k左右、同时限制字段个数,不适合大量的数据存储,每次请求会携带cookie,主要可以利用做身份检查。

    • 设置cookie有效期
    • 根据不同子域划分cookie较少传输
    • 静态资源域名和cookie域名采用不同域名,避免静态资源访问时携带cookie
  • localStorage: chrome 下最大存储5M, 除非手动清除,否则一直存在。利用localStorage存储静态资源

    js
    function cacheFile(url) {
      let fileContent = localStorage.getItem(url);
      if (fileContent) {
        eval(fileContent);
      } else {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", url, true);
        xhr.onload = function () {
          let reponseText = xhr.responseText;
          eval(reponseText);
          localStorage.setItem(url, reponseText);
        };
        xhr.send();
      }
    }
    cacheFile("/index.js");
  • sessionStorage: 会话级别存储,可用于页面间的传值

  • indexDB:浏览器的本地数据库 (基本无上限)

    js
    let request = window.indexedDB.open("myDatabase");
    request.onsuccess = function (event) {
      let db = event.target.result;
      let ts = db.transaction(["student"], "readwrite");
      ts.objectStore("student").add({ name: "zf" });
      let r = ts.objectStore("student").get(5);
      r.onsuccess = function (e) {
        console.log(e.target.result);
      };
    };
    request.onupgradeneeded = function (event) {
      let db = event.target.result;
      if (!db.objectStoreNames.contains("student")) {
        let store = db.createObjectStore("student", { autoIncrement: true });
      }
    };

6、增加体验 PWA(Progressive Web App)

webapp用户体验差(不能离线访问),用户粘性低(无法保存入口),pwa就是为了解决这一系列问题,让webapp具有快速,可靠,安全等特点

  • Web App Manifest:将网站添加到桌面、更类似 native 的体验
  • Service Worker:离线缓存内容,配合cache API
  • Push Api & Notification Api: 消息推送与提醒
  • App Shell & App Skeleton App壳骨架屏

7、LightHouse使用

sh
npm install lighthouse -g
lighthouse http://www.taobao.com

可以根据 lighthouse 中的建议进行页面的优化

requestFrameAnimation requestAdleCallback

图片 contain

Released under the MIT License.