性能

80 阅读6分钟

网络相关

DNS预解析 Prefetch
DNS 解析也是需要时间的,可以通过预解析的方式来预先获得域名所对应的 IP。
<link rel="dns-prefetch" href="//yuchengkai.cn" />

缓存
通常浏览器缓存策略分为两种:强缓存和协商缓存。

强缓存表示在缓存期间不需要请求,可以通过两种响应头实现:
    Expires
        Expires: Wed, 22 Oct 2018 08:41:00 GMT
        ExpiresHTTP/1.0的产物,表示资源会在 Wed, 22 Oct 2018 08:41:00 GMT 后过期,且Expires受限于本地时间,如果修改了本地时间,可能会造成缓存失效。
    Cache-Control
        Cache-control: max-age=30
        Cache-Control 出现于HTTP/1.1,优先级高于Expires。该属性表示资源会在 30 秒后过期,需要再次请求。

如果缓存过期了,可以使用协商缓存来解决问题,需要客户端和服务端共同实现。协商缓存需要请求,如果缓存有效会返回 304。
两种实现方式:
    Last-ModifiedIf-Modified-Since
        Last-Modified表示本地文件最后修改日期,If-Modified-Since会将Last-Modified的值发送给服务器,询问服务器在该日期后资源是否更新,有更新就将新的资源发送回来,但是如果在本地打开缓存文件,就会造成 Last-Modified 被修改。
    ETagIf-None-Match
        ETag类似于文件指纹If-None-Match会将当前ETag发送给服务器,询问该资源ETag是否变动,有变动就将新的资源发送回来。并且ETag优先级比Last-Modified高。

选择合适的缓存策略,大部分的场景都可以使用强缓存配合协商缓存解决,但是在一些特殊的地方可能需要选择特殊的缓存策略:
    不需要缓存的资源,可以使用 Cache-control: no-store ,表示该资源不需要缓存
    频繁变动的资源,可以使用 Cache-Control: no-cache 并配合 ETag 使用,表示该资源已被缓存,但是每次都会发送请求询问资源是否更新
    对于代码文件,通常使用 Cache-Control: max-age=31536000 并配合策略缓存使用,然后对文件进行指纹处理,一旦文件名变动就会立刻下载新的文件。




使用 HTTP / 2.0
浏览器会有并发请求限制,在HTTP/1.1 时代,每个请求都需要建立和断开,消耗了好几个RTT 时间,并且由于TCP慢启动,加载体积大的文件会需要更多的时间。在HTTP/2.0 中引入了多路复用,能够让多个请求使用同一个 TCP链接,加快网页的加载速度。并且还支持Header压缩,减少请求的数据大小。



预加载
有些资源不需要马上用到,但是希望尽早获取,强制浏览器请求资源,并且不会阻塞 onload 事件。
<link rel="preload" href="http://example.com" />
预加载可以一定程度上降低首屏的加载时间,因为可以将一些不影响首屏但重要的文件延后加载,唯一缺点就是兼容性不好。


预渲染
通过预渲染将下载的文件预先在后台渲染。
<link rel="prerender" href="http://example.com" />
预渲染虽然可以提高页面的加载速度,但是要确保该页面百分百会被用户在之后打开,否则就白白浪费资源去渲染。

懒执行
懒执行就是将某些逻辑延迟到使用时再计算。可以用于首屏优化,对于某些耗时逻辑并不需要在首屏就使用的,就可以使用懒执行。懒执行需要唤醒,可以通过定时器或者事件的调用来唤醒。

懒加载
懒加载的原理就是只加载自定义区域(通常是可视区域,但也可以是即将进入可视区域)内需要加载的东西。对于图片,先设置图片标签的 src 属性为一张占位图,将真实的图片资源放入一个自定义属性中,当进入自定义区域时,就将自定义属性替换为 src 属性,这样图片就会去下载资源,实现了图片懒加载。不仅可以用于图片,也可以使用在别的资源上。比如进入可视区域才开始播放视频等等。



文件优化
    图片优化
        减少像素点
        减少每个像素点能够显示的颜色
    图片加载优化
        修饰图片使用css代替
        一般图片使用CDN加载
        小图使用base64格式
        雪碧图
        正确使用图片格式和大小
    其他文件优化
        CSS放入head中
        服务端开启文件压缩功能
        script放到body底部或者放到任何位置加上defer,表示在HTML解析完成后并行下载顺序执行,对于没有任何依赖的JS文件加上async,表示加载和渲染元素的过程将和JS文件的加载执行并行无序进行。
        使用Webworker,另开线程执行脚本(不可有DOM操作)。


CDN
静态资源尽量使用CDN加载。由于浏览器对于单个域名有并发请求上限,可以考虑使用多个CDN域名。对于CDN加载静态资源需要注意CDN域名要与主站不同,否则每次请求都会带上主站的Cookie。


使用Webpack优化项目
    对于 Webpack4,打包项目使用 production 模式,这样会自动开启代码压缩
    使用 ES6 模块来开启 tree shaking,这个技术可以移除没有使用的代码
    优化图片,对于小图可以使用 base64 的方式写入文件中
    按照路由拆分代码,实现按需加载
    给打包出来的文件名添加哈希,实现浏览器缓存文件



监控
对于代码运行错误,通常的办法是使用 window.onerror 拦截报错。该方法能拦截到大部分的详细报错信息,但是也有例外:
    对于跨域的代码运行错误会显示 Script error. 对于这种情况我们需要给 script 标签添加 crossorigin 属性
    对于某些浏览器可能不会显示调用栈信息,这种情况可以通过 arguments.callee.caller 来做栈递归
    对于异步代码来说,可以使用 catch 的方式捕获错误。比如 Promise 可以直接使用 catch 函数,async await 可以使用 try catch
    线上运行的代码都是压缩过的,需要在打包时生成 sourceMap 文件便于 debug。
    对于捕获的错误需要上传给服务器,通常可以通过 img 标签的 src 发起一个请求。


如何渲染几万条数据并不卡住界面
    后端分页
    如果不分页,使用定时器时间是1000/60,直接给dom增加,因为大多数屏幕渲染时间间隔是每秒60帧。
    如果不分页,使用定时器0秒,循环渲染一部分,使用var rafId = window.requestAnimationFrame(add),不过要记得使用cancelAnimationFrame(rafId)去清除。
        requestAnimationFrame 比起 setTimeoutsetInterval的优势主要有两点:
            requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧。
            隐藏或不可见的元素,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的的cpu,gpu和内存使用量。