副标题参考 前端性能优化之旅 (opens new window)

# 性能测试

  1. Network分析:禁用缓存、启用网络限速(4g/3g) 模拟移动端弱网情况下的加载情况
  2. Performance分析:移动端处理性能比pc差,将cpu设置为4x slowdown或6x slowdown
  3. Lighthouse分析

# 缓存

# 发送请求

# MTU 策略

通常情况下,我们认为 TCP 网络传输的最大传输单元(Maximum Transmission Unit,MTU)为 1500B,即网络一个 RTT(Round-Trip Time,网络请求往返时间)时间内可以传输的数据量最大为 1500 字节。因此,在前后端分离的开发模式中,尽量保证页面的 HTML 内容在 1KB 以内,这样整个 HTML 的内容请求就可以在一个 RTT 时间内请求完成,最大限度地提高 HTML 载入速度。

# DNS 预解析

dns 预解析技术。浏览器中默认开启,可以对页面中出现的链接和当前域名进行预解析,当用户点击时加快跳转速度,相同域名不要预加载,因为已经有缓存了。也可以显示的指定没有出现的链接进行预加载

# 使用 CDN

使用 cdn 的好处:多域名加载资源、有可能被加载过浏览器有缓存、效率高、分布式数据中心、数据统计、网站安全 ​​​​。

配合cdn的combo技术还可以把多个资源文件合并成一次请求,语法是??:

<link rel="stylesheet" href="//g.alicdn.com/msui/sm/0.6.2/css/??sm.min.css,sm-extend.min.css">
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/??sm.min.js,sm-extend.min.js' charset='utf-8'></script>

# 服务端响应

  1. 浏览器在同一时刻向同一域名请求文件的并行下载数是有限的,,因此可以利用多个域名主机来存放不同的静态资源。
  2. 网关和直出服务机房部署不在同一区域,导致延时。

# 页面解析与处理

可以通过 chrome Coverage 查看代码覆盖率,然后通过 tree shaking 和懒加载代码解决首页代码体积

# iframe

加载 iframe 有可能会对页面的加载产生严重的影响,在 onload 之前加载会阻塞 onload 事件触发,从而阻塞 loading。可以将加载iframe的时机放到onload之后。并使用setTimeout触发异步加载iframe,可避免iframe带来的loading影响。

# 布局抖动

图标缺失、背景图缺失、字体大小改变导致页面抖动、出现非预期页面元素导致页面抖动会影响用户体验。

主要优化内容:

  1. 确定直出页面元素出现位置,根据直出数据做好布局
  2. 页面小图可以通过base64处理,页面解析的时候就会立即展示
  3. 减少动态内容对页面布局的影响,使用脱离文档流的方式或定好宽高

# 骨架屏

非直出页面加载数据时,提供骨架屏优化体验。

# 页面静态资源

资源加载顺序 (opens new window)

# html

不要缓存html,防止更新html后导致请求资源404的问题,方法:

  1. 增加如下头
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-cache">
<meta http-equiv="Cache" content="no-cache">
  1. 使用地址+?随机数

# CSS

  1. 带有 3D 属性优先使用 3D 的属性,会调用 GPU 加速。在移动端开发中,直接使用 transition 动画会让页面变慢,甚至变卡顿,所以我们通常添加 transform:translate3D(0,0,0)或 transform:translateZ(0)来开启移动端动画的 gpu 加速,让动画过程更流畅。 ​​​​
  2. css不会阻塞页面解析,但是会阻塞页面渲染。如果CSS文件较大或弱网情况,会影响到页面渲染时间,影响用户体验。
  3. 删除没用到的css:PurgeCSS (opens new window).

# 图片

  1. 优化请求数
    1. 在页面中图片较少,并且图片大小不超过 2kb 时可以使用 base64 内嵌图片,可以减少请求次数。
    2. 避免 href 和 src 为空,即使为空浏览器还是会加载,这样会阻塞页面中其他资源的加载。 ​​​​
  2. 预加载
  3. 懒加载,为什么要懒加载?减少不必要的请求,把网络资源让出来
  4. 图片加载顺序,优先级hight加载完毕之后才开始low的加载
  5. 尺寸优化:使用CDN 图床尺寸大小压缩功能,根据不同的设备渲染不同大小的图片调整图片格式,根据网络情况,渲染不同清晰度的图。配合使用 img 标签 srcset/sizes 属性和 picutre 标签实现响应式图片
<img src="cat-large.jpg" srcset="cat-small.jpg 480w, cat-large.jpg 1080w" sizes="50vw">
  1. gif体积过大,使用video替换,优先使用webm,备用mp4:
