我所知道的 Web 性能优化策略

前言

本文核心分为两部分,第一部分讲述普通浏览器中能干的事情,第二部分则讲述在自建容器的背景下更能干的事情。html

文章内容会比较粗略,若是你对具体实现感兴趣,欢迎在 留言。前端

1、止步于浏览器

1.1 DNS Prefetch

一般状况下,一个 html bundle 里面通常会有 script 等标签去加载其余的资源。浏览器在加载完 html 以后,就会去加载 script 等标签里面的内容,大多状况下,这种标签里 uri 的 host 和当前页面的每每是不相同的,那就会涉及到 DNS 解析的问题,会有必定程度的损耗。git

在 HTML 里面加入 DNS Prefetch 则会让浏览器提早进行 DNS 的解析而且缓存到系统中,这样就能够提高网页的加载效率了。github

<link rel="dns-prefetch" href="//img.alicdn.com">
复制代码

1.2 域名收敛

在像 v2ex 这样的社区论坛中,每每会有不少用户外链图片,不一样的图片有不一样的域名。这个时候 DNS Prefetch 会显得很无力,若是说在图片上传以后作必定的 转化 或者 同步,把域名收敛到同一个域名中,这样就能弥补相关的缺憾了。web

aaa.com/jjj.png ->  mycdn.com/aaa/jjj.png
bbb.com/jjj.png ->  mycdn.com/bbb/jjj.png
复制代码

1.3 加载合适的图片

同一个图片在 在不一样的设备、不一样的网络条件、不一样的渲染面积下,统一加载同样的尺寸不免是 “奢侈” 的,利用 CDN 裁剪 + 前端嗅探 去加载不一样体积、不一样压缩比率、不一样格式的图片,能在节省很多流量的同时提高很大的性能。算法

目前主流 CDN / OSS 都是支持经过 URL 后缀进行图片裁剪的,好比:阿里云小程序

http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/crop,x_100,y_50
复制代码

在前端嗅探上,通常咱们能够嗅探 是否支持 webp、客户端分辨率、当前网络情况,去全局设置不一样的图片加载 URL。微信小程序

base6四、浏览器

1.4 不要展现 “绝对Loading / 占位”

当前时代,大部分网页的数据都是动态下发的,甚至千人前面,为了减小用户等待的焦灼感,每每会设置一个 Loading 动画或者 骨骼图占位。但当请求响应足够快的时候,会发现这种 Loading 或 占位 却会给人相反的感受 —— 瞬间的闪动。缓存

针对这种状况,在使用 Loading 或骨骼图占位的,能够作必定的优化,好比请求发起后 200ms 以上还未返回数据才展现占位图。在 React Suspense 中,为了这种效果官方甚至加了一个 API。

1.5 资源 combo

在 HTTP 请求中,请求建立每每由于 TCP 三次握手等会有一个很是大的开销。涉想在 HTTP 1.0 时代,若是页面上有 50 个 Script 标签,会有 50 次的请求建立,在资源不是那么大的状况下,请求建立的耗时每每会远大于资源真正的下载时间。

在服务端作资源 Combo,而后再往 CDN 回源,就能很大程序上减小这种开销。

xx.com/a.js
xx.com/b.js
xx.com/c.js
xx.com/d.js

xx.com/combo?a.js,b.js,c.js,c.js
复制代码

在 HTTP 2.0 时代资源要不要 Combo + 多少个资源(多大的体积)Combo 到一块儿 是个有意思的话题,这里不作讨论。

1.6 在线 Shim

前端由于浏览器问题一直存在店铺这种东西,有不少的特性每每在新的浏览器版本里面已经支持,但奈何老的不支持须要作垫片。若是对代码统一加垫片则又会让新浏览器很尴尬(我要这新特性有何用?),使用在线的 Shim 应该是一个不错的 Shim。

好比著名的 polyfill.io/v3/url-buil… 就是一个这样的服务。在支持 Object.assign 中访问 polyfill.io/v3/polyfill… , 会获得:

/* Disable minification (remove `.min` from URL path) for more info */
复制代码

