rrweb 网页回放

最近项目需要对生产环境进行bug复现,老大让我调研一下rrweb,我寻思没啥调研的,随便写一个例子得了,下方分为前端代码和后端代码,想要后端代码的私聊up主,我会私发给你

App.vue 

<template>
  <div class="main">
    <div class="interaction rr-block">
      <el-button v-if="!showReplay" type="primary" @click="record"
        >录制</el-button
      >
      <el-button v-if="!showReplay" type="success" @click="replay"
        >回放</el-button
      >
      <el-button v-if="showReplay" type="warning" @click="reset"
        >返回演示</el-button
      >
      <!-- 获取接口数据 -->
      <el-button type="info" @click="getAXIOSdata">获取接口数据</el-button>
    </div>
    <div v-if="!showReplay">
      <div class="form">
        <input type="text" v-model="form.woc" placeholder="woc" />
        <el-form ref="form" :rules="formRules" :model="form" label-width="80px">
          <el-form-item label="活动名称" prop="name">
            <el-input v-model="form.name"></el-input>
          </el-form-item>
          <el-form-item label="活动区域" prop="region">
            <el-select v-model="form.region" placeholder="请选择活动区域">
              <el-option label="区域一" value="shanghai"></el-option>
              <el-option label="区域二" value="beijing"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="活动时间">
            <el-col :span="11" prop="date1">
              <el-date-picker
                type="date"
                placeholder="选择日期"
                v-model="form.date1"
                style="width: 100%"
              ></el-date-picker>
            </el-col>
            <el-col class="line" :span="2">-</el-col>
            <el-col :span="11" prop="date2">
              <el-time-picker
                placeholder="选择时间"
                v-model="form.date2"
                style="width: 100%"
              ></el-time-picker>
            </el-col>
          </el-form-item>
          <el-form-item label="即时配送">
            <el-switch v-model="form.delivery"></el-switch>
          </el-form-item>
          <el-form-item label="活动性质" prop="type">
            <el-checkbox-group v-model="form.type">
              <el-checkbox label="美食/餐厅线上活动" name="type"></el-checkbox>
              <el-checkbox label="地推活动" name="type"></el-checkbox>
              <el-checkbox label="线下主题活动" name="type"></el-checkbox>
              <el-checkbox label="单纯品牌曝光" name="type"></el-checkbox>
            </el-checkbox-group>
          </el-form-item>
          <el-form-item label="特殊资源" prop="resource">
            <el-radio-group v-model="form.resource">
              <el-radio label="线上品牌商赞助"></el-radio>
              <el-radio label="线下场地免费"></el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="活动形式" prop="desc">
            <el-input type="textarea" v-model="form.desc"></el-input>
          </el-form-item>
          <el-form-item>
            <el-button type="primary" @click="onSubmit">打印表单数据</el-button>
            <el-button type="danger" @click="logError">手动抛错</el-button>
          </el-form-item>
        </el-form>
      </div>
    </div>
    <div ref="replayer"></div>
  </div>
</template>

