搭建前端監控系統(六)JS怎麼截圖,JS怎麼錄屏篇

  怎樣定位前端線上問題,一直以來,都是很頭疼的問題,由於它發生於用戶的一系列操做以後。錯誤的緣由可能源於機型,網絡環境,接口請求,複雜的操做行爲等等,在咱們想要去解決的時候很難復現出來,天然也就沒法解決。 固然,這些問題並不是不能克服,讓咱們來一塊兒看看如何去監控並定位線上的問題吧。 css

  這是搭建前端監控系統的第六章,主要是介紹如何使用js進行頁面截屏和錄屏,跟着我一步步作,你也能搭建出一個屬於本身的前端監控系統。html

 

  具體效果請移步:前端監控系統  前端

 

  用戶對前端程序員來講,就是一個黑匣子。 若是用戶上報了一個錯誤,前端程序員就是兩眼一抹黑,由於不少錯誤是無法復現的。那麼有沒有一個辦法能夠解決用戶和前端程序員之間的障礙呢, 讓用戶對咱們來講,再也不是黑匣子,而是透明化。用戶的頁面長什麼樣,他們都作了什麼操做,發生了什麼錯誤,咱們都可以清晰的知道,那麼,再有問題上報的時候,咱們就能夠修復它。其實說白了,就是錯誤場景重現了。git

  好了,話很少少,咱們進入主題。程序員

 

  1、JS怎麼實現截圖?github

  若是用戶在頁面上產生一些特殊行爲,好比報錯,白屏的時候,咱們想看看頁面長什麼樣子的,就能夠利用js截屏技術達到咱們的目的。這樣咱們對用戶當時的頁面有個直觀的感覺,解決問題的時候也會的駕輕就熟一些。web

  須要用到一個開源庫叫 html2Canvas ,這個庫的名字,我想你們也是耳熟能詳了。 關於他原理的介紹,有篇文章已經說得很詳細了,如何實現web錄屏。好,既然咱們能夠對頁面進行截圖了,那麼接下來的問題就是上傳了。html2canvas的截圖是圖片數據,多則大幾百Kb, 少則也有個上百Kb, 這麼大的流量,對用戶端,損耗確實過大。也許將來5G的大時代到了,這點流量根本不算什麼,可是如今主要仍是4g時代,這樣的流量損耗不容忽視。json

首先,對一個小頁面用js截圖進行了幾種測試,以下:

  參數                 截圖方式一      截圖方式二      截圖方式三

  壓縮前/後 字符串長度       28764/10787     93076/34903     168312/63118

  圖片壓縮率              72%          40%          0%

  截圖大小               21Kb         68.2Kb       123Kb

  使用html2canvas截圖代碼以下:canvas

