溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)

發(fā)布時(shí)間:2021-10-19 17:17:29 來源:億速云 閱讀:217 作者:iii 欄目:web開發(fā)

本篇內(nèi)容主要講解“怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)”吧!

1 / 實(shí)現(xiàn)方案思路

要想給用戶的訪問做一次完整的應(yīng)用狀態(tài)監(jiān)控錄制與回放,除了錄制視頻外,通過 Web API 實(shí)現(xiàn)大致有兩類主流的解決思路。

第一種方案是通過記錄 DOM 的每次變更,并將內(nèi)容序列化下來,然后在沙箱中還原并回放這些 UI 變化。這種方案的優(yōu)點(diǎn)之一是能夠給 DOM 創(chuàng)建快照的概念,在應(yīng)用每一次狀態(tài)變化后進(jìn)行收集,把這個(gè)序列串起來后我們便可以靈活掌握回放速度、并針對(duì)關(guān)鍵性節(jié)點(diǎn)進(jìn)行自定義回放。在社區(qū)開源方案中,這類技術(shù)最成熟的莫過于 rrweb https://github.com/rrweb-io/rrweb ,此外若干互聯(lián)網(wǎng)企業(yè)也有提供一些商業(yè)解決方案,但技術(shù)設(shè)計(jì)上大同小異,大致都可以拆分為 DOM 序列化、構(gòu)建快照序列、反序列化回放以及運(yùn)行沙箱環(huán)境等四方面。

另一種可行方案,從我們的調(diào)研結(jié)果來看,是將用戶側(cè)所有數(shù)據(jù)收集起來,然后在一個(gè)可控運(yùn)行環(huán)境下嚴(yán)格按照校準(zhǔn)時(shí)間對(duì)用戶事件操作進(jìn)行派發(fā),從而控制回放。這里主要分為兩部分,一方面因?yàn)槲覀兺ㄟ^派發(fā)事件來重演用戶的行為,便需要高精度計(jì)時(shí)器以及完善的用戶事件收集策略;另一方面,由于要保證應(yīng)用狀態(tài)在兩端的變化一致性,只嚴(yán)格觸發(fā)用戶的操作還不夠,我們還要將任何應(yīng)用與網(wǎng)絡(luò)之前的交互操作(請(qǐng)求與響應(yīng)內(nèi)容)精準(zhǔn)對(duì)齊,所以我們需要攔截所有請(qǐng)求、即時(shí)的前端構(gòu)建產(chǎn)物以及用戶操作序列等等。

第二種方案由于可以近乎模擬用戶側(cè)運(yùn)行時(shí)的狀態(tài)變化,相當(dāng)于將當(dāng)時(shí)的用戶整個(gè)搬到了我們面前,因此可以方便開發(fā)同學(xué)進(jìn)一步調(diào)試。在社區(qū)開源方案中,我們沒有找到類似的實(shí)現(xiàn),但商業(yè)方案中 https://logrocket.com/ 最接近這個(gè)思路,其提供的不少功能甚至比這里提及的功能要更強(qiáng)大。