<script>
const rrweb = require("rrweb");
import axios from "axios";
import rrwebPlayer from "rrweb-player";
export default {
  data() {
    return {
      eventsMatrix: [[]], // 使用二维数组来存放多个 event 数组
      showReplay: false,
      form: {
        name: "",
        region: "",
        date1: "",
        date2: "",
        delivery: false,
        type: [],
        resource: "",
        desc: "",
      },
      formRules: {
        name: [
          {
            required: true,
            message: "请输入活动名称",
            trigger: "blur",
          },
        ],
        region: [
          {
            required: true,
            message: "请选择活动区域",
            trigger: "change",
          },
        ],
        date1: [
          {
            required: true,
            message: "请选择日期",
            trigger: "change",
          },
        ],
        date2: [
          {
            required: true,
            message: "请选择时间",
            trigger: "change",
          },
        ],
        type: [
          {
            required: true,
            message: "请选择活动性质",
            trigger: "change",
          },
        ],
        resource: [
          {
            required: true,
            message: "请选择活动资源",
            trigger: "change",
          },
        ],
        desc: [
          {
            required: true,
            message: "请填写活动形式",
            trigger: "blur",
          },
        ],
      },
      stopFn: null,

      requestKey: "",
    };
  },
  created() {
    console.log("created");
  },
  methods: {
    onSubmit() {
      console.log(JSON.stringify(this.form));
    },
    logError() {
      throw Error("模拟错误");
    },
    record() {
      const self = this;
      this.stopFn = rrweb.record({
        checkoutEveryNth: 100, // 每 100 个 event 重新制作快照
        emit(event, isCheckout) {
          // isCheckout 是一个标识,告诉你重新制作了快照
          if (isCheckout) {
            console.log("重新制作快照", event);
            self.eventsMatrix.push([]);
          }
          self.eventsMatrix[self.eventsMatrix.length - 1].push(event);
        },
        // 定制的选项
        plugins: [
          rrweb.getRecordConsolePlugin({
            level: ["info", "log", "warn", "error"],
            lengthThreshold: 10000,
            stringifyOptions: {
              stringLengthLimit: 1000,
              numOfKeysLimit: 100,
              depthOfLimit: 1,
            },
            logger: window.console,
          }),
        ],
      });
    },
    async replay() {
      function getSampleKey() {
        return Math.random().toString(36).substring(2);
      }
      if (!this.requestKey) {
        this.requestKey = getSampleKey();
      }
      // 由于一次性传输数据量过大,导致请求失败,所以采用分片传输,一次只传输20个event,
      // 如果遇到type为2的事件,则单独传输
      let chunkEvent = [];
      for (let i = 0; i < this.eventsMatrix.length; i++) {
        for (let j = 0; j < this.eventsMatrix[i].length; j++) {
          if (this.eventsMatrix[i][j].type == 2) {
            await axios({
              method: "post",
              url:
                process.env.NODE_ENV == "development"
                  ? "http://127.0.0.1:5000/store"
                  : "部署地址不便泄露",
              data: {
                event: [this.eventsMatrix[i][j]],
                key: this.requestKey,
              },
            });
          } else {
            // 否则,每20个event为一组
            chunkEvent.push(this.eventsMatrix[i][j]);
            if (chunkEvent.length == 20) {
              await axios({
                method: "post",
                url:
                  process.env.NODE_ENV == "development"
                    ? "http://127.0.0.1:5000/store"
                    : "部署地址不便泄露",
                data: {
                  //将二维数组转为一维数组
                  event: chunkEvent.reduce((acc, val) => acc.concat(val), []),
                  key: this.requestKey,
                },
              });
              chunkEvent = [];
            }
            // 如果最后一组不足20个,则单独传输
            if (
              i == this.eventsMatrix.length - 1 &&
              j == this.eventsMatrix[i].length - 1
            ) {
              await axios({
                method: "post",
                url:
                  process.env.NODE_ENV == "development"
                    ? "http://127.0.0.1:5000/store"
                    : "部署地址不便泄露",
                data: {
                  event: chunkEvent.reduce((acc, val) => acc.concat(val), []),
                  key: this.requestKey,
                },
              });
            }
          }
        }
      }

      this.$message.success("录制完成!");

      console.log(
        "最近的操作记录: ",
        JSON.stringify(this.eventsMatrix[this.eventsMatrix.length - 1])
      );
      if (this.eventsMatrix[this.eventsMatrix.length - 1].length <= 0)
        return this.$message.error("请先点击录制按钮进行录制!");
      this.stopFn();

      this.showReplay = true;
      new rrwebPlayer({
        target: this.$refs.replayer, // 可以自定义 DOM 元素
        // 配置项
        props: {
          logConfig: true,
          events: this.eventsMatrix.reduce((acc, val) => acc.concat(val), []),
          plugins: [
            rrweb.getReplayConsolePlugin({
              level: ["info", "log", "warn", "error"],
            }),
          ],
        },
      });
    },
    reset() {
      this.showReplay = false;
      location.reload();
    },
    async getAXIOSdata() {
      const res = await axios({
        method: "post",
        url: "部署地址不便泄露",
        data: {
          key:  process.env.NODE_ENV == "development" ? "dpfpek533u" : this.requestKey,
        },
      });

      // 将二维数据转为一维
      let events = res.data.reduce((acc, val) => acc.concat(val), []);
      this.showReplay = true;
      new rrwebPlayer({
        target: this.$refs.replayer, // 可以自定义 DOM 元素
        // 配置项
        props: {
          logConfig: true,
          events: events,
          plugins: [
            rrweb.getReplayConsolePlugin({
              level: ["info", "log", "warn", "error"],
            }),
          ],
        },
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.main {
  padding: 20px;

  .interaction {
    padding: 10px;
  }

  .form {
    width: 600px;
    margin: 20px auto;

    ::v-deep .el-form-item__content {
      text-align: left;
    }
  }
}
</style>

{
  "name": "rrweb-demo",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },
  "dependencies": {
    "axios": "^1.7.2",
    "core-js": "^3.6.5",
    "element-ui": "^2.15.6",
    "rrweb": "^2.0.0-alpha.4",
    "rrweb-player": "^1.0.0-alpha.4",
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "sass": "^1.23.7",
    "sass-loader": "^8.0.0",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值