<video autoplay loop muted playsinline>
    <source src="video.webm" type="video/webm">
    <source src="video.mp4" type="video/mp4">
</video>

参考:https://juejin.cn/post/6844903893143388168

  1. 响应式图片,不同分辨率的设备显示不同大小的图片,常用的方式是媒体查询。此外,还可以使用 HTML5 的 picture 属性进行响应式处理。还可以通过设置type优先使用新格式图片,来提供更好的优化。
<picture>
  <source srcset="src/img/l.webp" type="image/webp" media="(min-width: 1200px)" />
  <source srcset="src/img/l.png" media="(min-width: 1200px)" />
  <source srcset="src/img/2.png" media="(min-width: 992px)" />
  <source srcset="src/img/4.png" media="(min-width: 768px)" />
  <img src="src/img/4.png" />
</picture>

# svg

svg可以通过npmsvgo或者线上服务svgomg (opens new window)来压缩

# 字体

  1. 使用网络字体时,下载字体会耗费一些时间,而使用font-display可以控制字体下载完成之前的渲染行为
  2. 当字体文件比较大的时候,也会影响到页面的加载和渲染,可以使用 fontmin 将字体资源进行压缩
  3. 在线查找字体:https://google-webfonts-helper.herokuapp.com/fonts

# 多线程

  1. web worker,使用web worker处理第三方js的加载,目前有成熟的解决方案:https://github.com/BuilderIO/partytown

# 运行时

# 重排和重绘

页面生成后,如果页面元素位置发生变化,就要从布局阶段开始重新渲染,也就是页面重排,所以页面重排一定会进行后续重绘;如果页面元素只是显示样式改变而布局不变,那么页面内容改变将从绘制阶段开始,称为页面重绘。重排通常会导致页面元素几何大小位置发生变化且伴随着重新渲染的巨大代价,因此我们要尽可能避免页面的重排,并减少页面元素的重绘。

# 节流(throttle)

节流指的是某个函数在一定时间间隔内(例如 3 秒)只执行一次,在这 3 秒内 无视后来产生的函数调用请求,也不会延长时间间隔。3 秒间隔结束后第一次遇到新的函数调用会触发执行,然后在这新的 3 秒内依旧无视后来产生的函数调用请求,以此类推。
实现方式有两种:

  1. 第一种是用时间戳来判断是否已到执行时间,记录上次执行的时间戳,然后每次触发事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经达到时间差(Xms) ,如果是则执行,并更新上次执行的时间戳,如此循环。
  2. 第二种方法是使用定时器,比如当 scroll 事件刚触发时,设置个 1000ms 的定时器,此后每次触发 scroll 事件触发回调,如果已经存在定时器,则回调不执行方法,直到定时器触发,handler 被清除,然后重新设置定时器。
    第三方实现:
// Lodash.js
jQuery(window).on("scroll", _.throttle(updatePosition, 100));

# 防抖(debounce)

指定时间内只执行一次回调函数,如果在指定的时间内又触发了该事件,则回调函数的执行时间会基于此刻重新开始计算。原生实现:

debounce(fn, time) {
    this.timeId = null;
    return (...args) => {
        if (this.timeId) clearTimeout(this.timeId);
        this.timeId = setTimeout(function() {
            fn.apply(this, args);
        }, time);
    };
}

第三方实现:

// Lodash.js
jQuery(window).on("resize", _.debounce(calculateLayout, 150));

# requestAnimationFrame

如果我们不考虑兼容性,追求精度比较高的页面效果,可以考虑试试 html5 提供的 API--requestAnimationFrame。

与 setTimeout 相比,requestAnimationFrame 的时间间隔是有系统来决定,保证屏幕刷新一次,回调函数只会执行一次,比如屏幕的刷新频率是 60HZ,即间隔 1000ms/60 会执行一次回调。

var throttle = function (fn, delayTime) {
  var flag;
  return function () {
    if (!flag) {
      requestAnimationFrame(function () {
        fn();
        flag = false;
      });
      flag = true;
    }
  };
};

上述代码的基本功能就是保证在屏幕刷新的时候(对于大多数的屏幕来说,大约 16.67ms),可以执行一次回调函数 fn。使用这种方式也存在一种比较明显的缺点,时间间隔只能跟随系统变化,我们无法修改,但是准确性会比 setTimeout 高一些。

# 事件代理

页面元素尽量使用事件代理而不是事件绑定,可以避免每个元素都进行绑定,并且可以避免内存泄露及动态生成元素的事件绑定问题 ​​​​

