WEB前端性能优化
请求过程中一些潜在的性能优化点
深入理解http请求的过程是前端性能优化的核心!
- dns是否可以通过缓存减少dns查询时间?
 - 网络请求的过程走最近的网络环境?
 - 相同的静态资源是否可以缓存?
 - 能否减少请求http请求大小?
 - 减少http请求数量
 - 服务端渲染
 
DNS解析过程

服务器端请求处理

HTTP状态码

注意:
1、文件合并存在的问题。
- 首屏渲染问题(公共库合并)
 - 缓存失效问题(不同页面的合并)
 
2、资源的合并与压缩(html,css,js压缩和混乱,文件合并,开启gzip)。
- 目的1:减少请求http请求大小
 - 目的2:减少http请求数量
 
图片相关的优化的核心概念
png8/png24/png32之间的区别?
- png8 --- 2^8(256)色 + 支持透明
 - png24 --- 2^24色 + 不支持透明
 - png32 --- 2^24色 + 支持透明
 
不同格式图片的特点和常用的业务场景?
- jpg有损压缩,压缩率高,不支持透明(不需要透明图片的的业务场景)
 - png支持透明,浏览器兼容性好(需要透明图片的业务场景)
 - webp压缩程度更好,在ios webview中有兼容性问题(安卓全部)
 - svg矢量图,代码内嵌,相对较小(图片样式相对简单的场景)
 
