8

image.png

编写"谷歌扩展"便捷"自测"埋点上报信息是否正确

推荐我之前写的谷歌扩展入门&svelte入门文章:

记一次前端"chrome扩展"简单易懂的实战分享会(上)
记一次前端"chrome插件"基础实战分享会(建议收藏)(下篇)
聊聊Svelte.js技术它做了什么以及如何实现的(上)
聊聊Svelte.js技术它做了什么以及如何实现的(下)

背景

     每次开发完项目的埋点功能后都需要进行埋点自测, 此时我只能通过在控制台的Network上进行筛选, 由于埋点的请求参数是个"对象类型", 需要将传参逐一点开并且找到params这个key, 但是params是一个被json序列化的字符串, 需要进行JSON.parse(xxxxx.params);, 然后再打开它里面的数组结构因为可能是多个埋点信息一起上报所以JSON.parse后是个数据, 最后还要将里面的十个固有参数过滤掉只看我们新增的埋点参数。

     上述过程很有规律所以可以抽象成一个工具查找的逻辑, 那我们可以把问题拆解成3步:

  1. (展示)有合适的界面方便显示你想要的请求数据。
  2. (获取)拦截到你想要的请求。
  3. (处理)将请求的参数按固定规则解析, 剔除不需要查看的参数与父级。
  4. (配置)可配置哪些请求需要被监测。

     当然不只是埋点这个功能, 只要你有分析api数据的需求都可以做类似的插件。

最终效果

image.png

一、模拟数据的定义

     埋点上报数据我这里展示一个大概的数据结构, 大家根据业务自行调节, 假设下面的数据结构就是埋点上报api的上报参数

[
  {
    events: [
      {
        eventName: "事件名: 点击事件",
        params:
          '{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn1", "new2":"nnnnnn2"}',
        xxxxxxxx: 1636179764881,
        xxxxxxx: 0,
      },
      {
        eventName: "事件名: 取消事件",
        params:
          '{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn2", "new3":"nnnnnn3"}',
        xxxxxxxx: 1636179764881,
        xxxxxxx: 0,
      },
    ],
    user: {
      userId: 123,
      userName: "lulu",
    },
    header: {
      app_id: 999,
      os_name: "mac",
      os_version: "10_15_7",
      device_model: "Macintosh",
      language: "zh-CN",
    },
  },
];
  1. 传参本身是个"对象类型"
  2. 第一层主要是"埋点事件", "用户信息", "设备信息&更多"
  3. "events"是事件数组
  4. "eventName"事件名称, "params"里面是被"JSON序列化"的事件参数
  5. "params"里有一些我们不用看的默认值, 因为每个埋点都会带一些像"路径信息"&"停留事件"&"用户操作路径"等等的默认信息, 这些信息不用我们手动加入所以也就不用每次都看他的正确性。
  6. "new1, new2, new3"是我们新加入的参数, 也就是我们这次重点验证的参数

二、模拟埋点数据的发出

     编写两个按钮负责上报埋点请求, 方便我们后续的验证, 新建一个html文件:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <button onclick="send(`https://zhangzhaosong1.com/`)">点击上报1</button>
    <button onclick="send(`https://zhangzhaosong2.com/`)">点击上报2</button>
    <script>
      function send(url) {
        fetch(url, {
          method: "post",
          headers: {
            "Content-Type": "application/json;charset=utf-8;",
          },
          body: JSON.stringify([
            {
              events: [
                {
                  eventName: "事件名: 点击事件",
                  params:
                    '{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn1", "new2":"nnnnnn2"}',
                  xxxxxxxx: 1636179764881,
                  xxxxxxx: 0,
                },
                {
                  eventName: "事件名: 取消事件",
                  params:
                    '{"default1":"xxxxx","default2":"xxxxx","default3":"xxxxx","default4":"xxxxx","default5":"xxxxx","default6":"xxxxx","startTime":"xxxxx","endTime":"xxxxxxx", "new1":"nnnnnn2", "new3":"nnnnnn3"}',
                  xxxxxxxx: 1636179764881,
                  xxxxxxx: 0,
                },
              ],
              user: {
                userId: 123,
                userName: "lulu",
              },
              header: {
                app_id: 999,
                os_name: "mac",
                os_version: "10_15_7",
                device_model: "Macintosh",
                language: "zh-CN",
              },
            },
          ]),
        }).then(() => {});
      }
    </script>
  </body>
</html>

     上面html里面主要做的工作是添加两个"按钮", 每次点击发出埋点的模拟上报的post请求, 每个按钮上报的不一样所以有两个按钮。

image.png

三、创建一个谷歌插件项目 & 定义图标

     没看过基础课的同学一定要先看看基础文章, 否则没法转换思维。

主文件

manifest.json

