Считая время на первом экране страницы, многие ошибаются на первом шаге.

JavaScript оптимизация производительности

предисловие

Производительность страницы внешнего интерфейса играет важную роль в удержании пользователей и интуитивном интерфейсе. В этом случае очень важным становится вопрос о том, как лучше отслеживать производительность страницы интерфейса. Мониторинг производительности интерфейсных страниц в основном делится на два способа:

называетсяСинтетический мониторинг Synthetic Monitoring, SYN. В смоделированном сценарии отправьте страницу, требующую аудита производительности, запустите страницу с помощью ряда инструментов и правил, извлеките некоторые индикаторы производительности и получите отчет об аудите. Одной из последних тенденций синтетического мониторинга являетсяGoogleизLighthouse

Другойреальный мониторинг пользователейReal User Monitoring,RUM. Отслеживайте реальные данные о доступе пользователей, сообщайте данные на сервер, а затем обрабатывайте данные для получения окончательных данных о производительности.

Очень важным показателем в мониторинге производительности интерфейса является время первого экрана, потому что первое время экрана напрямую отражает, как долго пользователи могут видеть основной контент страницы, что определяет пользовательский опыт. В этом случае для нас становится очень важным, как получить точное время первого экрана. В этой статье, опираясь на предыдущую практику, мы поговорим о том, как рассчитать первое экранное время.

Performance

При применении SSR (рендеринга на стороне сервера) мы думаем, чтоhtmlизbodyВремя завершения рендеринга — это время над сгибом. Обычно мы используем стандарт W3CPerformanceобъект для расчета времени выше сгиба.

PerformanceЧасто используется для сбора данных о производительности, поскольку объект имеет встроенные параметры производительности практически для всех распространенных интерфейсов.

image

PerformanceСодержит четыре свойства:memory,navigation,timeOrigin,timingИ обработчик событийonresourcetimingbufferfull. Ниже мы кратко представимPerformanceизapi.

memory

memoryЭто свойство предоставляет объект, который может получить базовое использование памяти.MemoryInfo

performance.memory = {
  jsHeapSizeLimit, // 内存大小限制,单位是字节B
  totalJSHeapSize, // 可使用的内存大小,单位是字节B
  usedJSHeapSize   // JS对象占用的内存大小,单位是字节B
}

navigation

вернутьPerformanceNavigationОбъект, который предоставляет информацию об операциях, которые произошли в течение указанного периода времени, в том числе о том, была ли страница загружена или обновлена, сколько произошло перенаправлений и т. д.

performance.navigation = {
  redirectCount: '',
  type: ''
}

timeOrigin

Возвращает высокоточную метку времени начала измерения производительности.

timing

вернутьPerformanceTimingОбъект, который содержит различные данные, связанные с производительностью браузера, обеспечивает время, затрачиваемое браузером на каждый этап обработки страницы. Ниже приведены часто используемые расчеты моментов времени.

window.onload = function() {
  var timing  = performance.timing;
  console.log('准备新页面时间耗时: ' + timing.fetchStart - timing.navigationStart);
  console.log('redirect 重定向耗时: ' + timing.redirectEnd  - timing.redirectStart);
  console.log('Appcache 耗时: ' + timing.domainLookupStart  - timing.fetchStart);
  console.log('unload 前文档耗时: ' + timing.unloadEventEnd - timing.unloadEventStart);
  console.log('DNS 查询耗时: ' + timing.domainLookupEnd - timing.domainLookupStart);
  console.log('TCP连接耗时: ' + timing.connectEnd - timing.connectStart);
  console.log('request请求耗时: ' + timing.responseEnd - timing.requestStart);
  console.log('白屏时间: ' + timing.responseStart - timing.navigationStart);
  console.log('请求完毕至DOM加载: ' + timing.domInteractive - timing.responseEnd);
  console.log('解释dom树耗时: ' + timing.domComplete - timing.domInteractive);
  console.log('从开始至load总耗时: ' + timing.loadEventEnd - timing.navigationStart);
}

Благодаря приведенному выше введению мы можем легко получить первое экранное время.

domLoadedTime = timing.domContentLoadedEventStart - timing.navigationStart

FMP

Но сVueа такжеReactКогда преобладает передняя коробка, в результате чегоPerformanceТочно отследить время над сгибом страницы невозможно. потому что страницаbodyпусто, браузер должен сначала загрузитьсяjs, а затем черезjsдля отображения содержимого страницы. Итак, какие данные мы используем для экранного времени?