对图片的优化有哪些?
- 对图片进行压缩png24->png8(可以使用:https://tinypng.com/)
 - CSS雪碧图(可以使用用这个网站生成雪碧图相关的css代码:http://www.spritecow.com)
 - Image inline(base64编码)
 - 使用矢量图
 
css、js加载与执行(考虑优化点)

HTMLParser解析器自上而下解析,生成DOM树。
外部资源link、script,浏览器会发起请求。
解析CSS生成CSS规则树,进而构建渲染树(计算element位置,布局layout)。
接着调用操作系统NativeGUI的API进行绘制。
浏览器渲染页面

HTML渲染过程的一些特点
- 顺序执行、并发加载
 
外部资源并发请求,一个域名下的并发请求数是有上限的。
所以一般将网站的静态资源托管在多个CDN下。
- css阻塞
 
css在head中可以阻塞页面渲染、css可以阻塞js执行,但是css不阻塞外部脚本的加载。
- js阻塞
 
直接引入的js阻塞页面的渲染(asyn[异步下载、立即执行]、differ[并行下载、顺序执行]这两种方式加载js例外)
js不阻塞资源的加载(预资源加载器)
js顺序执行,阻塞后续js逻辑的执行(单线程)
懒加载、预加载
懒加载:图片进入可视区域之后请求图片资源。
懒加载的实现:
1.JS判断图片是否进入可视区域。
2.当进入时,修改img的src属性为实际图片地址。
预加载:图片等静态资源在使用之前提前请求。
预加载实现:
1.使用标签(<img src="xxx" style="display:none">)
2.使用Image对象(new Image();)
3.使用XMLHTTPRequest对象(资源跨域问题)
回流与重绘
UI线程和js线程互斥。
 css性能能让JavaScript变慢。
 频繁触发重绘与回流,会导致UI频繁渲染,最终导致js变慢。
 回流:  当render tree中的一部分(或全部)因为元素的规模尺寸,布局,隐藏等改变而需要重新构建,
这就称为回流(reflow)。
- 页面渲染初始化
 - 盒子模型相关属性变化
 - 窗口resize事件触发
 - DOM结构变化,比如删除了某个节点
 - 获取某些属性,引发回流 很多浏览器会对回流做优化,他会等到足够数量的变化发生,在做一次批处理回流。 但是除了render树的直接变化。
 
获取以下属性时会引发回流:
width,height
offsetTop/Left/Width/Height
scrollTop/Left/Width/Height
clientTop/Left/Width/Height
调用了getComputedStyle(), 或者 IE的 currentStyle
 重绘:  当render tree中的一些元素需要更新属性,而这些属性只是影响元素的外观,风格,而不会
影响布局的,比如background-color,这就称为重绘。
Chrome中满足以下任意情况就会创建图层:
video、canvas都是一个独立的图层。
- 3D或透视变换(perspective transform)CSS属性
 - 使用加速视频解码的<video>节点
 - 拥有3D(WebGL)上下文或加速的2D上下文的<canvas>节点
 - 混合插件(如Flash)
 - 对自己的opacity做CSS动画或使用一个动画webkit变换的元素
 - 拥有加速CSS过滤器的元素
 - 元素有一个包含复合层的后代节点(一个元素拥有一个子元素,该子元素在自己的层里)
 - 元素有一个z-index较低且包含一个复合层的兄弟元素(换句话说就是该元素在复合层上面渲染)
 
常用的会独立为一个图层的属性:
transform:translateZ(0);
will-change:transform;
怎么优化呢?
- 将频繁重绘回流的DOM元素单独作为一个独立图层,那么这个DOM元素重绘和回流的影响只会在这个图层中。(但是:浏览器图层合成也会花比较多的时间,所以,是否新建图层得看实际情况,具体问题,具体分析。)
 - 用
translate(改变不会触发浏览器重新布局,但是元素仍会占据原始位置)替代top(会触发重新布局)改变。 - 用
opacity(不会触发重绘)替代visibility(会触发重绘)。 - 不要一条一条地修改DOM的样式,预先定义好
class,然后修改DOM的className。 - 把DOM离线后修改。(eg:先把DOM给
display:none,然后修改100次,再把它显示出来。) - 不要把DOM节点的属性值放在一个循环里当成循环里的变量(offsetHeight、offsetWidth)
 - 不要使用
table布局,一个小的改动会造成整个table的重新布局。 - 对动画新建图层。
 - 启用GPU硬件加速。(transform:translate3d(0,0,0)、transform:translateZ(0))
 
浏览器存储
Cookie相关
Cookie 因为HTTP请求无状态,所以需要Cookie去维护客户端状态。
cookie的生成方式:
- 1.http response header中的 set-cookie(服务端生成,客户端保存)
 - 2.js中可以通过
document.cookie可以读写cookie(客户端自身数据的存储) 
cookie存储的限制:
- 1.作为浏览器端存储,大小4kb左右
 - 2.需要设置过期时间`expire
 
对于cookie的优化:
cookie中在相关域名下面--cdn的流量损耗
解决方法:__ cdn的域名和主站的域名要分开 __
对于cookie的读写操作:
// 写入
document.cookie = "username=hello";
// 读取
let cookie = document.cookie;
/**
  * param [String] cookie
  * return [Object] object
  */
function getCookie(cookie){
    if(!cookie){ return null; }
    let reg = /\s*([^;]+)\s*=\s*([^;]+)\s*/g;
    let obj = {};
    cookie.replace(reg,($0,$1,$2) => {
        if($1&&$2){
            obj[$1] = $2;
        }
    });
    return obj;
} cookie存储数据能力被localstorage替代。 
 httponly  :不允许js读写。(防止盗用cookie)
LocalStorage相关
- HTML5设计出来专门用于浏览器存储的
 - 大小为5M左右
 - 仅在客户端使用,不和服务器进行通信
 - 接口封装较好
 - 浏览器本地缓存方案
 
对于LocalStorage的读写操作:
// 写入
localStorage.setItem('username','hello')
undefined
// 读取
localStorage.getItem('username')
"hello"SessionStorage相关
- 会话级别的浏览器存储
 - 大小为5M左右
 - 仅在客户端使用,不和服务器进行通信
 - 接口封装较好
 - 对于表单信息的维护(多页表单数据的维护)
 
对于 SessionStorage的读写操作:
// 写入
 SessionStorage.setItem('username','hello')
undefined
// 读取
 SessionStorage.getItem('username')
"hello"IndexedDB(用的比较少)
- IndexedDB是一种低级API,用于客户端存储大量结构化数据。
 - 为应用创建离线版本
 
PWA相关
- 可靠:在没有网络的环境下也能提供基本的页面访问,而不会出现“未连接到互联网”的页面。
 - 快速:针对网页渲染及网络数据访问有较好优化。
 - 融入(Engaging):应用可以被增加到手机桌面,并且和普通应用一样有全屏、推送等特性。
 
具体内容查看笔记:
ServiceWorker探索
ServiceWorker和cacheStorage缓存及离线开发
利用ServiceWorker进行多页面通信:
// 主页面发送信息
navigator.serviceWorker.controller.postMessage(value);
// 主页面监听消息
navigator.serviceWorker.addEventListener('message',event => {
    // console.log(event.data);
});
// ServiceWorker接收信息(对其他页面消息分发)
self.addEventListener('message',event => {
    let promise = self.clients.matchAll().then(clientList => {
        let senderID = event.source ? event.source : 'unknown';
        clientList.forEach(client => {
            if(client.id === senderID){
                return;
            }
            client.postMessage({
                client: senderID,
                message: event.data
            });
        });
    });
    event.waitUntil(promise);    
});浏览器缓存
Cache-Control

-  max-age  当小于缓存时间时,直接加载本地资源(
from memory cache),expires(到期时间http/1.0)和max-age相比,max-age具有更高的优先级。 -   s-maxage  共享缓存,
public相关的缓存设备例如CDN,优先级高于max-age,如果客户端访问到的是CDN服务器缓存中的数据切未更改则返回304状态码。(Cache-control:max-age=3600, s-maxage=31536000,就算在max-age时间内,也不直接加载本地文件,而是访问CDN缓存。缓存中的文件如果没有更改,则直接通知客户端304,加载本地文件。感觉和no-cache很像呀) -   no-cache  (例如:
Cache-Control:private, max-age=0, no-cache),不是不缓存的意思,它实际上的机制是,仍然对资源使用缓存,但每一次在使用缓存之前必须(MUST)向服务器对缓存资源进行验证。 - no-store 对该文件不适用任何缓存策略。
 - public 资源将被客户端和代理服务器缓存。
 - private 资源仅被客户端缓存,代理服务器不缓存。
 - public VS private 要知道从服务器到浏览器之间并非只有浏览器能够对资源进行缓存,服务器的返回可能会经过一些中间(intermediate)服务器甚至甚至专业的中间缓存服务器,还有CDN。而有些请求返回是用户级别、是私人的,所以你可能不希望这些中间服务器缓存返回。此时你需要将Cache-Control设置为private以避免暴露。
 
Expires
Expires是http/1.0中定义的浏览器缓存策略。(expires: Wed, 24 Jan 2018 12:19:34 GMT)
- 用来指定资源到期的时间,是服务器端的具体的时间点。
 - 告诉浏览器在过期时间前可以直接从缓存取数据,而无需再次请求。
 
Last-Modified/If-Modified-Since
- 基于客户端和服务端协商的缓存机制
 - last-modified —— response header
 - if-modified-since —— request header
 - 需要与cache-control共同使用
 
但是last-modified是有缺点的。
- 1.某些服务器
不能获取精确的修改时间 - 文件修改时间改了,单文件内容却没有变
 
Etag/If-None-Match
 此缓存策略优先级高于Last-Modified/If-Modified-Since 
- 文件内容的hash值
 - etag —— response header
 - if-none-Match —— request header
 - 需要与cache-controlgongt使用
 
总体缓存流程图

服务器端性能
vue渲染遇到的问题
vue执行过程:
下载vue.js ==>  执行vue.js代码 ==> 生成HTML页面
随着前端浏览器的性能的提升,大量的运算在前端执行。
使用vue框架出现了首屏性能、渲染问题。
优化方案?
- 构建层模板编译(在构建层做模板编译工作,将模板语法编译成在
vue的runtime中可以直接执行的js代码) - 数据无关的
prerender的方式(将vue渲染完成的静态页面返回) - 服务端渲染