但本文中,我們只討論當(dāng)需要實(shí)現(xiàn)這樣一個(gè)系統(tǒng)時(shí)的涉及技術(shù)項(xiàng)。所以接下來,來看看我們?cè)谡{(diào)研中記錄的一些有用的 Web API,希望對(duì)你們實(shí)現(xiàn)有幫助。

2 / 關(guān)鍵技術(shù)基礎(chǔ)

本章主要介紹要實(shí)現(xiàn)這樣一個(gè)前端監(jiān)控回放系統(tǒng)的四個(gè)環(huán)節(jié),關(guān)于這些內(nèi)容,rrweb 的描述篇幅會(huì)更加翔實(shí),可以更進(jìn)一步查閱他們的文檔描述。

2.1 應(yīng)用與狀態(tài)的序列化

如何回放一個(gè)應(yīng)用的狀態(tài)變化?首先,我們要將應(yīng)用的內(nèi)容以及狀態(tài)變化收集起來。如果用 jQuery 我們可以這樣實(shí)現(xiàn) body 內(nèi)容的收集與替換:

// record const snapshot = $('body').clone(); // replay $('body').replaceWith(snapshot);

如果換用MediaRecorder API,利用 ondataavailable 和 onstop 兩個(gè)事件處理 API,我們還可以將 DOM 轉(zhuǎn)變成可播放的媒體文件,比如這樣:

startRecording() {     const stream = (this.canvas as any).captureStream();     this.recorder = new MediaRecorder(stream, { mimeType: 'video/webm' });     const data = [];        this.recorder.ondataavailable = (event) => {       if (event.data && event.data.size) {         data.push(event.data);       }     };       this.recorder.onstop = () => {       const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' }));       this.videoUrl$.next(         this.sanitizer.bypassSecurityTrustUrl(url)       );     };       this.recorder.start();     this.recordVideo$.next(true); }

但這些內(nèi)容是沒法序列化的,而媒體體積又過于龐大且無法進(jìn)一步分析。舉個(gè)例子,一個(gè) input 標(biāo)簽,如果我們不做任何額外處理只將其轉(zhuǎn)化成文本進(jìn)行存儲(chǔ)(類似 innerHTML),那么其中包含的 value 等狀態(tài)便會(huì)丟失,這便是我們首先需要將 DOM 及其視圖狀態(tài)進(jìn)行序列化的原因所在。關(guān)于如何序列化 DOM 也有不少開源方案比如 https://github.com/inikulin/parse5 ,rrweb 在文檔中有提到為什么沒有采用的原因,而我這里簡(jiǎn)單列一下在序列化環(huán)節(jié)中需要考慮的幾點(diǎn)細(xì)節(jié):

  • 如何將 DOM 樹轉(zhuǎn)化成一個(gè)帶視圖狀態(tài)的樹狀結(jié)構(gòu),包括未反映在 HTML 中的視圖狀態(tài);

  • 如何定義唯一標(biāo)識(shí),方便應(yīng)用狀態(tài)變化(快照)的溯源;

  • 如何處理特殊標(biāo)簽諸如 script 以及樣式等內(nèi)容,因方案而異;

簡(jiǎn)而言之,這部分的目的是完成一個(gè) DOM 樹至可存儲(chǔ)狀態(tài)的數(shù)據(jù)結(jié)構(gòu)映射。

2.2 記錄 DOM 變化與交互的快照

如果只是記錄 DOM 變更的話,我們可以很方便的利用 MutationObserver API 達(dá)到變更監(jiān)聽與記錄這一目的,但此外我們還需要考慮如何將 Mutation 的批量序列轉(zhuǎn)化為快照上的增量更新。

比如,為了方便針對(duì) Node 進(jìn)行增刪時(shí)可以唯一確定其在樹形結(jié)構(gòu)中的位置,我們最好設(shè)計(jì)一個(gè)合適的 DOM 唯一標(biāo)記策略,此外,如何優(yōu)化諸如 mousemove 以及大量頻繁 input 輸入導(dǎo)致的視圖變更等。前者的設(shè)計(jì)可以繼續(xù)用在增量快照的實(shí)現(xiàn)上,而后者的表現(xiàn)則直接影響用戶體驗(yàn),容易導(dǎo)致 DOM 在更改時(shí) Node 記錄的出現(xiàn)順序錯(cuò)誤。

這一環(huán)節(jié),主要依賴 DOM 的序列化方案繼續(xù)處理。在添加一些其他必要信息諸如時(shí)間序列編號(hào)等,便可以進(jìn)行存儲(chǔ)等操作了。

2.3 回放重演

簡(jiǎn)單來說,重演就是將收集到的數(shù)據(jù)按照順序依次“播放”一遍,視頻文件的播放需要音視頻解碼器,而我們的重演環(huán)節(jié)要做的工作就可以簡(jiǎn)單理解成一個(gè) Web 應(yīng)用解碼器,從用戶端收集上來的數(shù)據(jù)結(jié)構(gòu)除了要做清洗和存儲(chǔ)外,還不能直接被回放側(cè)使用,其中有不少需要考慮的細(xì)節(jié)。

舉個(gè)簡(jiǎn)單的例子,我們利用 Web API 是沒法達(dá)到派發(fā) hover 事件的,但是我們的項(xiàng)目中一定存在大量的 hover 樣式,那么如何針對(duì)這些交互做額外處理,對(duì) Node 狀態(tài)變化做相應(yīng)樣式的補(bǔ)全,便成為一個(gè)需要考慮的環(huán)節(jié)結(jié)合 mousedown 和 mouseup 兩個(gè)事件的觸發(fā)時(shí)機(jī)是否夠用?事件收集的掛載節(jié)點(diǎn)如何圈定?這些都是需要考慮的地方。再比如,回放中想跳過被認(rèn)為無意義的操作片段,如何設(shè)計(jì)才能保持應(yīng)用在前后兩個(gè)時(shí)間節(jié)點(diǎn)上不因?yàn)樘^的操作而缺失視圖狀態(tài)?

這一環(huán)節(jié),目的是為了實(shí)現(xiàn)對(duì)快照存儲(chǔ)下來的數(shù)據(jù)結(jié)構(gòu)進(jìn)行回放。因?yàn)橐WC回放側(cè)與收集側(cè)的嚴(yán)格一致,諸如高精度計(jì)時(shí)、DOM 補(bǔ)全以及交互效果模擬等細(xì)節(jié),都需要詳細(xì)設(shè)計(jì)。

2.4 沙箱

沙箱,是為了給回放提供一個(gè)安全可控的運(yùn)行環(huán)境。如何采用 DOM 快照方案,那么便需要考慮如何禁止一些“不安全”的 DOM 操作。例如應(yīng)用內(nèi)鏈接跳轉(zhuǎn)、我們不太可能會(huì)直接給用戶打開一個(gè)新的 tab,為了保證快照狀態(tài)依次回放,我們還需要考慮如何安全準(zhǔn)確的反序列化構(gòu)建 DOM。如果實(shí)現(xiàn)上考慮通過派發(fā)事件的思路來實(shí)現(xiàn),那么如何準(zhǔn)確定位派發(fā)的 Node 節(jié)點(diǎn)、如何匹配數(shù)據(jù)請(qǐng)求并響應(yīng)等等都是需要重點(diǎn)考慮的。

兩種思路都有一些需要共同考慮的事情,比如如何保證運(yùn)行環(huán)境符合瀏覽器的安全限制、需要展示和用戶操作保持一致的渲染層等等。這部分在技術(shù)項(xiàng)拆分一節(jié),會(huì)提到兩個(gè)解決方案,分別是 iframe 以及 puppeteer,此處不再贅述。

3 / 技術(shù)項(xiàng)拆分與相關(guān) Web API

Web 有強(qiáng)大的 API List,本章節(jié)針對(duì)可能用到的 API 與相關(guān)技術(shù)做一一講解。

3.1 MutationObserver

MutationObserver API 可以用于監(jiān)聽觀察 DOM 對(duì)象的變化并予以記錄數(shù)組的形式返回,這可以用在應(yīng)用初始化和增量快照的記錄部分。關(guān)于 MutationObserver 的方法與入?yún)⑦@里不做詳細(xì)介紹,簡(jiǎn)單來看,要用 MutationObserver API 監(jiān)聽一個(gè) Node 的變更大致分為這么幾步:

  • 利用 document.getElementById 等 API 定位你所需要的 Node

  • 定義一個(gè) MutationObserverInit 對(duì)象,此對(duì)象的配置項(xiàng)描述了 DOM 的哪些變化應(yīng)該提供給當(dāng)前觀察者的回調(diào)函數(shù)

  • 定義上述回調(diào)函數(shù)被調(diào)用時(shí)的執(zhí)行邏輯

  • 創(chuàng)建一個(gè)觀察器實(shí)例并傳入回調(diào)函數(shù)

  • 調(diào)用 MutationObserver 的 observe() 方法開始觀察