而在不支持的浏览器中访问,会获得:

(function(undefined) {if (!(// In IE8, defineProperty could only act on DOM elements, so full support
// for the feature requires the ability to set a property on an arbitrary object
'defineProperty' in Object && (function() {
	try {
		var a = {};
		Object.defineProperty(a, 'test', {value:42});
		return true;
	} catch(e) {
		return false
	}
}()))) {!function(e){
...
复制代码

在国内也有这样的服务,好比:polyfill.alicdn.com/polyfill.mi… ,若是你以为不够信赖,能够自建而后部署在 CDN 上。

1.7 分离静态资源

在绝大多数状况下,访问静态资源的时候并不须要知道 Cookie 信息,为静态资源使用一个新的域名能有效避免用不着的 Cookie 上传,能减小一部分无用流量的传输。

1.8 使用 requestAnimationFrame 实现 60 FPS 动画

在绘制动画的时候,优先使用 requestAnimationFrame,会充分利用分利用显示器的刷新机制,实现 60 FPS 的感受。

1.9 节流 & 防抖

在 Web 中,像 Scroll 这种事件,在界面操做中触发的频率是很是之高的。涉想这样的一种场景:用户往下滑动网页,当滑动距离超过 1000px 的时候,右下角展示一个 回到顶部 的按钮。想固然的操做就是监听 Scroll 事件,当值大于 1000px 的时候展现 按钮,但由于 Scroll 的高频率触发,尤为在移动端这样作就能感受到比较明显的性能问题了,若是咱们对其加个操做 —— 1s 内检测函数只触发 1 次 或者在用户停下来的时候再去检测位置。这样页面总体就会流畅不少了,相对应的两个操做就是 节流 和 防抖。

在 Web 开发过程当中,对于这种高频次触发的事情,合理的进程节流和防抖能在很大程度上增长页面流产度。

1.10 使用新版本 JavaScript

通常状况下,V8 等 JavaScript 的 Runtime 都会对新特性有优化,在新浏览器上使用 Babel 转化后的代码不免会有必定的浪费与惋惜。在新浏览器上使用新预发,老浏览器上使用老语法,才是比较好姿式。

实现上一种思路就是在线 Shim;第二种思路是正对先加载 seed 再加载 bundle 代码,能够在加载 bundle 以前作一个知否支持新版本 ES 的判断(好比是否支持 async 函数),而后再加载相应的 Bundle。

1.11 性能测量

window.performace 能展现绝大多数检测 Web 性能的指标,在业务代码中埋点收集 window.performance 的值,能够为网页性能短板作很好的测量与统计。

1.12 善用 LocalStorage

在一些场景下,每次用户进入时数据的变化不会太大,好比不怎么更新的我的博客页面。这个时候就可使用 LocalStorage 去作 HTML 的缓存,页面进入的时候直接从 Storage 中获取缓存,而后 append 到页面上,等接口数据回来以后,再 Diff 作更新。

在新版本浏览器中,能够用 indexedDB 等代替 LocalStorage。

1.13 组件级缓存

在 SPA 网站中,加载 bundle 大体上能够分为两份:

  1. 全部的组件代码和业务代码打到一块儿,和业务代码一块儿输出
  2. 组件代码在组件内部各自打包,业务代码打包的时候 external 掉组件代码,最后 combo 到一块儿输出。

针对第二种状况,能够利用 LocalStorage 等单独缓存组件代码(带上版本号),在端侧实现一个 Combo 的机制(有 Cache 取 Cache,没 Cache Fetch),这样一来,就能让一个网站的多个页面享受同份缓存,让之第一次也能很是快速的访问。

1.14 GZIP & BBR

GZIP 压缩使用 Deflate 能有效压缩文本资源的大小,在现代浏览器中,对 GZIP 的支持也是很是良好。值的注意的是,GZIP 的压缩并非压的越小越好,过小会产生压缩性能的问题。

传统 TCP 使用的是基于丢包的拥塞控制算法,但并非全部的丢包都是网络堵塞所致使的,为此 Google 开发了 BBR 拥塞控制算法,能有效提高服务器的吞吐量,若是服务器支持的话,能够开启 BBR 来加快网络传输。

1.15 Service Worker

Service Worker 本质上充当Web应用程序与浏览器之间的代理服务器,利用 Service Worker 能够极致的控制每一个请求,进而能够对 Web APP 在浏览器上作离线处理。

传说中的 PWA 就是对这个东西的一个极致应用。

1.16 WebWorker

WebWorker 为 JavaScript 在浏览器中多线程调用提供了能力,可让主线程建立 Worker 线程,针对一些密集计算或者须要时间比较高的场景,是很是有效的。好比:网页版邮箱附件上传等。

2、外探于自建容器

2.1 WKWebview

从 iOS8 开始,iOS 提供了 WKWebview 来代替 UIWebview。相比于 UIWebview,在性能和内存控制上都有很是大的提高,固然问题也是有的,好比 Cookie 同步问题等,但坑总能趟过去。

回到优势上,WKWebview 给前端最直接的体验莫过于 “Scroll 终于再也不须要滚动中止下来才能触发了”,进而 LoadMore 等会更加的流畅。

2.2 Webviw 内核内置

Android 的碎片化一直是一个很严重的问题,即便是在今天。经过内置高性能的 Webview(好比 U四、X5)等,会为整个 APP Web 页面提速很多,在兼容性方面,也会好不少。

2.3 资源 Cache

要实现页面秒开效果,Cache 确定是第一优先级,经过下发离线包,让页面上的资源(HTML,CSS)离线,就能够很大程度上提高页面的性能。

在离线体系建设上,主要有两点须要考虑:1. 离线若是能快速下发,快速覆盖新版本 2. 前端如何才能无感知接入离线体系。

针对第一个问题,每每采起推+拉结合的方式;针对第二个问题,在实现的时候采用拦截网络的策略,就能够避免前端对离线的感知了

2.4 代理请求

如前文所述,建立一个 HTTP 请求是很是耗时的,从客户端的角度来看,是能够去优化这种请求的,好比被普遍使用的 Spidy。

在 WebAPP 中,发出一个数据请求,让走容器的经过而非 WebView 的通道,不只有机会能让速度变快,同时还能够进行相应的加密,让抓包者懵逼。

2.5 数据预加载

试想一个网页的加载过程,加载 HTML -> 加载 JS -> 执行 JS -> 请求数据 -> 再次渲染。请求数据的流程是比较靠后的,在 WebAPP 中,若是能让这个请求或者请求到的数据提早,则会进一步提高网页速度。

一个比较经常使用的方式是往客户端下发一份配置,标识 页面地址、请求入参、缓存时间等信息,让客户端在访问这个 URL 的时候去加载数据,等前端代码真正请求的时候,直接拿客户端提早请求到的数据;或者说在前一个页面调用接口通知客户端去请求相关的接口。

目前这种作法应用比较普遍,好比:微信公众号,UC Feed 流等。

2.6 阉割版 WebView

WebView 由于各类历史缘由,“慢” 一直徘徊在他的左右。去定制 WebView,甚至去实现一层上层 DSL,只保存比较优秀的部分,也能在限制部分场景的同时去提高页面性能。

最典型的设计就是微信小程序了,阉割版的 WebView + Cache 机制,很难让人会以为这是一个 WebAPP

2.7 Weex/React Native

Weex/RN 相比于阉割版 WebView,是一个更加激进的优化访问。核心原理是本身造一个高性能的 Runtime,和客户端高度配置,由 JS Bundle 去 call native,用 Native 的方式去渲染页面。

同时对于 Web 上性能上有问题的部分,能够用传入表达式的方式让 Native 去执行。好比注明的 BindingX,就是传入一个表达式,而后去客户端去执行这个表达式,进而避免了频繁 call native 的问题,这样就能够保证动画的流畅运行了。

2.8 Flutter

...

原文地址:github.com/rccoder/blo…

相关文章
相关标签/搜索