公告

👇👇👇扫我

图片
Skip to content

分时函数

requestIdleCallback 浏览器空闲时执行

html
<div class="w_680 d_f jc_sb">
    <div class="d_f fd_c ai_c">
        <button onclick="handleInsert()">插入1万个元素(原始写法)</button>

        <div id="idOld"></div>
    </div>

    <div class="d_f fd_c ai_c">
        <button onclick="handleOptimize()">插入1万个元素(优化后的写法)</button>

        <div id="idOptimize"></div>
    </div>
</div>

<script>
    function handleInsert() {
        console.time();
        let idOld = document.querySelector("#idOld"),
                datas = Array.from({ length: 100000 }, (_, i) => i + 1);

        for (const item of datas) {
            const div = document.createElement("div");

            div.textContent = item;
            idOld.appendChild(div);
        }
        console.timeEnd();
    }


    // 优化方式一(参数归一化)
    function handleOptimize() {
        console.time();
        let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
                idOptimize = document.querySelector("#idOptimize"),
                taskHandler = (item, i) => {
                    const div = document.createElement("div");

                    div.textContent = item;
                    idOptimize.appendChild(div);
                };

        performChunk(datas, taskHandler);
        console.timeEnd();
        // performChunk(100000, taskHandler);
    }

    function performChunk(datas, taskHandler) {
        // 参数归一化
        if (typeof datas === "number") datas = Array.from({ length: datas }, (_, i) => i + 1);
        if (datas.length === 0) return false;

        let i = 0;

        function _run() {
            if (i >= datas.length) return false;

            requestIdleCallback((idle) => {
                while (idle.timeRemaining() > 0 && i < datas.length) {
                    taskHandler(datas[i], i);

                    i++;
                }

                _run();
            });
        }

        _run();
    }


    // 优化方式二(当浏览器不支持requestIdleCallback方法的时候)
    function handleOptimize1() {
        console.time();
        let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
                idOptimize = document.querySelector("#idOptimize"),
                taskHandler = (item, i) => {
                    const div = document.createElement("div");

                    div.textContent = item;
                    idOptimize.appendChild(div);
                },
                scheduler = (task) => {
                    setTimeout(() => {
                        const start = Date.now();
                        task(() => Date.now() - start < 50);
                    }, 100);
                };

        performChunk1(datas, taskHandler, scheduler);
        console.timeEnd();
    }

    function performChunk1(datas, taskHandler, scheduler) {
        if (typeof datas === "number") datas = Array.from({ length: datas }, (_, i) => i + 1);
        if (datas.length === 0) return false;

        let i = 0;

        function _run() {
            if (i >= datas.length) return false;

            scheduler((isGoOn) => {
                while (isGoOn() > 0 && i < datas.length) {
                    taskHandler(datas[i], i);

                    i++;
                }

                _run();
            });
        }

        _run();
    }

    // 优化方式三(判断环境)
    function handleOptimize2() {
        console.time();
        let datas = Array.from({ length: 100000 }, (_, i) => i + 1),
                idOptimize = document.querySelector("#idOptimize"),
                taskHandler = (item, i) => {
                    const div = document.createElement("div");

                    div.textContent = item;
                    idOptimize.appendChild(div);
                };

        browserPerformChunk2(datas, taskHandler);
        console.timeEnd();
    }

    function browserPerformChunk2(datas, taskHandler) {
        const scheduler = (task) => {
            requestIdleCallback((idle) => {
                task(() => idle.timeRemaining() > 0);
            });
        };

        performChunk2(datas, taskHandler, scheduler);

    }

    function performChunk2(datas, taskHandler, scheduler) {
        if (typeof datas === "number") datas = Array.from({ length: datas }, (_, i) => i + 1);
        if (datas.length === 0) return false;

        let i = 0;

        function _run() {
            if (i >= datas.length) return false;

            scheduler((isGoOn) => {
                while (isGoOn() > 0 && i < datas.length) {
                    taskHandler(datas[i], i);

                    i++;
                }

                _run();
            });
        }

        _run();
    }

</script>

分片

(可以通过 DocumentFragment 的使用,减少 DOM 操作次数,降低回流对性能的影响。并且可以通过 requestAniminationFrame 保证插入新节点操作在页面重绘前执行,二者结合可以实现数据渲染优化。)

关于requestAnimationFrame

  1. requestAnimationFrame会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率。

  2. 在隐藏或不可见的元素中,requestAnimationFrame将不会进行重绘或回流,这当然就意味着更少的CPU、GPU和内存使用量。

  3. requestAnimationFrame是由浏览器专门为动画提供的API,在运行时浏览器会自动优化方法的调用,并且如果页面不是激活状态下的话,动画会自动暂停,有效节省了CPU开销