существуетLighthouseВ , мы можем получить значение FMP, FMP (полное название First Meaningful Paint, переводится как первая эффективная окраска) представляет собой момент времени, когда основное содержимое страницы начинает появляться на экране, что является для нас основным показателем для измерения пользовательского опыта загрузки. мы можем думатьFMPЗначением является время выше сгиба, но браузер неFMPпредоставленные данные. Итак, как мы это вычисляем?

Весь процесс расчета делится на следующие две части: 1. Отслеживайте загрузку элемента, в основном для расчетаDomсчет 2. Рассчитать кривизну счета и вычислить окончательныйFMPстоимость

Инициализировать прослушиватель

initObserver() {
  try {
    if (this.supportTiming()) {
      this.observer = new MutationObserver(() => {
        let time = Date.now() - performance.timing.fetchStart;
        let bodyTarget = document.body;
        if (bodyTarget) {
          let score = 0;
          score += calculateScore(bodyTarget, 1, false);
          SCORE_ITEMS.push({
            score,
            t: time
          });
        } else {
          SCORE_ITEMS.push({
            score: 0,
            t: time
          });
        }
      });
    }

    this.observer.observe(document, {
      childList: true,
      subtree: true
    });

    if (document.readyState === "complete") {
      this.mark = 'readyState';
      this.calFinallScore();
    } else {
      window.addEventListener(
        "load",
        () => {
          this.mark = 'load';
          this.calFinallScore();
        },
        true
      );
      window.addEventListener(
        'beforeunload',
        () => {
          this.mark = 'beforeunload';
          this.calFinallScore();
        },
        true
      )
      const that = this;
      function listenTouchstart() {
        if(Date.now() > 2000) {
          that.calFinallScore();
          this.mark = 'touch';
          window.removeEventListener('touchstart', listenTouchstart, true);
        }
      }
      window.addEventListener(
        'touchstart',
        listenTouchstart,
        true
      )
    }
  } catch (error) {}
}

мы проходимMutationObserverконтролироватьDomизменить, а затем рассчитать текущий моментDomсчет. Можно спросить, еслиDomКаждое изменение отслеживается, будет ли оно потреблять производительность страницы? фактическиMutationObserverПри выполнении обратного вызова он выполняется пакетами, что-то похожееVueДождитесь процесса рендеринга интерфейсной платформы.

Рассчитать балл

function calculateScore(el, tiers, parentScore) {
  try {
    let score = 0;
    const tagName = el.tagName;
    if ("SCRIPT" !== tagName && "STYLE" !== tagName && "META" !== tagName && "HEAD" !== tagName) {
      const childrenLen = el.children ? el.children.length : 0;
      if (childrenLen > 0) for (let childs = el.children, len = childrenLen - 1; len >= 0; len--) {
        score += calculateScore(childs[len], tiers + 1, score > 0);
      }
      if (score <= 0 && !parentScore) {
        if (!(el.getBoundingClientRect && el.getBoundingClientRect().top < WH)) return 0;
      }
      score += 1 + .5 * tiers;
    }
    return score;
  } catch (error) {

  }
}

С помощью приведенного выше кода мы можем получить шаги для расчета оценки.

1. ИзbodyРекурсивный расчет развития элемента 2. Он проверит бесполезное сравнение меток элементовSCRIPTЖдать 3. Если элемент выходит за пределы экрана, считается 0 баллов 4. Элемент первого слоя равен 1 баллу, а элемент второго слоя равен 1 + (количество слоев * 0,5), что равно 1,5 балла и так далее.DomОбщая оценка

Рассчитать FMP

мы проходимMutationObserverПолучил массив, каждый элемент массива каждый разDomИзменить время и счет. Итак, как мы рассчитываем, что мы хотимFMPстоимость?

let fmps = getFmp(SCORE_ITEMS);
let record = null
for (let o = 1; o < fmps.length; o++) {
  if (fmps[o].t >= fmps[o - 1].t) {
    let l = fmps[o].score - fmps[o - 1].score;
    (!record || record.rate <= l) && (record = {
      t: fmps[o].t,
      rate: l
    });
  }
}

С приведенным выше кодом мы получим окончательныйFMPЗначение , является тем, которое изменяется больше всегоDOMРазнообразие.

image

Суммировать

До сих пор мы в основном ввели метод расчета первого экранного времени. Подводя итог в одном предложении, этоSSRиспользоватьDomвремя окончания рендеринга,SPAИспользование проектаFMPвремя.

Превью статьи этого месяца

В соответствии с уведомлением мы опубликуем практику и размышления Zhuanzhuan о многотерминальном SDK, мобильном терминале и другой инфраструктуре и технологиях среднего уровня.Приглашаем всех обратить внимание на общедоступный аккаунт «Da Zhuan Zhuan FE» и надеемся на общение с вами больше .