# 编译

# js

不使用最新语法特性可以减少bundle size

# 第三方包

检查第三方或者本地重复引用的包Webpack Bundle Analyzer (opens new window)

# 预加载

# preload

当前页面优先级高的加载。哪些元素支持preloads?

  • audio
  • document
  • embed
  • fetch
  • font
  • image
  • object
  • script
  • style
  • track
  • worker
  • video
<head>
  <link rel="preload" href="critical.css" as="style">
  <link rel="preload" href="critical.js" as="script">
</head>

# prefetch

下一个页面有可能会用到的资源,预先加载

# dns-prefetch

dns-prefetch 仅对跨域 (opens new window)域上的 DNS查找有效。是尝试在请求资源之前解析域名。这可能是后面要加载的文件,也可能是用户尝试打开的链接目标。

当浏览器从(第三方)服务器请求资源时,必须先将该跨域 (opens new window)域名解析为 IP地址,然后浏览器才能发出请求。此过程称为 DNS解析。DNS 缓存可以帮助减少此延迟,而 DNS解析可以导致请求增加明显的延迟。

<link rel="dns-prefetch" href="https://fonts.googleapis.com/"> 

# 性能指标

参考: 用 Webpack 解决应用性能问题 (opens new window)

该参考值考虑到了移动端与国外等多种访问环境:

  • 页面初载时,所有未压缩的 JavaScript 脚本大小:<=200KB;
  • 页面初载时,所有未压缩的 CSS 资源大小:<=100KB;
  • HTTP 协议下,请求资源数:<=6 个;
  • HTTP/2 协议下,请求资源数:<=20 个 ;
  • 90%的代码利用率(也就是说,仅允许 10% 的未使用代码);

# 懒加载

使用lazysizes,支持img和iframe。大部分情况下所有图片都应该使用lazyload,这样即使改变滚动位置后刷新也可以保障顶部和其他区域图片的懒加载。

还可以使用content-visibility,值为auto时,网页只渲染可见区域内容,跳过不可见区域的元素。

# 秒开率

页面的加载时长是页面性能的一个重要指标。有一个指标叫秒开率,关于秒开率,有一个  1 秒钟法则的说法:2G 网络  1 秒进入页面,3G 网络  1 秒首屏,4G 网络  1 秒页面加载完毕。

# Web Vitals

https://mp.weixin.qq.com/s/BDnlY0sVpKDFdb1_dk2dVQ

# 性能监控

页面性能监测之performance (opens new window)

  1. performance.timing ,过时
  2. PerformanceNavigationTiming, 局限是只能在window.onload获取一次
  3. PerformanceObserver, 动态监听,可以在监听的事件触发时立即响应

# 埋点

埋点的方式有以下几种:

  1. ajax
  2. img
  3. Navigator.sendBeacon()

# SEO

# google

# 富媒体搜索结果(Rich Results)

搜索的摘要由网页内容或者meta内容生成

多媒体摘要,需要手动编写:https://developers.google.com/search/docs/advanced/structured-data/search-gallery

# title

  1. 不要使用关键字堆砌,如Foobar, foo bar, foobars, foo bars

# meta

  1. keyword
  2. description

# html 标签

img 标签中的 alt 有利于 SEO ​​​​

# title

  1. 百度更容易搜索到符号,首页:网站名称介绍,列表页:列表名称网站名称。文章页:文章标题文章分类_网站名称 ​​​​

2

# meta

# description

描述网站内容

# 百度 SEO

白杨算法:

算法内容:针对移动站点有地域属性,加上地理位置标识,即有机会获得优先排名。如,酒店服务类型网站,会分不同城市,网站加上地理位置标识,用户在移动端搜索 地域+酒店,则会比没加标识的站点来得有利些。
实操说明:
在地域优化的的过程中,站长通过在 META 标签中添加地理位置信来完成。
以下是白杨算法 META 地理位置信息的格式、添加方式和提交:

(1)、Meta 声明格式

Name 属性的值是 location,Content 的值为 province=北京;city=北京;coord=116.306522891,40.0555055968

解说:province 为省份简称,city 为城市简称,coord 是页面信息的经纬度坐标,采用的是 bd09ll 坐标

另外可以查看 百度搜索引擎优化指南 2.0 (opens new window)百度算法 (opens new window)获取更多百度 seo 帮助

# 文章

  1. 提高Web页面渲染速度的7个技巧 (opens new window)
  2. 性能优化相关API (opens new window)