{
  "manifest_version": 2,
  "name": "埋:更方便的观察埋点数据",
  "description": "更方便的观察埋点数据",
  "version": "0.1",
  "browser_action": {
    "default_icon": "images/logo.png"
  }
}

上述的图标大家自由发挥哈

引入插件

image.png

image.png

image.png

image.png

四、定义控制台tab

     在manifest.json文件里面增加

{
  "manifest_version": 2,
  "name": "埋:更方便的观察埋点数据",
  "description": "更方便的观察埋点数据",
  "version": "0.1",
  "browser_action": {
    "default_icon": "images/logo.png"
  },
  "devtools_page": "devtools/index.html" // 这句是新增的
}

devtools/index.html主要功能只是引入js文件

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script src="./index.js"></script>
</body>
</html>

devtools/index.js主要功能是创建tab

chrome.devtools.panels.create(
  "埋点验证tab, 还原埋点上报信息",
  null,
  "../panel/index.html",
  function () {
    console.log("自定义面板创建成功!");
  }
);

panel/index.html里面才是tab的内容区样式:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>创建成功</div>
</body>
</html>

image.png

五、初始化svelte项目

     tab里面的内容我们懒得原生操作dom, 使用"react"框架有点重, 所以这次我选择了"svelte框架", 具体它的好处可以看看我之前写的文章(文章放在文章头部了)。

初始项目
npx degit sveltejs/template devpanel

cd ./devpanel

yarn

我们编辑一下 rollup.config.js文件, 使得打包后的文件直接放入panel文件夹里。

  output: {
    sourcemap: false,
    format: "iife",
    name: "app",
    file: production ? "../panel/index.js" : "public/build/bundle.js",
  },

并且将panel里面的index.html文件的文件引用指向我们的index.js文件, 以及引入样式 <link rel="stylesheet" href="./bundle.css">

在这里运行命令yarn build后的效果如下, 请点击刷新插件并且"F12"重新打开控制台:

image.png

image.png

开发时候在localhost:5000/, 开发完打包就可以和插件一起上线了。

六、拦截请求

     核心功能就是拦截请求, 让我们使用onRequestFinishedapi

chrome.devtools.network.onRequestFinished.addListener((res) => {})

     上面的返回值res的数据格式如下图:
image.png

     关键信息都有, 请求方法与url可以用来比对是否为需要拦截的请求, 而我们剩下的工作就是对"text"的解析了。

     要注意的是, svelte里面无法读取到chrome的值, 所以需要用if (chrome) {}包裹起来, 所以在svelte里面开发时我使用的是mock数据调试。

七、处理数据

<script>
  const path = "https://zhangzhaosong.com/";
  import getNowTime from "./getNowTime";

  if (chrome) {
    let checkEvent = "";
    let dataList = [];
    const baseKeyArr = [
      "default1",
      "default2",
      "default3",
      "default4",
      "default5",
      "default6",
      "startTime",
      "endTime",
    ];
    chrome.devtools.network.onRequestFinished.addListener((res) => {
      const req = res.request;
      if (req.method === "POST" && req.url === path) {
        const eventsArr = JSON.parse(req.postData.text);
        eventsArr.forEach((item) => {
          if (item.events) {
            item.events.forEach((event) => {
              const params = JSON.parse(event.params);
              const res = {};
              for (let i in params) {
                if (!baseKeyArr.includes(i)) {
                  res[i] = params[i];
                }
              }
              dataList.unshift({
                res,
                time: getNowTime(),
                eventName: event.eventName,
              });
              dataList = dataList;
            });
          }
        });
        console.log("---", dataList);
      }
    });
  }
</script>

<main>
  <div>list</div>
</main>

<style>
</style>

image.png

  1. baseKeyArr这个数组里面是一些埋点默认的key, 用来过滤掉我们不关心的值
  2. path是要拦截的请求地址, 当然也可以换成正则之类的
  3. getNowTime获取当前的时间

八、简单设计个样式

     将上面得到的数组循环显示出来, 形成一个被拦截的埋点事件的参数list:

<main>
  {#each Object.keys(dataList) as item}
    <div>
      <header>
        <span>{dataList[item].time} : </span>
        <span class="title">{dataList[item].eventName}</span>
      </header>
      <ul>
        {#each Object.keys(dataList[item].res) as key}
          <li>
            <span>{key} :</span>
            <span>{dataList[item].res[key]}</span>
          </li>
        {/each}
      </ul>
    </div>
  {/each}
</main>

<style>
  .title {
    background-color: bisque;
  }
</style>

image.png

九、过滤功能与清除功能

     为了好用我们可以添加一个过滤功能与清除功能, 这样查看起来更舒服:

image.png

end

     网站快卡炸了, 这次就是这样, 希望与你一起进步。


lulu_up
5.7k 声望6.9k 粉丝

自信自律, 终身学习, 创业者