页面性能优化

一、页面加载过程

  • 加载

    1. DNS解析:域名 -> IP地址;
    2. 取得目的主机 MAC 地址;
    3. 浏览器与目的IP地址建立TCP连接,发起Http请求;
    4. 服务器处理请求,返回资源。
  • 渲染

    1. 解析 HTML 代码构建 DOM 树,解析 CSS 代码构建 CSSOM 规则树;
    2. 根据 DOM 和 CSSOM 构建 Render Tree ;
    3. 利用 Render Tree 对页面进行布局和绘制;
    4. 在解析 HTML 代码时,遇到<script> 浏览器会先下载和构建CSSOM,然后执行脚本,最后才是继续构建DOM。

二、优化方向与方法

1、总体方向:

  • 加快加载速度:

    压缩代码,减少体积;

    减少请求数;

    使用 CDN 加速;

    使用 SSR (服务器端渲染)。

  • 加快渲染速度:

    CSS放在 head 标签内,JS放在 body 最后;*

    尽早开始执行JS,用 DOMContentLoaded 触发;**

    懒加载;

    缓存 DOM 查询操作,将对 DOM 的操作合并,最后再操作改变 DOM;

    节流(throttle)、防抖(debounce)。

* 前者是为了防止渲染过程中Render Tree发生改变重新渲染;后者是为了让页面尽快呈现出来,减少用户等待时间;

** window.onload:等页面全部资源加载完才会执行,包括视频、图片;

​ DOMContentLoaded: DOM渲染完成后就能执行,此时图片、视频等可能为加载完成4。

2、实现方法:

  1. SSR : 可以使用 Nuxt.js 做 SSR

  2. 懒加载:

    方法一:

    <img id="img" src="preview.png" data-realsrc="abc.png">
    <script>
      var num = document.getElementsByTagName('img').length;
        var img = document.getElementsByTagName("img");
        var n = 0; //存储图片加载到的位置,避免每次都从第一张图片开始遍历
        lazyload(); //页面载入完毕加载可是区域内的图片
        window.onscroll = lazyload;
        function lazyload() { //监听页面滚动事件
            var seeHeight = document.documentElement.clientHeight; //可见区域高度
            var scrollTop = document.documentElement.scrollTop || document.body.scrollTop; //滚动条距离顶部高度
            for (var i = n; i < num; i++) {
                if (img[i].offsetTop < seeHeight + scrollTop) {
                    if (img[i].getAttribute("src") == "default.jpg") {
                        img[i].src = img[i].getAttribute("data-src");
                    }
                    n = i + 1;
                }
            }
        }
    </script>

    方法二:https://juejin.im/post/5bbc60e8f265da0af609cd04

  3. 节流(throttle):规定一个单位时间,在这个单位时间内,只能有一次触发事件的回调函数执行,如果在同一个单位时间内某事件被触发多次,只有一次能生效。节流可以使用在 scroll 函数的事件监听上,通过事件节流来降低事件调用的频率。

    // 函数节流的实现;
    function throttle(fn, delay) {
      var preTime = Date.now();
      return function () {
        var context = this,
          args = arguments,
          nowTime = Date.now();
        // 如果两次时间间隔超过了指定时间,则执行函数。
        if (nowTime - preTime >= delay) {
          preTime = Date.now();
          return fn.apply(context, args);
        }
      };
    }
    // 业务实例:拖拽一个元素,要随时拿到该元素被拖拽的位置。
    // 直接用drag事件,则会频发的触发,很容易导致卡顿。
    // 而节流则是无论比拖拽多快,都会每隔一个固定事件触发一次。
    const div1 = document.getElementById('div1')
    div1.addEventListener('drag', throttle( function (e) {
      console.log(e.offsetX, e.offsetY)
    }, 200))
  4. 防抖(debounce):在事件被触发 n 秒后再执行回调,如果在这 n 秒内事件又被触发,则重新计时。这可以使用在一些点击请求的事件上,避免因为用户的多次点击向后端发送多次请求。

    // 函数防抖的实现
    function debounce(fn, wait) {
      var timer = null;
      return function () {
        var context = this,
          args = arguments;
        // 如果此时存在定时器的话,则取消之前的定时器重新记时
        if (timer) {
          clearTimeout(timer);
          timer = null;
        }
        // 设置定时器,使事件间隔指定事件后执行
        timer = setTimeout(() => {
          fn.apply(context, args);
        }, wait);
      };
    }
    // 业务实例:监听一个输入框的文字变化后,触发change事件。
    // 直接用keyup事件,会频繁的触发change事件。
    // 而防抖则是用户输入结束或者暂停时,才会触发change事件。
    const input1 = document.getElementById('input1')
    input1.addEventListener('keyup', debounce(() => {
      console.log(input1.value)
    }))

三、补充

  1. DNS 预解析:使浏览器主动去执行域名解析。

    <link rel="dns-prefetch" href="//example.com">
  2. HTTP预连接:

    解释href的属性值,如果是合法的URL,然后继续判断URL的协议是否是http或者https否则就结束处理。

    如果当前页面host不同于href属性中的host,crossorigin其实被设置为anonymous(就是不带cookie了),如果希望带上cookie等信息可以加上crossorign属性,corssorign就等同于设置为use-credentials

    <link rel="preconnect" href="//example.com">
    <link rel="preconnect" href="//cdn.example.com" crossorigin>
  3. 预加载资源:让浏览器预加载一个资源(HTML,JS,CSS或者图片等),但页面是不会解析或者JS是不会直接执行的。

    <link rel="prefetch" href="//example.com/next-page.html" as="html" crossorigin="use-credentials">
    <link rel="prefetch" href="/library.js" as="script">
  4. 预渲染:不仅会加载资源,还会解执行页面,进行预渲染,但是这都是根据浏览器自身进行判断。

    • 分配少量资源对页面进行预渲染。
    • 挂起部分请求直至页面可见时。
    • 可能会放弃预渲染,如果消耗资源过多。
    <link rel="prerender" href="//example.com/next-page.html">
  5. pr属性:

    dns-prefetch,preconnect,prefetch和prerender都支持一个pr属性(0.0到1.0范围的值),让浏览器能够判断优先加载哪些资源。浏览器内部是有可用的连接池的,资源紧张的情况下只能加载优先级更高的资源。

  6. 浏览器缓存:链接

参考:

https://developer.mozilla.org/zh-CN/docs/Web/HTML/CORS_settings_attributes

https://segmentfault.com/a/1190000011065339

https://github.com/joshbuchea/HEAD


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

使用图床为md插入图片 Previous
Hexo搭建 Next