/**
     * js處理截圖
     */
    this.screenShot = function (cntElem, callback) {
      var shareContent = cntElem;//須要截圖的包裹的(原生的)DOM 對象
      var width = shareContent.offsetWidth; //獲取dom 寬度
      var height = shareContent.offsetHeight; //獲取dom 高度
      var canvas = document.createElement("canvas"); //建立一個canvas節點
      var scale = 0.6; //定義任意放大倍數 支持小數
      canvas.style.display = "none";
      canvas.width = width * scale; //定義canvas 寬度 * 縮放
      canvas.height = height * scale; //定義canvas高度 *縮放
      canvas.getContext("2d").scale(scale, scale); //獲取context,設置scale
      var opts = {
        scale: scale, // 添加的scale 參數
        canvas: canvas, //自定義 canvas
        logging: false, //日誌開關,便於查看html2canvas的內部執行流程
        width: width, //dom 原始寬度
        height: height,
        useCORS: true // 【重要】開啓跨域配置
      };
      html2canvas(cntElem, opts).then(function(canvas) {
        var dataURL = canvas.toDataURL();
        var tempCompress = dataURL.replace("data:image/png;base64,", "");
        var compressedDataURL = Base64String.compress(tempCompress);
        callback(compressedDataURL);
      });
    }

  綜上分析,截圖方式一, 壓縮率高,雖然截圖不是很清晰,可是,也可以看得出,線上用戶頁面是什麼樣子的。並且,也解決了,在低端機上截圖消耗性能過大的弊端,二十幾Kb的流量,也是咱們徹底可以接受的大小了。因而可知,該方式可以知足咱們追蹤用戶行爲的需求了,固然,也能夠適當的提升清晰度,以實際狀況而定。api

  2、JS怎麼實現錄屏?

  Fundebug好久以前出了一個錄屏功能,進入Fundebug首頁,第一條即是 黑科技!支持錄屏。 這下就驚呆我了,js作前端監控,竟然還能錄屏? 你丫這是要逆天啊? 因此,趕忙註冊了帳號,進行試用。 由於當時fundebug的錄屏功能還在適用階段,因此我還誤覺得是靠一連串的截圖組成的視頻,反倒惹人笑話😂。

  實現JS錄屏須要用到一個開源庫rrweb,當看到這個庫的時候很有一番感慨:

  一嘆前輩高人技藝之精湛,二嘆開源世界心胸之廣闊,正是有這些無私奉獻的大牛們,才讓這技術的世界多姿多彩。

  這個錄屏庫的原理跟html2canvas不一樣,並不是是靠截圖,而是記錄下整個dom樹的結構,而後再分別記錄不一樣dom節點的變化,最後在iframe裏邊將他們從新渲染出來,就是咱們能看到的錄屏結果了。至於具體的錄屏效果,你們能夠進入個人首頁,點擊「錄屏測試」菜單查看效果,確實很驚豔。

      錄屏的api,rrweb已經寫得很清楚了,我就再也不浪費筆墨了。Js的錄屏效果很驚豔,可是同時也帶來了一個比較棘手的問題,以個人網站爲例,一段將近20秒的視頻,視頻數據居然達到了將近20M之多。像我網站首頁,最大的dom樹結構長度高達幾十萬之多,因此解決錄屏信息的長度即是首要任務。你們能夠看看個人測試數據,我錄製了一段近2分鐘的視頻,數據總量壓縮在1.5M左右,那麼除以2分鐘的之間,10秒鐘差很少控制在120kb大小左右。跟fundebug提出的,10s內錄屏數據壓縮在百K之內,感受也相差很少了,總之我一人之力有限,時間亦有限,因此暫時先作到這一步,並投入使用看看效果再說。下邊說說,如何進行字符串壓縮

 

   3、JS如何壓縮JSON字符串?

  對字符串進行壓縮,可能工做中不少狀況下是用不到的,可是若是作上傳日誌等相關的工做,如遇到截圖,錄屏等,那麼字符串壓縮就是不得不作了。

  由於rrweb的錄屏數據全都是JSON格式,天然而然要轉化成JSON字符串進行上傳了。我這裏使用兩步對JSON字符串進行壓縮:

  1. 簡化JSON中的key值。

   因爲rrweb是將整個dom樹轉化成json格式的數據,因此裏邊出現了大量的重複的key,因此我這裏使用更短小的key值,將他們替換,以達到縮短JSON字符串的目的。另外,裏邊包含了不少樣式的內容,你們都知道,樣式的key值很長,並且大多數都重複,因此樣式裏的值,咱們也要對其進行縮短替換。代碼以下:

var JSON_KEY = {"type":"≠","childNodes":"ā","name":"á","id":"ǎ","tagName":"à","attributes":"ē","style":"é","textContent":"ě","isStyle":"è","isSVG":"ī","content":"í","href":"ǐ","src":"ì","class":"ō","tabindex":"ó","aria-label":"ǒ","viewBox":"ò","focusable":"ū","data-icon":"ú","width":"ǔ","height":"ù","fill":"ǖ","aria-hidden":"ǘ","stroke":"ǚ","stroke-width":"ǜ","paint-order":"ü","stroke-opacity":"ê","stroke-dasharray":"ɑ","stroke-linecap":"?","stroke-linejoin":"ń","stroke-miterlimit":"ň","clip-path":"Γ","alignment-baseline":"Δ","fill-opacity":"Θ","transform":"Ξ","text-anchor":"Π","offset":"Σ","stop-color":"Υ","stop-opacity":"Φ"};
var JSON_CSS_KEY = {"background":"≠","background-attachment":"ā","background-color":"á","background-image":"ǎ","background-position":"à","background-repeat":"ē","background-clip":"é","background-origin":"ě","background-size":"è","border":"Г","border-bottom":"η","color":"┯","style":"Υ","width":"б","border-color":"ū","border-left":"ǚ","border-right":"ň","border-style":"Δ","border-top":"З","border-width":"Ω","outline":"α","outline-color":"β","outline-style":"γ","outline-width":"δ","left-radius":"Ж","right-radius":"И","border-image":"ω","outset":"μ","repeat":"ξ","repeated":"π","rounded":"ρ","stretched":"σ","slice":"υ","source":"ψ","border-radius":"Б","radius":"Д","box-decoration":"Й","break":"К","box-shadow":"Л","overflow-x":"Ф","overflow-y":"У","overflow-style":"Ц","rotation":"Ч","rotation-point":"Щ","opacity":"Ъ","height":"Ы","max-height":"Э","max-width":"Ю","min-height":"Я","min-width":"а","font":"в","font-family":"г","font-size":"ж","adjust":"з","aspect":"и","font-stretch":"й","font-style":"к","font-variant":"л","font-weight":"ф","content":"ц","before":"ч","after":"ш","counter-increment":"щ","counter-reset":"ъ","quotes":"ы","list-style":"+","image":"-","position":"|","type":"┌","margin":"┍","margin-bottom":"┎","margin-left":"┏","margin-right":"┐","margin-top":"┑","padding":"┒","padding-bottom":"┓","padding-left":"—","padding-right":"┄","padding-top":"┈","bottom":"├","clear":"┝","clip":"┞","cursor":"┟","display":"┠","float":"┡","left":"┢","overflow":"┣","right":"┆","top":"┊","vertical-align":"┬","visibility":"┭","z-index":"┮","direction":"┰","letter-spacing":"┱","line-height":"┲","text-align":"6","text-decoration":"┼","text-indent":"┽","text-shadow":"10","text-transform":"┿","unicode-bidi":"╀","white-space":"╂","word-spacing":"╁","hanging-punctuation":"╃","punctuation-trim":"1","last":"3","text-emphasis":"4","text-justify":"5","justify":"7","text-outline":"8","text-overflow":"9","text-wrap":"11","word-break":"12","word-wrap":"13"}

this.compressJson = function(o) {
      if (o instanceof Array) {
        var n = []
        for (var i = 0; i < o.length; ++i) {
          n[i] = this.compressJson(o[i])
        }
        return n
      } else if (o instanceof Object) {
        var n = {}
        for (var i in o) {
          if (i == "_cssText") {
            o[i]= o[i].replace(/ {/g, "{").replace(/; /g, ";").replace(/: /g, ":").replace(/, /g, ",").replace(/{ /g, "{")
            for (var key in JSON_CSS_KEY) {
              var cssAttr = JSON_CSS_KEY[key]
              var cssReg = new RegExp(key, "g");
              o[i]= o[i].replace(cssReg, cssAttr)
            }
          }

          if (JSON_KEY[i]) {
            n[JSON_KEY[i]] = this.compressJson(o[i])
            delete n[i]
          } else {
            n[i] = this.compressJson(o[i])
          }
        }
        return n
      }
      return o
    }

  2. 使用lz-string 庫對字符串進行壓縮

  lz-string有兩個api: 

  LZString.compress(String) --- 壓縮效果極好,可是我後臺沒法解析,我經歷不少嘗試,都解析不出來。
  LZString.compressToBase64(String) --- 壓縮效果通常,可是後臺能夠很好的解析出來,無奈,只能選擇了第二api進行壓縮。

  

至此,前端監控系統即可以完成截屏和錄屏的功能了。

相關文章
相關標籤/搜索