以下節(jié)選自 MDN 的一段示例代碼用于釋義:

const targetNode = document.getElementById('some-id'); const config = { attributes: true, childList: true, subtree: true };  const callback = function(mutationsList, observer) {     for(let mutation of mutationsList) {         if (mutation.type === 'childList') {             console.log('A child node has been added or removed.');         }     } };  const observer = new MutationObserver(callback); observer.observe(targetNode, config);

3.2 iframe 標(biāo)簽

iframe 大家肯定都用過,它能夠?qū)⒘硪粋€(gè) HTML 頁面嵌入到當(dāng)前頁面中。利用 iframe ,我們可以快速構(gòu)建一個(gè)安全的沙箱機(jī)制,比如將其用于限制 JavaScript 執(zhí)行等。除了我們平時(shí)直接給 iframe 的 src 屬性賦值外,iframe 還有不少其他值得了解的屬性,這些在完善運(yùn)行沙箱環(huán)境上都會(huì)有所幫助,比如其中的 sandbox 屬性便可以對(duì)呈現(xiàn)在 iframe 中的內(nèi)容啟用一些額外的限制條件。

此外,要實(shí)現(xiàn)沙箱中不同容器的的通信,可以通過 postMessage API 來完成。這里有一篇非常詳細(xì)的文章介紹了 iframe 的方方面面,可以進(jìn)一步查閱 https://blog.logrocket.com/the-ultimate-guide-to-iframes/ 。