第一种

html
<ul id="list-with-big-data">1000个好友</ul>

<script>
  // 全局变量,先扔这儿
  let [ary, ul] = [[], document.querySelector("#list-with-big-data")];
  // 生成好友数据,暂拟1000个
  for (i = 1; i < 1000; i++) {
    ary.push(i);
  }

  // step1.
  console.time();

  function renderList() {
    for (let i = 0; i < ary.length; i++) {
      let li = document.createElement("li");
      li.innerHTML = ary[i];
      ul.appendChild(li);
    }
  }

  renderList();

  console.timeEnd();

第二种

javascript

  console.time();

  function renderData(fn, count) {
    // renderData方法接受两个参数fn和count,fn为创建节点逻辑方法,count参数表示每批创建节点数量。
    // 每次遍历count个数据,传入节点创建函数,同时删除此数据。
    let dataChunk = () => {
      for (i = 0; i < (Math.min(ary.length, count || 0)); i++) {
        let item = ary.shift();
        // 将每条数据作为参数传入节点创建函数中。
        fn(item);
      }
    };
    // 此处计时器设为10毫秒,当然也改为参数传入。
    return () => {
      let timeOut = setInterval(function(params) {
        // dataChunk方法遍历数据时,逐一将数据删除,当数据为空时,结束计时器。
        if (ary.length === 0) {
          return clearInterval(timeOut);
        }
        dataChunk();
      }, 10);
    };
  }

  const renderList1 = renderData((n) => {
    // 再简单不过的创建节点并插入的过程。 默认第二个参数即count为8,表一次渲染8条数据。
    let li = document.createElement("li");
    li.innerHTML = n;
    ul.appendChild(li);
  }, 8);
  renderList1();

  console.timeEnd();

第三种

javascript
  console.time();
  function renderList2(count = 8) {
    let dataChunk = function() {
      // 创建fragment,fragment详细可移步度娘。 vue双向绑定的实现大多也有用到。
      let fragment = document.createDocumentFragment();
      for (i = 0; i < (Math.min(ary.length, count || 0)); i++) {
        // 与step2大同小异,将节点创建移步此处,并逐条删除数据。
        let li = document.createElement("li");
        li.innerHTML = ary[0];
        // 私以为就是将动态生成的list节点挂载到fragment生成的虚拟dom上,之后再一并插入到ul底下。
        fragment.appendChild(li);
        ary.shift();
      }
      ;
      ul.appendChild(fragment);
      renderData();
    };
    var renderData = () => {
      if (ary.length > 0) {
        // 此处调用requestAnimationFrame方法,将dataChunk作为参数传入,实现递归,反复的一进一出渲染数据。
        window.requestAnimationFrame(dataChunk);
      }
    };
    renderData();
  }

  renderList2();

  console.timeEnd();
  • setTimeout 延迟加载 (使用不当可能适得其反)
  • web worker 多线程 (不能操作dom,主要计算密集型的任务)
  • requestIdleCallback 帧空闲时运行 (react18带🔥的,低优先级任务)
  • requestAnimationFrame 每帧都会运行 (主要做动画效果调优)

sendBeacon 方法可以接受两个参数:

  1. url:一个字符串,表示要发送数据的目标 URL。

  2. data:一个 ArrayBufferView、Blob、DOMString 或 FormData 对象,包含要发送的数据。

Performance

Performance 是一个与性能相关的API,可以获取到当前页面中与性能相关的信息,它提供了许多用于测量和分析网页性能的关键 API。以下是一些重要的 Performance 接口的 API:

performance.timing: 这个属性提供了一个包含了各种关键时间点的 PerformanceTiming 对象,可以用来测量网页加载的不同阶段,如导航开始、资源加载、DOM 解析等。

performance.now() : 这个方法返回一个高精度的时间戳,用于测量性能。它适用于执行时间间隔的测量,比如代码执行时间。

performance.mark() 和 performance.measure() : 这些方法用于在代码中插入标记点,以便测量不同代码块之间的性能差异。可以使用 performance.mark() 创建标记,然后使用 performance.measure() 来测量两个标记之间的时间间隔。

performance.getEntries() 和 performance.getEntriesByType() : 这些方法用于获取各种性能条目(如资源、导航等)的列表。您可以使用这些方法来分析特定类型的性能数据。

performance.navigation: 这个属性提供了导航类型的信息,帮助您确定是新导航还是页面刷新。

performance.memory: 这个属性提供了有关内存使用情况的信息。它返回一个 PerformanceMemory 对象,包含了内存使用的统计数据。

