大前端性能总结

5,657 阅读5分钟

Node端性能

性能评估

  1. 通过性能测试工具评估:

    • wrk:支持lua脚本,可以轻松应对各种测试需求,如
    -- 带随机参数请求
    function request()
        wrk.headers["Connection"] = "keep-Alive"
        arg = math.random(1, 500)
        path = "/random?arg=" .. arg
        return wrk.format("GET", path)
    end
    
  2. 通过v8 profiling查看程序执行过程数据,与Webstorm联合使用,可以在Webstorm【Run/Debug Configurations】里面配置,注意需要先安装:v8-profiler

常见优化方案

  1. 并发请求,例如:promise.all并发三方HTTP接口调用、RPC调用
  2. 如果用到mysql
    • 使用数据库连接池,重用连接
    • 常用查询建立索引
    • 用程序避免联合查询
    • 读写分离
  3. 加入缓存策略

前端性能

DNS查询

  • 减少DNS查询次数

TCP/HTTP

优化核心

  1. 消除和减少不必要的网络延迟
  2. 把传输字节数降到最少

具体步骤

  • 减少HTTP请求:对于HTTP1.0/1.1 减少请求资源数(打包、压缩、合并等)
  • 使用CDN
  • 添加Expires首部并配置ETag标签
  • GZip资源
  • 避免HTTP重定向
  • 持久化连接:避免TCP的三次握手,HTTP1.1默认开启,HTTP1.0可以使用:Connection: Keep-Alive
  • HTTP1 不支持多路复用,可以为一台主机并行打开多个TCP会话(一般为6个)
  • 消除不必要的请求字节(HTTP1 请求、响应头不会被压缩)
  • 嵌入资源,如:Base64嵌入资源(针对小的静态图片资源)

客户端渲染

关键渲染路径:DOM -> CSSDOM / JS -> RenderTree -> Layout -> Paint

浏览器渲染过程

构建DOM

  • 构建过程:character -> token -> node -> DOM
  • 增量构建

构建CSSOM(CSS Object Model)

  • 构建过程:character、token、node、CSSOM
  • 选择器越复杂,匹配用的时间约多

构建RenderTree

  • RenderTree包含所有需要呈现在页面上的节点信息
  • display: none的元素不会被添加到RenderTree中,因为它不需要被渲染,visibility: hidden的元素会被添加到RenderTree中

Layout

  • 计算需要渲染的节点的大小和位置
  • 节点位置和大小是基于将viewport计算的
  • 在移动端通常将viewport设置为浏览器推荐的理想视口,以保证字体显示大小易于阅读
  • 旋转屏幕、修改浏览器窗口大小,修改位置大小相关的CSS属性,都可能触发Layout

Paint

  • 根据background、border、box-shadow等样式,将Layout生成的区域填充为最终将显示在屏幕上的像素

资源优化

CSS性能优化(防止资源阻塞初次渲染)

  • media query(link里面加上media)
    • 此时样式表仍然会加载,当浏览器环境不匹配媒体查询条件时,该样式表不会阻塞渲染
    • 针对不同媒体环境拆分CSS文件,避免为了加载非关键CSS资源,而阻塞初次渲染
  • 使用DOM API添加link不会阻塞初次渲染
var style = document.createElement('lin');
style.rel = 'stylesheet';
style.href = 'index.css';
document.head.appendChild(style);
  • preload & prefetch & prerender(不止可以用于CSS)
    • rel="preload",不是stylesheet不会阻塞渲染
    • preload是resource hint规范中定义的一个功能,resource hint告知浏览器提前建立连接或加载资源,以提高资源加载的速度:
      • preload:https://www.w3.org/TR/preload/
      • prefetch & prerender:https://w3c.github.io/resource-hints/#prefetch
    • 浏览器遇到标记为preload的link时,会开始加载它
    • 当onload事件发生时,将rel改为stylesheet,即可应用此样式
    • 可以通过 loadCSS.js 使用preload (CSS preload polyfill):原理其实就是使用DOM API
<link rel="preload" type="text/css" href="./style.css" as="style" onload="this.rel='stylesheet'"/>

JS性能优化

  • JS会阻塞HTML Parse(HTML解析器,增量),因而会阻塞出现在脚本后面的HTML标记的渲染
  • 原因:JS可以通过document.write修改HTML文档流,因此在执行JS时,浏览器会暂停解析DOM的工作
  • CSS会阻塞JS
  • 浏览器资源加载策略(preload,与前面CSS preload不一样,详见:https://juejin.cn/post/6844903545016156174)
    • 当HTML Parser被脚本阻塞时,Parser虽然会停止构建DOM,但仍会识别该脚本后面的资源,并提前加载
  • 性能优化(防止阻塞)
    • 将资源放到body底部
    • 使用defer延迟脚本执行:使用defer,该脚本会被推迟到整个HTML文档解析完后,再开始执行。被defer的脚本,在执行时会严格按照在HTML文档中出现的顺序执行。使用defer方法,可以提早脚本资源加载
    • 使用async异步加载脚本:该脚本不会阻塞HTML parser,也不会被CSS阻塞,脚本加载完就开始执行。async适用于无依赖的独立资源(比如百度统计代码)

图片

  • 使用blob异步加载
  • 使用img-2代替img标签(原理与上面一样),对于图片多的网站轻松提升五倍以上访问速度

字体

  • 浏览器为了避免FOUT(Flash Of Unstyled Text),会尽量等待字体加载完成后,再显示应用了该字体的内容。带来了FOIT(Flash Of Invisible Text 问题),导致空白
  • 设置多字体,降级方法:使用默认字体
  • 异步加载字体文件:通过异步加载CSS,即可避免字体阻塞渲染,还是会空白

其它

优化关键渲染路径

优化目标

  • 关键资源数
  • 关键资源体积
  • 关键资源网络来回数

勿盲目内联资源

  • 若启用HTTP2,则无需内联资源
  • 若资源被多个页面共享,则无法充分利用缓存,导致重复得下载

内联与缓存结合

  • 首次访问,使用内联,并通过异步(如:rel="prefetch")请求缓存资源,缓存成功通过cookie标记,下次访问时,则只返回外部资源标记

其它

常用工具

性能测试工具

相关资料

补充

TTFB(Time To First Byte):

  • 从客户端发出请求到接收到第一个响应字节所花费时间(Transfer-Encoding: chunked)

HTTP2.0主要设计:

  • 解决HTTP中“队首阻塞”;
  • 二进制分帧机制,无需建立多个TCP连接,从而改进TCP利用率
  • 保持HTTP1.1语义

队首阻塞:

  • HTTP应用层队首阻塞:按照优先级发送请求
  • TCP传输层队首阻塞:TCP要求分组严格按照顺序交付

React性能优化Time Slicing(附Suspence):