3.3 HTTP Archive

HTTP 請(qǐng)求與響應(yīng)匯集,即我們常說的 HAR 格式數(shù)據(jù)。打開 chrome devtools,network tab 下的每一條數(shù)據(jù)流都代表一個(gè)請(qǐng)求,從 http 到 weoscket,request 到 response,包含 request 入?yún)?、headers、連接耗時(shí)、發(fā)起時(shí)間、響應(yīng)時(shí)間、TTFB、內(nèi)容大小等等。如同前面所述,如果要在回放側(cè)派發(fā)用戶操作的話,那么即需要在采集時(shí)將所有請(qǐng)求攔截并標(biāo)號(hào)進(jìn)行存儲(chǔ),如此一來,直接收集 HAR 便成了最佳的選擇。

但想要收集 HAR 會(huì)遇到一些限制,比如這類數(shù)據(jù)只在 chrome devtools API 中開放,所以要實(shí)現(xiàn)這塊數(shù)據(jù)的收集,必須采用類似 chrome 插件的形式進(jìn)行開發(fā),但這樣一來,如何進(jìn)行用戶無感知的數(shù)據(jù)收集與上報(bào)又成了一個(gè)難題。

怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)

3.4 network 與 webRequest API

利用 chrome.webRequest API 可以允許我們觀察和分析流量,并在運(yùn)行中攔截、阻止或修改它。從上文描述來看,要收集 HAR 只能通過這個(gè) API 來實(shí)現(xiàn),下面給出一個(gè)調(diào)用 network 對(duì)請(qǐng)求攔截處理的示例,詳細(xì)用法可參照文檔 https://developer.chrome.com/extensions/webRequest

chrome.devtools.network.onRequestFinished.addListener(function (req) {     // Only collect Resource when XHR option is enabled     if (document.getElementById('check-xhr').checked) {         console.log('Resource Collector pushed: ', req.request.url);         req.getContent(function (body, encoding) {             if (!body) {                 console.log('No Content Detected!, Resource Collector will ignore: ', req.request.url);             } else {                 reqs[req.request.url] = {                     body,                     encoding                 };             }             setResourceCount();         });         setResourceCount();     } });

3.5 Service Worker 與 proxy

我們都知道 Servcie Worker 的 cache API 在 PWA 應(yīng)用中廣泛使用,但其實(shí)除了可以將其用于離線應(yīng)用體驗(yàn)增強(qiáng)外,由于 Servcie Worker 擁有更精細(xì)、更完整的控制特性,它完全可以作為一個(gè)頁面與服務(wù)器之間的代理中間層,用于捕獲它所負(fù)責(zé)的頁面請(qǐng)求,并返回相應(yīng)資源。

一般來說,基于框架 HTTPClient (Angular) 或者原生 XMLHttpRequest 的監(jiān)聽,對(duì)頁面的請(qǐng)求攔截都或多或少存在一些無法捕獲的盲區(qū),但 Service Worker 不會(huì),你所需要注意的是需要將它放在你的應(yīng)用根目錄下,或者通過入?yún)⒃谧?cè)時(shí)指定 scope 以使你的 Service Worker 在指定范圍內(nèi)生效。

關(guān)于它,除了理解其生命周期外,還有些細(xì)節(jié)需要注意,比如作用域 scope。單個(gè) Service Worker 可以控制多個(gè)頁面,每個(gè)頁面不會(huì)有自己獨(dú)有的 worker,所以請(qǐng)注意 scope 的生效范圍;在你 scope 范圍內(nèi)的頁面在加載完時(shí),Service Worker 便可以開始控制它,所以請(qǐng)小心定義 Service Worker 腳本里的全局變量。

當(dāng)然,為了方便開發(fā),你可以使用 TypeScript、Babel、webpack 等語言和工具,來加速你的開發(fā)體驗(yàn)。MDN 有一篇教程對(duì) Service Worker 入門介紹的挺詳細(xì),可以一看 https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers

怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)

上圖為 Service Worker 的生命周期

3.6 Web Worker

Web Worker 的作用,就是為 JavaScript 創(chuàng)造多線程環(huán)境,允許主線程創(chuàng)建 Worker 線程,將一些任務(wù)分配給后者運(yùn)行。在主線程運(yùn)行的同時(shí),Worker 線程在后臺(tái)運(yùn)行,兩者互不干擾。等到 Worker 線程完成計(jì)算任務(wù),再把結(jié)果返回給主線程。這樣的好處是,一些計(jì)算密集型或高延遲的任務(wù),被 Worker 線程負(fù)擔(dān)了,主線程(通常負(fù)責(zé) UI 交互)就會(huì)很流暢,不會(huì)被阻塞或拖慢。—— 阮一峰的網(wǎng)絡(luò)日志

