使用rrweb 进行web 操作录制以及回放

rrweb 是使用typescript 开发的web 操作录制以及回放框架,包含了比较完整的系统组件

  • rrweb-snapshot 进行dom 与操作实践的关联处理
  • rrweb 主要包含了record 以及replay
  • rrweb-player rrweb 的UI 提供了方便的基于UI的操作,比如暂停,时间段选择

简单使用

使用docker-compose 运行,同时使用openresty提供了一个简单的rest api(就是request 以及response没有具体的存储操作)

  • docker-compose 文件
version: "3"
services: 
  app: 
   image: openresty/openresty:alpine-fat
   ports:
   - "8080:8080"
   volumes:
   - "./nginx_lua/:/opt/app/"
   - "./index.html:/usr/local/openresty/nginx/html/index.html"
   - "./nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf"
 
 
  • nginx.conf 配置
worker_processes 1;
user root;
events {
    worker_connections 1024;
}
http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;
    root html;
    gzip on;
    lua_package_path '/opt/app/?.lua;;';
    lua_code_cache off;
    lua_need_request_body on;
    real_ip_header X-Forwarded-For;
    real_ip_recursive on;
    server {
        listen 8080;
        server_name localhost;
        charset utf-8;
        ssi on;
        default_type text/html;
        location / {
           index index.html index.html;
        }
        location /api {
           content_by_lua_block {
              require("api/api")()
            }
        }
        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
            root html;
        }
    }
}
  • 基于openresty 的简单api

    nginx_lua/api/api.lua 文件

--  很简单就是获取request body,后边的处理就可以有好多方法了
local json = require("cjson")
function init()
    ngx.req.read_body()
    local body = ngx.req.get_body_data()
    if not body then
        if ngx.req.get_body_file() then
            return nil, "request body did not fit into client body buffer, consider raising 'client_body_buffer_size'"
        else
            return ""
        end
    end
    local res = {
        code = 1,
        message ="ok"
    }
    ngx.log(ngx.ERR, body)
    ngx.say(json.encode(res))
end
return init
 
  • 集成rrweb 的index.html 页面

    代码很简单,就是点击

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <title>rrweb demo web site</title>
  <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/rrweb.min.js"></script>
  <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb@latest/dist/record/rrweb-record.min.js"></script>
  <link rel="stylesheet" crossorigin="anonymous"
    href="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/style.css" />
  <script crossorigin="anonymous" src="https://cdn.jsdelivr.net/npm/rrweb-player@latest/dist/index.js"></script>
</head>
<body>
  <h1 class="some-title">this is some title for test</h1>
  <input type="button" value="record action" onclick="record()" />
  <br />
  <input type="button" value="replay action" onclick="replay()" />
  <div id="replaycontent">
  </div>
  <script>
    window.events = [];
    function record() {
      rrweb.record({
        emit(event) {
          // 将 event 存入 events 数组中
          events.push(event);
        },
      });
    }
    function replay() {
      new rrwebPlayer({
        target: document.getElementById("replaycontent"), // 可以自定义 DOM 元素
        data: {
          events,
        },
      });
    }
    // save 函数用于将 events 发送至后端存入,并重置 events 数组
    function save() {
      const body = JSON.stringify(window.events);
      events = [];
      fetch("http://localhost:8080/api", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body,
      });
    }
    // 每 10 秒调用一次 save 方法,避免请求过多
    setInterval(save, 10 * 1000);
  </script>
</body>
</html>
 

启动&&试用

  • 启动
docker-compose up -d
  • 效果

说明

从目前的使用上还有莫名有一个奇怪的bug,但是还是一个不错的开源方案,很值得学习使用下

参考资料

https://github.com/rrweb-io/rrweb
https://www.rrweb.io/
https://github.com/rongfengliang/rrweb-basic-learning

原文地址:https://www.cnblogs.com/rongfengliang/p/11038787.html