eb performance

eb performance test

// ==UserScript==
// @name         eb performance
// @namespace    http://tampermonkey.net/
// @version      0.1.6
// @description  eb performance test
// @author       timluo465
// @match        http://*/*
// @icon         data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant        none
// @license      MIT
// ==/UserScript==

const defaultStyle = "font-size:14px";
const focalStyle = "font-weight:700;font-size:16px";

(function () {
  "use strict";
  console.log("开始初始化eb性能分析插件");
  const recordFuns = [];
  const moduleObj = {};

  const createInterval = function (callback) {
    const interval = setInterval(function () {
      const res = callback();

      if (res) {
        clearInterval(interval);
      }
    }, 20);
  };

  createInterval(function () {
    if (window.mobx) {
      initMobx();
    }
    return !!window.mobx;
  });

  createInterval(function () {
    if (window.React) {
      initReact();
    }
    return !!window.React;
  });

  const createPerformanceTimer = function (fn, opts) {
    return function (...args) {
      const { fnName = "" } = opts || {};
      const now = performance.now();
      const res = fn(...args);
      const last = performance.now();
      const time = (last - now).toString();
      const spentTime = time.substring(0, time.indexOf(".") + 3);

      if (spentTime > 5) {
        const consoleFun = spentTime > 50 ? console.error : console.warn;
        recordFuns.push({ func: this, time: spentTime });

        consoleFun(
          `%c${fnName}%c 耗时 %c${spentTime} ms%c, 参数为:%o \n${
            res ? `返回结果:%o` : "无返回结果"
          }`,
          focalStyle,
          defaultStyle,
          focalStyle,
          defaultStyle,
          args,
          res
        );
      }

      return res;
    };
  };

  const call = Function.prototype.call;

  Function.prototype.call = function () {
    const now = performance.now();
    const res = call.apply(this, arguments);
    const last = performance.now();
    const time = (last - now).toString();
    const spentTime = time.substring(0, time.indexOf(".") + 3);

    if (spentTime > 5) {
      const module = getModule();

      if (module) {
        const moduleName = `@weapp/${module}`;
        const consoleFun = spentTime > 50 ? console.error : console.warn;
        recordFuns.push({ func: this, time: spentTime });

        if (module && moduleObj[moduleName]) {
          const { all, serious } = moduleObj[moduleName];

          moduleObj[moduleName] = {
            ...moduleObj[moduleName],
            all: all + 1,
          };

          if(spentTime > 50){
            moduleObj[moduleName].serious = serious + 1;
          }
        } else {
          moduleObj[moduleName] = {
            moduleName: moduleName,
            all: 1,
            serious: 1,
          };
        }
        consoleFun(
          `%c未知方法执行耗时 %c${spentTime} ms%c,来源模块:%c@weapp/${module}%c \n点击定位方法:%o \n${
            res ? `返回结果:%o` : "无返回结果"
          }`,
          defaultStyle,
          focalStyle,
          defaultStyle,
          focalStyle,
          defaultStyle,
          this,
          res
        );
      }
    }
    return res;
  };

  function getModule(isMobx) {
    //创建一个Error来获取Error所在的位置及其堆栈跟踪
    const e = new Error();
    const regex = /\((.*):(\d+):(\d+)\)$/;

    if (!isMobx) {
      const stak = e.stack.split("\n");
      const match = regex.exec(stak[stak?.length - 1]);
      const module = match?.[1].match(/(?<=build\/).*?(?=\/static)/)?.[0] || "";

      return module;
    }
  }

  function initMobx() {
    const mobx = window.mobx;

    ["toJS", "computed", "runInAction"].forEach(function (act) {
      const originalFn = mobx[act];

      mobx[act] = createPerformanceTimer(originalFn, {
        fnName: `mobx.${act}`,
      });

      Object.keys(originalFn).forEach(function (key) {
        mobx[act][key] = originalFn[key];
      });
    });

    Object.keys(mobx.observable).forEach(function (action) {
      const originalFn = mobx.observable[action];

      mobx.observable[action] = createPerformanceTimer(originalFn, {
        fnName: `mobx.observable.${action}`,
      });
    });

    console.log("Mobx性能分析插件初始化完成!");
  }

  function initReact() {
    const React = window.React;

    [
      "useEffect",
      "useCallback",
      "useState",
      "useMemo",
      "useRef",
      "memo",
    ].forEach(function (hook) {
      const originalHook = React[hook];

      React[hook] = createPerformanceTimer(originalHook, {
        fnName: `React.${hook}`,
      });
    });

    console.log("React Hooks性能分析插件初始化完成!");
  }

  function print() {
    const printObj = [];
    const moduleArrays = [];
    Object.keys(moduleObj).forEach(function (key) {
      moduleArrays.push(moduleObj[key]);
    });
    const sortModuleArrays = moduleArrays.sort((a, b) => {
      return b.all - a.all;
    });

    sortModuleArrays.forEach((array) => {
      printObj.push({
        前端模块: array.moduleName,
        "问题数(所有)": array.all,
        "问题数(耗时大于50ms)": array.serious,
      });
    });

    console.table(printObj);
  }

  function getRecords() {
    const funs = [];
    const beyondThresholdFuns = [];
    const threshold = 50;
    let times = 0;

    recordFuns.forEach((recordFun) => {
      funs.push(recordFun);

      if (recordFun.time > threshold) {
        beyondThresholdFuns.push(recordFun);
        times++;
      }
    });

    console.log(
      `截至当前已发现性能问题点共计${recordFuns.length}个,大于${threshold}ms的问题共计${times}个 \n所有记录:%o\n大于${threshold}ms的记录:%o`,
      funs,
      beyondThresholdFuns
    );

    return;
  }
  window.performanceTools = { print, getRecords };
})();