用于收集數(shù)據(jù)上報(bào)的計(jì)算工作,由于重計(jì)算邏輯且需要頻繁做數(shù)據(jù)處理,最好不要在主線程操作,否則很影響交互體驗(yàn)。Web Worker 是一個(gè)很好的解決方案,在采用它之后,其余涉及到 BOM/DOM 的相關(guān)操作還可以將需要的數(shù)據(jù)直接事件傳遞通知或者 SharedArrayBuffer 共享。

那么,如何構(gòu)建一個(gè) Web Worker 呢?一個(gè) worker 文件由簡(jiǎn)單的 JavaScript 代碼組成,在寫完之后,你需要在主線程中傳入 URI 來構(gòu)建這么一個(gè) worker 線程,如下圖所示:

const myWorker = new Worker('worker.js');

在 worker 中,除了完善用于計(jì)算的邏輯代碼外,我們還可以引入腳本、通過 postMessage 與主線程通信等等:

// 引入腳本 importScripts('foo.js', 'bar.js');  // 在 Web Worker 中監(jiān)聽消息與向外通信 onmessage = function(e) {   console.log('Message received from main script');   var workerResult = 'Result: ' + (e.data[0] * e.data[1]);   console.log('Posting message back to main script');   postMessage(workerResult); }

上面提到的 Service Worker 也可以算作 Web Worker 的一個(gè)相似品,但與一般的 Web Worker 不同,Service Worker 有一些額外的特性來實(shí)現(xiàn)代理的目的。只要它們被安裝且被激活,Service Worker 就可以攔截主線程中發(fā)起的任何網(wǎng)絡(luò)請(qǐng)求。

此外,還有一個(gè)特別的 Worker 叫做 Worklet,這些 API 都挺有意思,值得另開篇幅介紹,但與本文暫不相關(guān),便不詳述。

3.7 空閑調(diào)度與 requestIdleCallback

window.requestIdleCallback() 方法將在瀏覽器的空閑時(shí)段內(nèi)調(diào)用的函數(shù)排隊(duì)。這使開發(fā)者能夠在主事件循環(huán)上執(zhí)行后臺(tái)和低優(yōu)先級(jí)工作,而不會(huì)影響延遲關(guān)鍵事件,如動(dòng)畫和輸入響應(yīng)。函數(shù)一般會(huì)按先進(jìn)先調(diào)用的順序執(zhí)行,然而,如果回調(diào)函數(shù)指定了執(zhí)行超時(shí)時(shí)間 timeout,則有可能為了在超時(shí)前執(zhí)行函數(shù)而打亂執(zhí)行順序。 —— MDN

由于系統(tǒng)需要對(duì)用戶數(shù)據(jù)進(jìn)行全量收集,除了計(jì)算邏輯的負(fù)擔(dān)分?jǐn)偼?,包含序列化?jié)點(diǎn)、快照等結(jié)構(gòu)數(shù)據(jù)的上傳勢(shì)必又會(huì)成為項(xiàng)目潛在的瓶頸與需要考慮的優(yōu)化點(diǎn)。利用 requestIdleCallback API,我們可以保證數(shù)據(jù)在處理后的上報(bào)(網(wǎng)絡(luò)請(qǐng)求)不對(duì)用戶交互造成影響,例如使用戶頁面卡頓等。

更為人熟知的一個(gè) Web API 是 requestAnimationFrame,這個(gè) API 可以告訴瀏覽器在下次重繪之前執(zhí)行傳入的回調(diào)函數(shù),由于是每幀執(zhí)行一次,所以其每秒的執(zhí)行次數(shù)與瀏覽器屏幕刷新次數(shù)一致,通常是每秒60次。而 requestIdleCallback 與其相反,它會(huì)在每幀的最后執(zhí)行,但并不是每一幀都保證會(huì)執(zhí)行 requestIdleCallback。這個(gè)原因很簡(jiǎn)單,我們無法保證每一幀結(jié)束時(shí)我們還有時(shí)間,所以并不能保證 requestIdleCallback 的執(zhí)行時(shí)間。

requestIdleCallback API 的設(shè)計(jì)很簡(jiǎn)單,一個(gè)空閑調(diào)度函數(shù),一個(gè)可選配置項(xiàng)參數(shù)。