Performance.timing Performance.timing对象包含了延迟相关的性能信息,window.performance.timing 对象提供了许多属性,用于访问有关网页加载和性能的时间信息。以下是这些属性的列表:

navigationStart: 导航开始的时间戳,即浏览器开始获取当前文档的时间。

unloadEventStart: 前一个文档的 unload 事件开始时间。

unloadEventEnd: 前一个文档的 unload 事件结束时间。

redirectStart: 重定向开始的时间戳,即从前一个文档到当前文档的重定向开始时间。

redirectEnd: 重定向结束的时间戳,即从前一个文档到当前文档的重定向结束时间。

fetchStart: 开始获取文档的时间戳,通常是发起请求的时间。

domainLookupStart: 域名解析开始的时间戳,即浏览器开始解析域名的时间。

domainLookupEnd: 域名解析结束的时间戳,即浏览器完成域名解析的时间。

connectStart: 开始建立连接的时间戳,即浏览器开始建立网络连接的时间。

connectEnd: 完成网络连接的时间戳,即浏览器完成网络连接的时间。

secureConnectionStart: 安全连接开始的时间戳,用于HTTPS连接。

requestStart: 开始发送请求的时间戳,即浏览器开始向服务器发送请求的时间。

responseStart: 开始接收响应的时间戳,即浏览器开始接收服务器响应的时间。

responseEnd: 响应结束的时间戳,即浏览器完成接收服务器响应的时间。

domLoading: 开始解析文档对象模型(DOM)的时间戳。

domInteractive: DOM 变为可交互的时间戳,即用户可以与页面进行交互的时间。

domContentLoadedEventStart: DOMContentLoaded 事件开始的时间戳。

domContentLoadedEventEnd: DOMContentLoaded 事件结束的时间戳。

domComplete: DOM 解析完成的时间戳。

loadEventStart: 加载事件开始的时间戳,即文档加载事件开始的时间。

loadEventEnd: 加载事件结束的时间戳,即文档加载事件结束的时间。

通过这些属性,我们了解页面加载和渲染的各个阶段,从而优化页面性能和用户体验。而这些属性值都是时间戳,以毫秒为单位。

performance.getEntries() Performance.getEntries(FilterOptions),对于给定的filter,此方法返回 PerformanceEntry对象数组,PerformanceEntry对象代表了 performance 时间列表中的单个 metric 数据,metric(MaiTreeKe)可以理解为基本单元,一条记录。手动构建 mark或者measure生成Performance entries, 在资源加载的时候,也会被动生成(例如图片、script、css等资源加载)

通过 performance.getEntries() 获取各个资源请求的 PerformanceEntry 对象,统计耗时 performanceEntries 是指浏览器性能API中的 PerformanceEntry 对象数组。这个数组包含了在页面生命周期中收集的不同类型的性能数据,比如资源加载时间、导航信息、用户交互延迟等等。

这些数据是通过 Performance API 提供的接口,如 performance.getEntries() 或 performance.getEntriesByType(type) 来获取的。在这个返回的数组中,每个 PerformanceEntry 对象都代表了特定的性能指标或事件。不同类型的性能指标(如资源加载、用户交互等)会有不同的属性。

常见的 PerformanceEntry 对象属性包括:

name: 标识资源或事件的名称。

entryType: 表示性能条目的类型,如 "resource"、"navigation" 等。

startTime: 开始时间,表示性能事件的开始时间。

duration: 持续时间,表示性能事件的时长。

initiatorType: 发起者类型,用于资源加载事件,如 "script"、"img" 等。

其他类型特定的属性,如 decodedBodySize、transferSize 等,用于描述资源加载事件。

通过遍历 performanceEntries 数组,可以获取页面中不同类型的性能数据,进而进行分析、优化和监控。这对于了解页面性能并针对性地进行改进非常有帮助。

javascript
const ptiming = performance.timing
  // 白屏时间
  result.blankTime = fix(ptiming.responseStart - ptiming.navigationStart)
  // 浏览器发起请求的时间
  result.fStartTime = fix(ptiming.fetchStart - ptiming.navigationStart)
  // connect开始
  result.cStartTime = fix(ptiming.connectStart - ptiming.navigationStart)
  // connect结束
  result.cEndTime = fix(ptiming.connectEnd - ptiming.navigationStart)
  // dns开始时间
  result.dnsStartTime = fix(ptiming.domainLookupStart - ptiming.navigationStart)
  // dns结束时间
  result.dnsEndTime = fix(ptiming.domainLookupEnd - ptiming.navigationStart)
阅读量: 0
评论量: 0