整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          Web性能測試-頁面彈出窗口中內容加載時間的計算


          什么Web性能很重要

          研究表明,更好的核心網頁指標可以提高用戶互動度和業務指標。例如:

          • 研究表明,當網站達到核心網頁指標閾值時,用戶放棄加載網頁的可能性會降低 24%
          • Largest Contentful Paint (LCP) 每減少 100 毫秒,Farfetch 的網站轉化率就會提高 1.3%
          • 將 Cumulative Layout Shift (CLS) 降低 0.2 后,Yahoo! JAPAN 的每次訪問帶來的網頁瀏覽量提高了 15%,訪問時長延長了 13%,跳出率下降了 1.72 個百分點。
          • Netzwelt 提升核心網頁指標后,廣告收入增加了 18%,網頁瀏覽量增加了 27%
          • 將 CLS 從 1.65 降低至 0,使 redBus 域名在全球的排名大幅提升

          核心 Web 指標

          每項核心 Web 指標代表用戶體驗的一個不同方面,能夠進行實際測量,并且反映出以用戶為中心的關鍵結果的真實體驗。核心 Web 指標的構成指標會隨著時間的推移而發展 。當前針對 2020 年的指標構成側重于用戶體驗的三個方面——加載性能交互性視覺穩定性——并包括以下指標(及各指標相應的閾值):


          • Largest Contentful Paint (LCP) :最大內容繪制,測量加載性能。為了提供良好的用戶體驗,LCP 應在頁面首次開始加載后的2.5 秒內發生。
          • First Input Delay (FID) :首次輸入延遲,測量交互性。為了提供良好的用戶體驗,頁面的 FID 應為100 毫秒或更短。
          • Cumulative Layout Shift (CLS) :累積布局偏移,測量視覺穩定性。為了提供良好的用戶體驗,頁面的 CLS 應保持在 0.1. 或更少。

          本文將主要討論復雜頁面的加載性能—— Largest Contentful Paint (LCP)。

          什么是最大內容繪制 (LCP)?

          以下示例展示了一些熱門網站上出現最大內容繪制的時間點:

          在上方的兩個時間軸中,最大元素隨內容加載而變化。在第一個示例中,新內容被添加進 DOM,并因此使最大元素發生了改變。在第二個示例中,由于布局的改變,先前的最大內容從可視區域中被移除。

          雖然延遲加載的內容通常比頁面上已有的內容更大,但實際情況并非一定如此。接下來的兩個示例顯示了在頁面完全加載之前出現的最大內容繪制。

          在第一個示例中,Instagram 標志加載得相對較早,即使其他內容隨后陸續顯示,但標志始終是最大元素。在 Google 搜索結果頁面示例中,最大元素是一段文本,這段文本在所有圖像或標志完成加載之前就顯示了出來。由于所有單個圖像都小于這段文字,因此這段文字在整個加載過程中始終是最大元素。

          哪些元素在考量范圍內?

          根據當前最大內容繪制 API中的規定,最大內容繪制考量的元素類型為:

          • <img>元素
          • 內嵌在<svg>元素內的<image>元素
          • <video>元素(使用封面圖像)
          • 通過url()函數(而非使用CSS 漸變)加載的帶有背景圖像的元素
          • 包含文本節點或其他行內級文本元素子元素的塊級元素。

          如何確定一個元素的大小?

          • 報告給最大內容繪制的元素大小通常是用戶在可視區域內可見的大小。如果有元素延伸到可視區域之外,或者任何元素被剪裁或包含不可見的溢出,則這些部分不計入元素大小。
          • 對于在原始尺寸之上經過調整的圖像元素,報告給指標的元素大小為可見尺寸或原始尺寸,以尺寸較小者為準。例如,遠小于其原始尺寸的縮小圖像將僅報告圖像的顯示尺寸,而拉伸或放大成更大尺寸的圖像將僅報告圖像的原始尺寸。
          • 對于文本元素,指標僅考量其文本節點的大小(包含所有文本節點的最小矩形)。
          • 對于所有元素,通過 CSS 設置的任何邊距、填充或邊框都不在考量范圍內。

          如何計算復雜頁面的加載性能?

          本文主要討論復雜頁面的加載性能度量,相對于簡單的頁面加載性能,可以直接使用Google Performance,如下圖。

          對于特殊頁面的交互是無法通過使用Performance獲取相關指標數據,如下圖,獲取 PageOffice彈窗插件中的文本內容加載時間。

          本文主要討論這種復雜場景加載性能的計算方式。

          復雜頁面的加載性能計算

          計算的指標同樣參考Largest Contentful Paint (LCP) 指標,具體計算原理為:
          將視頻進行逐幀分析,首先計算最大元素出現的第一幀,同時計算出“開始加載的頁面標識元素”所出現的最后一幀,然后計算出幀數差,最后使用幀差數除以視頻幀率,得出加載時間。

          具體實現邏輯

          步驟一: 將采用將視頻進行逐幀分析,計算最大元素出現的第一幀,再通過“開始加載的頁面標識元素”所出現的最后一幀,如下圖,折線圖為最大元素,最大元素出現的第一幀就是我們要找出的幀;圖中分析按鈕為“開始加載的頁面標識元素”,當點擊按鈕后,按鈕消失前的最后一幀就是我們要找出的幀。

          步驟二:計算出幀數差,“開始加載的頁面標識元素”出現的最后一幀的幀數最大元素出現的第一幀的幀數之差,這就是頁面加載過程的總幀數。

          步驟三:幀數差除以視頻幀率(每一秒包含的幀數),最終計算出加載時間。

          Python實現偽代碼

          # -*- coding: cp936 -*-
          
          import cv2, os, time
          from config.deployment_config import END_IMAGE_PATH, IMAGE_SAVE_PATH, IMAGE_SAVE_AS_PATH, \
              START_IMAGE_PATH
          from lib.ui_image_identification.core.image_identification import ImageIdentification
          from lib.ui_image_identification.uicv import imread
          
          
          class FrameTimeDiff:
          
              def __init__(self, mp4):
                  # 視頻路徑,直接把腳本和視頻放在同一個目錄下最好,也可以指定對應的視頻路徑
                  self.cap = cv2.VideoCapture(mp4)
                  (major_ver, minor_ver, subminor_ver) = (cv2.__version__).split('.')
          
                  if int(major_ver) < 3:
                      self.fps = self.cap.get(cv2.cv.CV_CAP_PROP_FPS)
                  else:
                      self.fps = self.cap.get(cv2.CAP_PROP_FPS)
          
          
              def extract_picture_frame(self):
              		"""
          				提取視頻中的圖片幀
          				"""
                  i = 0
                  while True:
                      ret, frame = self.cap.read()  # ret:True或者False,代表有沒有讀取到圖片;frame:表示截取到一幀的圖片
                      if not ret:
                          break
                      # 展示圖片
                      cv2.imshow('capture', frame)
                      image_path = f".\image\\{i}.png"
                      # 保存圖片
                      cv2.imwrite(image_path, frame)
                      i = i + 1
                      if cv2.waitKey(1) & 0xFF == ord('q'):
                          break
          
              @staticmethod
              def merge_number(num_lst):
                  """
          				合并臨近數,方便計算“開始加載的頁面標識元素”出現的最后一幀的幀數 與 最大元素出現的第一幀的幀數
                  eg: [1, 3,4,5,6, 9,10] -> [[1], [3, 4, 5, 6], [9, 10]]
                  """
                  num_lst_tmp = [int(n) for n in num_lst]
                  sort_lst = sorted(num_lst_tmp)  # ascending
                  len_lst = len(sort_lst)
                  i = 0
                  split_lst = []
          
                  tmp_lst = [sort_lst[i]]
                  while True:
                      if i + 1 == len_lst:
                          break
                      next_n = sort_lst[i + 1]
                      if sort_lst[i] + 2 == next_n:
                          tmp_lst.append(next_n)
                      else:
                          split_lst.append(tmp_lst)
                          tmp_lst = [next_n]
                      i += 1
                  split_lst.append(tmp_lst)
                  return split_lst
          
              @staticmethod
              def extract_key_frame(target_pos, _filter_image_list=None, image_save_path=IMAGE_SAVE_PATH):
              		圖片幀檢測標識圖,如最大元素截圖
                  __list = []
                  png_list = {
                      int(os.path.splitext(yml)[0]): os.path.join(image_save_path, yml)
                      for root, dirs, files in os.walk(image_save_path)
                      for yml in files
                      if os.path.splitext(yml)[-1] == ".png"
                  }
                  if _filter_image_list is None:
                      _filter_image_list = []
                  for index in sorted(png_list):
                      if int(index % 2) == 0:
                          file_path = png_list[index]
                          print(file_path)
                          # 圖像識別定位
                          if index not in _filter_image_list:
                          		# 在當前圖片幀中檢測是否存在標識圖片
                              last_scroll_location, last_scroll_match_result, last_scroll_best_match_detail = \
                                  ImageIdentification(
                                      target_pos,
                                      threshold=0.99,
                                      resolution=(1920, 1080)
                                  ).match_in(imread(file_path))
          
                              if last_scroll_location is not None:
                                  print(file_path, last_scroll_location)
                                  image_path = os.path.join(IMAGE_SAVE_AS_PATH, f"{index}.png")
                                  frame = imread(file_path)
           												# 保存圖片
                                  cv2.imwrite(image_path, frame) 
                                  __list.append(index)
                                  _filter_image_list.append(index)
          
                  return __list, _filter_image_list
          
          
              def analysis_more_picture_frame(self):
          				"""
          				計算多次重復頁面操作的加載時間
          				"""
                  find_1_list, filter_image_list = FrameTimeDiff.extract_key_frame(target_pos=START_IMAGE_PATH)
          
                  find_2_list, filter_image_list = FrameTimeDiff.extract_key_frame(
                      _filter_image_list=filter_image_list,
                      target_pos=END_IMAGE_PATH
                  )
          				# “開始加載的頁面標識元素”出現的幀
                  merge_list_1 = FrameTimeDiff.merge_number(find_1_list)
          				# 最大元素出現的幀
                  merge_list_2 = FrameTimeDiff.merge_number(find_2_list)
                  sectionalization = len(merge_list_1)
                  time_difference_list = []
                  for i in range(sectionalization):
          
                      start_frame_index = merge_list_1[i][-1]
                      end_frame_index = merge_list_2[i][0]
          
                      time_difference = (end_frame_index - start_frame_index) / self.fps
                      time_difference_list.append({"time_difference": time_difference, "start_frame_index": start_frame_index,
                       "end_frame_index": end_frame_index})
          
                  return time_difference_list
          
          
          if __name__ == '__main__':
              frameTimeDiff = FrameTimeDiff("20230703-164844.mp4")
              # 提取視頻中的圖片幀
              frameTimeDiff.extract_picture_frame()
              # 釋放對象和銷毀窗口
              frameTimeDiff.cap.release()
              cv2.destroyAllWindows()
          		# 計算頁面操作的加載時間
              time_diff = frameTimeDiff.analysis_more_picture_frame()
              print(time_diff)

          考慮到偽代碼易讀性,未添加多進程處理邏輯。上述 ImageIdentification 方法是封裝的圖像識別方法,封裝了kaze、brisk、sift、surf等圖像識別算法。

          拓展

          通過 Selenium 實現頁面的操作自動化執行,通過自動錄屏,獲取操作過程的視頻。最后通過上述 LCP指標的計算方式,是可以實現復雜頁面加載時間測試的自動化測試的,有興趣的可以嘗試一下。

          imeDown.js

          /*
          時間倒計時插件
          TimeDown.js
          */
          function TimeDown(id, endDateStr) {
          //結束時間
          var endDate = new Date(endDateStr);
          //當前時間
          var nowDate = new Date();
          //相差的總秒數
          var totalSeconds = parseInt((endDate - nowDate) / 1000);
          //天數
          var days = Math.floor(totalSeconds / (60 * 60 * 24));
          //取模(余數)
          var modulo = totalSeconds % (60 * 60 * 24);
          //小時數
          var hours = Math.floor(modulo / (60 * 60));
          modulo = modulo % (60 * 60);
          //分鐘
          var minutes = Math.floor(modulo / 60);
          //秒
          var seconds = modulo % 60;
          //輸出到頁面
          document.getElementById(id).innerHTML = "還剩:" + days + "天" + hours + "小時" + minutes + "分鐘" + seconds + "秒";
          //延遲一秒執行自己
          setTimeout(function () {
          TimeDown(id, endDateStr);
          }, 1000)
          }

          html

          <!DOCTYPE html>
          <html>
          <head runat="server">
          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
          <title>時間倒計時</title>
          <script src="TimeDown.js"></script>
          </head>
          <body>
          <form id="form1" runat="server">
          <div id="show">
          </div>
          <script type="text/javascript">
          TimeDown("show", "2024-03-9 8:00:45");
          </script>
          </form>
          </body>
          </html>

          顯示效果:

          還剩:2天19小時29分鐘5秒


          關于setTimeout與setInterval的區別:

          setTimeout只會執行一次, 在執行完成后, 重新啟動新的Timeout, 時間runtime計算設置為差時, 減少出現間隔越來越大的情況; 而setInterval()會導致間隔越來越大的情況, 而出現執行時間不準確的問題:

          1 Javascript會把執行的回調函數、瀏覽器的觸發事件、UI渲染事件,先放到隊列中, 隊列根據先進先出的規則, 依次執行他們, 當執行到隊列中的setInterval時很難保證其與setTimeout同步關系還保持。

          2 setInterval無視代碼錯誤:代碼報錯, 但是setInterval依舊會按時執行, 不會中斷。

          3 setInterval無視網絡延遲:如果調用ajax或其他服務, 他不會管是否返回回調, 會繼續按時執行。

          4 setInterval不保證執行:因為setInterval會定時執行, 如果函數邏輯很長, 間隔時間內執行不完, 后續方法會被拋棄。

          5 setInterval會受瀏覽器狀態影響、最小化、最大化、tab切換等外界因素的影響。

          網:https://day.js.org/en

          Github:https://github.com/iamkun/dayjs

          1. 安裝

          1.1. 下載

          下載地址:https://github.com/iamkun/dayjs/releases

          <script src="path/to/dayjs/dayjs.min.js"></script>
          <script>dayjs().format()</script>

          1.2. CDN

          <script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
          <script>dayjs().format()</script>

          1.3. Node.JS

          npm install dayjs --save
          var dayjs = require('dayjs')
          //import dayjs from 'dayjs' // ES 2015
          dayjs().format()

          1.4. TypeScript

          npm install dayjs --save
          import * as dayjs from 'dayjs'
          dayjs().format()

          更多見官方文檔:https://day.js.org/docs/en/installation/typescript

          2. API

          2.1. 解析

          Day.js 并沒有改變或覆蓋 Javascript 原生的 Date.prototype, 而是創造了一個全新的包含 Javascript Date 的 Day.js 對象,可以直接使用 dayjs() 來調用。

          Day.js 對象是不可變的, 所有的 API 操作都將返回一個新的 Day.js 對象。

          // 返回包含當前日期和時間的 Day.js 對象
          // 什么都不傳,相當于 dayjs(new Date())
          let now = dayjs();
          // 傳入一個標準的 ISO 8601 時間字符串
          // https://en.wikipedia.org/wiki/ISO_8601
          let date = dayjs('2020-06-01');
          // 傳入一個 Unix 時間戳 (13位)
          let date = dayjs(1591149248030);
          // 傳入一個 Javascript Date 對象
          let date = dayjs(new Date(2020, 6, 1));
          // 因為 Day.js 對象是不可變的,可使用如下方法獲取一個對象拷貝
          let date1 = date.clone(); // 方法一:在一個 Day.js 對象上使用 clone 函數
          let date2 = dayjs(date); // 方法二:傳入一個 Day.js 對象
          // 檢查當前 Day.js 對象是否是有效日期時間
          if (dayjs().isValid()) {
          // 有效
          } else {
          // 無效
          }

          2.2. 獲取和設置

          // 獲取,返回 number 類型的值
          dayjs().year(); // 年 ==> dayjs().get('year')
          dayjs().month(); // 月 ==> dayjs().get('month')
          dayjs().date(); // 日 ==> dayjs().get('date')
          dayjs().hour(); // 時 ==> dayjs().get('hour')
          dayjs().minute(); // 分 ==> dayjs().get('minute')
          dayjs().second(); // 秒 ==> dayjs().get('second')
          dayjs().millisecond(); // 毫秒 ==> dayjs().get('millisecond')
          dayjs().day(); // 本周的第幾天 ==> dayjs().get('day')
          // 設置,單位對應的值大小寫不敏感
          dayjs().set('month', 3);
          dayjs().set('second', 30);

          2.3. 操作

          // 增加
          dayjs().add(7, 'day'); // 增加 7 天
          // 減少
          dayjs().subtract(2, 'month'); // 減少 2 個月
          // 開頭
          dayjs().startOf('month'); // 當月第一天
          // 末尾
          dayjs().endOf('year'); // 當年最后一天

          2.4. 顯示

          // 格式化
          dayjs().format(); // 默認格式,如:2020-06-03T20:06:13+08:00
          dayjs().format("YYYY-MM-DD HH:mm:ss"); // 指定格式 2020-06-03 20:07:12
          // 獲取兩個 Day.js 對象的時間差,默認毫秒,可指定單位
          dayjs('2020').diff(dayjs('1998')); // 694224000000
          dayjs('2020').diff(dayjs('1998'), 'year'); // 22
          // 時間戳
          dayjs().valueOf(); // 毫秒
          dayjs().unix(); // 秒
          // 天數
          dayjs('2020-07').daysInMonth(); // 31
          // 原生 Date 對象
          dayjs().toDate(); // Wed Jun 03 2020 20:13:40 GMT+0800 (China Standard Time)
          // 返回 ISO 8601 格式的字符串
          dayjs().toJSON(); // "2020-06-03T12:15:54.635Z"
          dayjs().toISOString(); // "2020-06-03T12:16:48.199Z"

          2.5. 查詢


          主站蜘蛛池模板: 久久久久久人妻一区二区三区 | 亚洲欧洲无码一区二区三区| 国产成人精品亚洲一区 | 国产爆乳无码一区二区麻豆 | 精品一区二区三区无码免费直播| 一区二区国产在线播放| 成人h动漫精品一区二区无码| 精品欧美一区二区在线观看| 久久亚洲国产精品一区二区 | 麻豆一区二区三区蜜桃免费| 亚洲AV无码一区二区三区系列| 精品国产日韩亚洲一区| 亲子乱av一区二区三区| 中文字幕永久一区二区三区在线观看| 成人精品视频一区二区三区| 亚洲国产一区二区a毛片| 福利一区国产原创多挂探花| 日韩在线一区视频| 国产伦精品一区二区三区精品| 日韩AV无码一区二区三区不卡毛片 | 免费一区二区无码东京热| AV无码精品一区二区三区宅噜噜| 国产亚洲情侣一区二区无| 国产一区视频在线| 日韩免费一区二区三区在线播放 | 极品少妇一区二区三区四区| 韩国精品福利一区二区三区| 午夜无码视频一区二区三区| 在线观看免费视频一区| 日韩视频一区二区三区| 91精品一区二区综合在线| 成人免费一区二区三区在线观看| 日韩aⅴ人妻无码一区二区| 一区二区三区伦理高清| 亚洲一区二区在线视频| 麻豆国产在线不卡一区二区| 熟妇人妻系列av无码一区二区| 精品一区二区三区在线成人| 国产中的精品一区的| 亚洲色欲一区二区三区在线观看| 国产精品福利一区二区久久|