const handle = window.requestIdleCallback(callback[, options])

舉個(gè)例子,假設(shè)我們現(xiàn)在需要追蹤用戶的點(diǎn)擊事件,并將數(shù)據(jù)上報(bào)服務(wù)器,利用這個(gè) API 我們可以這樣完成數(shù)據(jù)收集以及上報(bào)調(diào)度:

const btns = btns.forEach(btn =>  btn.addEventListener('click', e => {     // 其他交互          putIntoQueue({       type: 'click'       // 收集數(shù)據(jù)     }));     schedule(); });  function schedule() {     requestIdleCallback(       deadline => {           while (deadline > 0) {             const event = queues.pop();             send(event);           }       },       { timeout: 1000 }    ); }

3.8 本地持久化存儲(chǔ) - localStorage 與 IndexedDB

既然要上報(bào),那么就要考慮在數(shù)據(jù)未完成上報(bào)時(shí)用戶的意外退出或者網(wǎng)絡(luò)斷開等。在這些情況下,瀏覽器的本地存儲(chǔ)方案便派上了用場(chǎng)。由于需要保證數(shù)據(jù)上報(bào)的完整性,持久化存儲(chǔ)推薦 localStorage API 以及 IndexedDB API。

利用 localStorage API,我們可以快速的存取字符串形式的鍵值對(duì),但受瀏覽器限制,存儲(chǔ)大小一般有限,僅幾兆而已。

利用 IndexedDB API,我們可以在客戶端存儲(chǔ)大量的結(jié)構(gòu)化數(shù)據(jù)(也包括文件/二進(jìn)制大型對(duì)象等),IndexedDB 被瀏覽器存在本地磁盤中,于是,你可以將其存儲(chǔ)上限近似看成計(jì)算機(jī)的剩余存儲(chǔ)容量。

IndexedDB 是一個(gè)事務(wù)型數(shù)據(jù)庫系統(tǒng),類似于基于 SQL 的 RDBMS。 然而,不像 RDBMS 使用固定列表,IndexedDB 是一個(gè)基于 JavaScript 的面向?qū)ο髷?shù)據(jù)庫。IndexedDB 允許您存儲(chǔ)和檢索用鍵索引的對(duì)象;可以存儲(chǔ)結(jié)構(gòu)化克隆算法支持的任何對(duì)象。您只需要指定數(shù)據(jù)庫模式,打開與數(shù)據(jù)庫的連接,然后檢索和更新一系列事務(wù)。

localStorage 與 IndexedDB 的使用都相對(duì)容易,MDN 上有較為完善的入門指導(dǎo),此處便不貼代碼了。

3.9 puppeteer - Headless Chrome Node.js API

假設(shè)我們不使用 iframe 來做便捷沙箱環(huán)境,那么一個(gè)更強(qiáng)大的解決方案便是 puppeteer。

puppeteer 是谷歌官方出品的一個(gè)通過 DevTools 協(xié)議控制 headless Chrome 的 Node 庫,我們可以通過 puppeteer 提供的 API 直接控制 Chrome,進(jìn)而模擬大部分用戶在瀏覽器上的操作,來進(jìn)行 UI Test 或者作為爬蟲訪問頁面來收集數(shù)據(jù)。有關(guān) puppeteer 的使用可以參考文檔 https://pptr.dev/

回到我們的場(chǎng)景,當(dāng)需要重演用戶的操作時(shí),我們可以便捷的利用 page API 來做,比如下面這個(gè)例子:

const puppeteer = require('puppeteer');  (async () => {   const browser = await puppeteer.launch()   const page = await browser.newPage()      await page.goto('https://hijiangtao.github.io/')      await page.setViewport({ width: 2510, height: 1306 })      await page.waitForSelector('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div')   await page.click('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .ant-tabs-tab:nth-child(2)')      await page.waitForSelector('.ant-tabs-nav-wrap > .container')   await page.click('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .ant-tabs-tab:nth-child(1)')      await browser.close() })()

當(dāng)然,puppeteer 存在廣泛的使用場(chǎng)景,比如生成頁面截圖或者 PDF、進(jìn)行自動(dòng)化 UI 測(cè)試、構(gòu)建爬蟲系統(tǒng)、捕獲頁面時(shí)間軸進(jìn)行性能診斷等等

到此,相信大家對(duì)“怎么實(shí)現(xiàn)一個(gè)前端監(jiān)控回放系統(tǒng)”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI