【解决方案】rrweb对css加载时间太短问题解决方案记录

408 阅读1分钟

在rrweb对html还原时,如果html中的操作是:快速跳了多个页面,将会出现有一些时间css丢失的情况,因为一些css资源是需要从服务器中请求的,如果加载时间不够将会出现css丢失

该问题不会影响一般的页面显示,但是如果我们需要对html中的div截图的话,将会造成较大影响

该问题算是 rrweb 的一个bug或是优化项,rrweb 没有做的话,我们需要先手动处理他的数据

解决方案

方案1:对需要请求服务器获取的 css 资源先进行预加载

加载 css 方法,先写一个获取css的方法,需要使用 http 模块,方法返回已加载 css 数据

const http = require('http');
const https = require('https');
function requestCSS(url) {
    return new Promise((resolve, reject) => {
        let protocol = null
        if (url.includes("https")) {
            protocol = https
        } else if (url.includes("http")) {
            protocol = http
        } else {
            reject('url error')
        }
        try {
            protocol && protocol.get(url, (response) => {
                let chunks = [];
                let size = 0;
                response.on('data', (chunk) => {
                    chunks.push(chunk);
                    size += chunk.length;
                });
                response.on('end', () => {
                    let data = Buffer.concat(chunks, size);
                    let base64Data = data.toString('utf-8');
                    resolve(base64Data);
                });
            }).on("error", (error) => {
                console.log("Error: " + error.message);
                reject(error)
            });
        } catch (error) {
            console.log("url Error: " + error.message);
            reject(error)
        }
    })
}

对 rrweb 数据使用正则匹配出需要从服务器获取的 css 标签数据,并调用加载方法对其加载,返回处理后的数据,需注意的是,可以对 css 的注释进行过滤

function preloadCSS(eventsJson) {
    return new Promise(async (resolve) => {
        let cssHrefObj = [];
        let cssHref = eventsJson.match(/link(.)+?\}/g);
        if (cssHref) {
            cssHref.forEach(item => {
                let res = item.match(/(?<=attributes\"\:).*/g);
                if (res) { cssHrefObj.push(...res) }
            })
        }
        for (let i = 0; i < cssHrefObj.length; i++) {
            let url = cssHrefObj[i].match(/(?<=href\"\:\").*?(?=\")/g)
            if (url) {
                try {
                    let res = await requestCSS(url[0]);
                    console.log(res)
                    // res = JSON.stringify(res).replace(/\/\*.+?\*\//g, "")
                    eventsJson = eventsJson.replace(cssHrefObj[i], '{"_cssText": ' + JSON.stringify(res) + '}');
                } catch (err) {
                    console.log(err);
                }
            }
        }
        resolve(eventsJson)
    })
}

方案2:如果需要对div截图,在截图之前预留更多的时间给html页面加载css

通过时间戳识别出,页面跳转前的时间,一旦匹配到页面需要跳转时,预留更多的时间留给css加载

(async () => {
    let delay = 500
    await new Promise((resolve) => {
        setTimeout(() => { resolve() }, delay)
    })
    const buffer = await this.wrapperEl.screenshot({
        encoding: "binary",
    });
})()

可以从rrweb的原始数据中获取到页面的时间戳,在时间戳前面,通过await做延时执行,延时500毫秒(css从服务器中加载时间大约在200-250ms)